summaryrefslogtreecommitdiffstats
path: root/libblkid
diff options
context:
space:
mode:
Diffstat (limited to 'libblkid')
-rw-r--r--libblkid/Android.mk198
-rw-r--r--libblkid/COPYING8
-rw-r--r--libblkid/Makemodule.am16
-rw-r--r--libblkid/blkid.pc.in11
-rw-r--r--libblkid/docs/.gitignore18
-rw-r--r--libblkid/docs/Makefile.am95
-rw-r--r--libblkid/docs/libblkid-config.xml57
-rw-r--r--libblkid/docs/libblkid-docs.xml70
-rw-r--r--libblkid/docs/libblkid-sections.txt201
-rw-r--r--libblkid/docs/version.xml.in1
-rw-r--r--libblkid/include/Makemodule.am57
-rw-r--r--libblkid/include/all-io.h84
-rw-r--r--libblkid/include/at.h32
-rw-r--r--libblkid/include/bitops.h124
-rw-r--r--libblkid/include/blkdev.h146
-rw-r--r--libblkid/include/blkid.h414
-rw-r--r--libblkid/include/canonicalize.h21
-rw-r--r--libblkid/include/carefulputc.h67
-rw-r--r--libblkid/include/closestream.h71
-rw-r--r--libblkid/include/colors.h94
-rw-r--r--libblkid/include/config.h687
-rw-r--r--libblkid/include/cpuset.h99
-rw-r--r--libblkid/include/crc32.h10
-rw-r--r--libblkid/include/crc64.h9
-rw-r--r--libblkid/include/debug.h179
-rw-r--r--libblkid/include/env.h16
-rw-r--r--libblkid/include/exec_shell.h1
-rw-r--r--libblkid/include/exitcodes.h35
-rw-r--r--libblkid/include/fileutils.h33
-rw-r--r--libblkid/include/ismounted.h14
-rw-r--r--libblkid/include/linux_reboot.h72
-rw-r--r--libblkid/include/linux_version.h14
-rw-r--r--libblkid/include/list.h346
-rw-r--r--libblkid/include/loopdev.h193
-rw-r--r--libblkid/include/mangle.h26
-rw-r--r--libblkid/include/match.h12
-rw-r--r--libblkid/include/mbsalign.h56
-rw-r--r--libblkid/include/md5.h29
-rw-r--r--libblkid/include/minix.h85
-rw-r--r--libblkid/include/monotonic.h11
-rw-r--r--libblkid/include/namespace.h44
-rw-r--r--libblkid/include/nls.h115
-rw-r--r--libblkid/include/optutils.h103
-rw-r--r--libblkid/include/pager.h6
-rw-r--r--libblkid/include/pamfail.h26
-rw-r--r--libblkid/include/path.h33
-rw-r--r--libblkid/include/pathnames.h196
-rw-r--r--libblkid/include/procutils.h32
-rw-r--r--libblkid/include/pt-bsd.h156
-rw-r--r--libblkid/include/pt-mbr-partnames.h105
-rw-r--r--libblkid/include/pt-mbr.h178
-rw-r--r--libblkid/include/pt-sgi.h110
-rw-r--r--libblkid/include/pt-sun.h90
-rw-r--r--libblkid/include/randutils.h13
-rw-r--r--libblkid/include/readutmp.h28
-rw-r--r--libblkid/include/rpmatch.h9
-rw-r--r--libblkid/include/setproctitle.h7
-rw-r--r--libblkid/include/statfs_magic.h99
-rw-r--r--libblkid/include/strutils.h204
-rw-r--r--libblkid/include/swapheader.h23
-rw-r--r--libblkid/include/swapprober.h9
-rw-r--r--libblkid/include/sysfs.h94
-rw-r--r--libblkid/include/timer.h31
-rw-r--r--libblkid/include/timeutils.h56
-rw-r--r--libblkid/include/ttyutils.h168
-rw-r--r--libblkid/include/widechar.h38
-rw-r--r--libblkid/include/xalloc.h118
-rw-r--r--libblkid/lib/Makemodule.am115
-rw-r--r--libblkid/lib/at.c143
-rw-r--r--libblkid/lib/blkdev.c378
-rw-r--r--libblkid/lib/canonicalize.c145
-rw-r--r--libblkid/lib/colors.c877
-rw-r--r--libblkid/lib/cpuset.c403
-rw-r--r--libblkid/lib/crc32.c118
-rw-r--r--libblkid/lib/crc64.c109
-rw-r--r--libblkid/lib/env.c110
-rw-r--r--libblkid/lib/exec_shell.c47
-rw-r--r--libblkid/lib/fileutils.c144
-rw-r--r--libblkid/lib/ismounted.c388
-rw-r--r--libblkid/lib/langinfo.c121
-rw-r--r--libblkid/lib/linux_version.c25
-rw-r--r--libblkid/lib/loopdev.c1572
-rw-r--r--libblkid/lib/mangle.c166
-rw-r--r--libblkid/lib/match.c53
-rw-r--r--libblkid/lib/mbsalign.c466
-rw-r--r--libblkid/lib/md5.c257
-rw-r--r--libblkid/lib/monotonic.c68
-rw-r--r--libblkid/lib/pager.c210
-rw-r--r--libblkid/lib/path.c258
-rw-r--r--libblkid/lib/procutils.c264
-rw-r--r--libblkid/lib/randutils.c147
-rw-r--r--libblkid/lib/readutmp.c78
-rw-r--r--libblkid/lib/setproctitle.c74
-rw-r--r--libblkid/lib/strutils.c763
-rw-r--r--libblkid/lib/swapprober.c49
-rw-r--r--libblkid/lib/sysfs.c1075
-rw-r--r--libblkid/lib/terminal-colors.d.5190
-rw-r--r--libblkid/lib/timeutils.c342
-rw-r--r--libblkid/lib/ttyutils.c95
-rw-r--r--libblkid/libblkid.379
-rw-r--r--libblkid/libfdisk/COPYING8
-rw-r--r--libblkid/libfdisk/Makemodule.am14
-rw-r--r--libblkid/libfdisk/docs/.gitignore18
-rw-r--r--libblkid/libfdisk/docs/Makefile.am93
-rw-r--r--libblkid/libfdisk/docs/libfdisk-docs.xml62
-rw-r--r--libblkid/libfdisk/docs/libfdisk-sections.txt303
-rw-r--r--libblkid/libfdisk/docs/version.xml.in1
-rw-r--r--libblkid/libfdisk/fdisk.pc.in11
-rw-r--r--libblkid/libfdisk/src/.gitignore0
-rw-r--r--libblkid/libfdisk/src/Makemodule.am112
-rw-r--r--libblkid/libfdisk/src/alignment.c654
-rw-r--r--libblkid/libfdisk/src/ask.c1044
-rw-r--r--libblkid/libfdisk/src/bsd.c992
-rw-r--r--libblkid/libfdisk/src/context.c1017
-rw-r--r--libblkid/libfdisk/src/dos.c2331
-rw-r--r--libblkid/libfdisk/src/fdiskP.h438
-rw-r--r--libblkid/libfdisk/src/gpt.c2565
-rw-r--r--libblkid/libfdisk/src/init.c55
-rw-r--r--libblkid/libfdisk/src/iter.c82
-rw-r--r--libblkid/libfdisk/src/label.c569
-rw-r--r--libblkid/libfdisk/src/libfdisk.h579
-rw-r--r--libblkid/libfdisk/src/libfdisk.h.in579
-rw-r--r--libblkid/libfdisk/src/libfdisk.sym234
-rw-r--r--libblkid/libfdisk/src/partition.c963
-rw-r--r--libblkid/libfdisk/src/parttype.c401
-rw-r--r--libblkid/libfdisk/src/script.c1235
-rw-r--r--libblkid/libfdisk/src/sgi.c1185
-rw-r--r--libblkid/libfdisk/src/sun.c1130
-rw-r--r--libblkid/libfdisk/src/table.c664
-rw-r--r--libblkid/libfdisk/src/test.c59
-rw-r--r--libblkid/libfdisk/src/utils.c154
-rw-r--r--libblkid/libuuid/COPYING5
-rw-r--r--libblkid/libuuid/Makemodule.am10
-rw-r--r--libblkid/libuuid/man/.gitignore3
-rw-r--r--libblkid/libuuid/man/Makemodule.am14
-rw-r--r--libblkid/libuuid/man/uuid.365
-rw-r--r--libblkid/libuuid/man/uuid_clear.362
-rw-r--r--libblkid/libuuid/man/uuid_compare.368
-rw-r--r--libblkid/libuuid/man/uuid_copy.364
-rw-r--r--libblkid/libuuid/man/uuid_generate.3126
-rw-r--r--libblkid/libuuid/man/uuid_is_null.364
-rw-r--r--libblkid/libuuid/man/uuid_parse.373
-rw-r--r--libblkid/libuuid/man/uuid_time.378
-rw-r--r--libblkid/libuuid/man/uuid_unparse.381
-rw-r--r--libblkid/libuuid/src/Makemodule.am61
-rw-r--r--libblkid/libuuid/src/clear.c43
-rw-r--r--libblkid/libuuid/src/compare.c55
-rw-r--r--libblkid/libuuid/src/copy.c45
-rw-r--r--libblkid/libuuid/src/gen_uuid.c545
-rw-r--r--libblkid/libuuid/src/isnull.c48
-rw-r--r--libblkid/libuuid/src/libuuid.sym48
-rw-r--r--libblkid/libuuid/src/pack.c69
-rw-r--r--libblkid/libuuid/src/parse.c79
-rw-r--r--libblkid/libuuid/src/test_uuid.c180
-rw-r--r--libblkid/libuuid/src/unpack.c63
-rw-r--r--libblkid/libuuid/src/unparse.c76
-rw-r--r--libblkid/libuuid/src/uuid.h104
-rw-r--r--libblkid/libuuid/src/uuidP.h61
-rw-r--r--libblkid/libuuid/src/uuid_time.c171
-rw-r--r--libblkid/libuuid/src/uuidd.h54
-rw-r--r--libblkid/libuuid/uuid.pc.in11
-rw-r--r--libblkid/samples/.gitignore4
-rw-r--r--libblkid/samples/Makemodule.am22
-rw-r--r--libblkid/samples/mkfs.c78
-rw-r--r--libblkid/samples/partitions.c96
-rw-r--r--libblkid/samples/superblocks.c67
-rw-r--r--libblkid/samples/topology.c86
-rw-r--r--libblkid/src/.gitignore1
-rw-r--r--libblkid/src/bitops.h126
-rw-r--r--libblkid/src/blkdev.h146
-rw-r--r--libblkid/src/blkid.h.in414
-rw-r--r--libblkid/src/blkidP.h545
-rw-r--r--libblkid/src/c.h295
-rw-r--r--libblkid/src/cache.c226
-rw-r--r--libblkid/src/canonicalize.h21
-rw-r--r--libblkid/src/closestream.h71
-rw-r--r--libblkid/src/colors.h94
-rw-r--r--libblkid/src/config.c198
-rw-r--r--libblkid/src/crc32.h10
-rw-r--r--libblkid/src/debug.h179
-rw-r--r--libblkid/src/dev.c274
-rw-r--r--libblkid/src/devname.c676
-rw-r--r--libblkid/src/devno.c375
-rw-r--r--libblkid/src/encode.c340
-rw-r--r--libblkid/src/env.h16
-rw-r--r--libblkid/src/evaluate.c325
-rw-r--r--libblkid/src/exec_shell.h1
-rw-r--r--libblkid/src/fileutils.h33
-rw-r--r--libblkid/src/getsize.c34
-rw-r--r--libblkid/src/init.c66
-rw-r--r--libblkid/src/ismounted.h14
-rw-r--r--libblkid/src/libblkid.sym166
-rw-r--r--libblkid/src/linux_version.h14
-rw-r--r--libblkid/src/list.h340
-rw-r--r--libblkid/src/llseek.c142
-rw-r--r--libblkid/src/loopdev.h193
-rw-r--r--libblkid/src/mangle.h26
-rw-r--r--libblkid/src/match.h12
-rw-r--r--libblkid/src/mbsalign.h56
-rw-r--r--libblkid/src/md5.h29
-rw-r--r--libblkid/src/nls.h115
-rw-r--r--libblkid/src/partitions/aix.c57
-rw-r--r--libblkid/src/partitions/aix.h7
-rw-r--r--libblkid/src/partitions/bsd.c179
-rw-r--r--libblkid/src/partitions/dos.c307
-rw-r--r--libblkid/src/partitions/gpt.c470
-rw-r--r--libblkid/src/partitions/mac.c192
-rw-r--r--libblkid/src/partitions/minix.c102
-rw-r--r--libblkid/src/partitions/partitions.c1513
-rw-r--r--libblkid/src/partitions/partitions.h71
-rw-r--r--libblkid/src/partitions/sgi.c87
-rw-r--r--libblkid/src/partitions/solaris_x86.c154
-rw-r--r--libblkid/src/partitions/sun.c125
-rw-r--r--libblkid/src/partitions/ultrix.c99
-rw-r--r--libblkid/src/partitions/unixware.c197
-rw-r--r--libblkid/src/pathnames.h196
-rw-r--r--libblkid/src/probe.c1815
-rw-r--r--libblkid/src/pt-bsd.h156
-rw-r--r--libblkid/src/pt-mbr.h178
-rw-r--r--libblkid/src/read.c507
-rw-r--r--libblkid/src/resolve.c130
-rw-r--r--libblkid/src/save.c241
-rw-r--r--libblkid/src/strutils.h204
-rw-r--r--libblkid/src/superblocks/adaptec_raid.c116
-rw-r--r--libblkid/src/superblocks/bcache.c136
-rw-r--r--libblkid/src/superblocks/befs.c482
-rw-r--r--libblkid/src/superblocks/bfs.c23
-rw-r--r--libblkid/src/superblocks/btrfs.c93
-rw-r--r--libblkid/src/superblocks/cramfs.c62
-rw-r--r--libblkid/src/superblocks/ddf_raid.c141
-rw-r--r--libblkid/src/superblocks/drbd.c117
-rw-r--r--libblkid/src/superblocks/drbdproxy_datalog.c55
-rw-r--r--libblkid/src/superblocks/exfat.c148
-rw-r--r--libblkid/src/superblocks/ext.c365
-rw-r--r--libblkid/src/superblocks/f2fs.c99
-rw-r--r--libblkid/src/superblocks/gfs.c131
-rw-r--r--libblkid/src/superblocks/hfs.c321
-rw-r--r--libblkid/src/superblocks/highpoint_raid.c87
-rw-r--r--libblkid/src/superblocks/hpfs.c122
-rw-r--r--libblkid/src/superblocks/iso9660.c273
-rw-r--r--libblkid/src/superblocks/iso9660.h14
-rw-r--r--libblkid/src/superblocks/isw_raid.c66
-rw-r--r--libblkid/src/superblocks/jfs.c71
-rw-r--r--libblkid/src/superblocks/jmicron_raid.c65
-rw-r--r--libblkid/src/superblocks/linux_raid.c267
-rw-r--r--libblkid/src/superblocks/lsi_raid.c60
-rw-r--r--libblkid/src/superblocks/luks.c66
-rw-r--r--libblkid/src/superblocks/lvm.c226
-rw-r--r--libblkid/src/superblocks/minix.c161
-rw-r--r--libblkid/src/superblocks/netware.c97
-rw-r--r--libblkid/src/superblocks/nilfs.c138
-rw-r--r--libblkid/src/superblocks/ntfs.c224
-rw-r--r--libblkid/src/superblocks/nvidia_raid.c64
-rw-r--r--libblkid/src/superblocks/ocfs.c213
-rw-r--r--libblkid/src/superblocks/promise_raid.c71
-rw-r--r--libblkid/src/superblocks/refs.c26
-rw-r--r--libblkid/src/superblocks/reiserfs.c128
-rw-r--r--libblkid/src/superblocks/romfs.c51
-rw-r--r--libblkid/src/superblocks/silicon_raid.c130
-rw-r--r--libblkid/src/superblocks/squashfs.c101
-rw-r--r--libblkid/src/superblocks/superblocks.c813
-rw-r--r--libblkid/src/superblocks/superblocks.h99
-rw-r--r--libblkid/src/superblocks/swap.c178
-rw-r--r--libblkid/src/superblocks/sysv.c154
-rw-r--r--libblkid/src/superblocks/ubifs.c121
-rw-r--r--libblkid/src/superblocks/udf.c201
-rw-r--r--libblkid/src/superblocks/ufs.c258
-rw-r--r--libblkid/src/superblocks/vfat.c431
-rw-r--r--libblkid/src/superblocks/via_raid.c91
-rw-r--r--libblkid/src/superblocks/vmfs.c101
-rw-r--r--libblkid/src/superblocks/vxfs.c41
-rw-r--r--libblkid/src/superblocks/xfs.c277
-rw-r--r--libblkid/src/superblocks/zfs.c233
-rw-r--r--libblkid/src/sysfs.h94
-rw-r--r--libblkid/src/tag.c475
-rw-r--r--libblkid/src/topology/dm.c136
-rw-r--r--libblkid/src/topology/evms.c78
-rw-r--r--libblkid/src/topology/ioctl.c74
-rw-r--r--libblkid/src/topology/lvm.c148
-rw-r--r--libblkid/src/topology/md.c155
-rw-r--r--libblkid/src/topology/sysfs.c119
-rw-r--r--libblkid/src/topology/topology.c362
-rw-r--r--libblkid/src/topology/topology.h24
-rw-r--r--libblkid/src/verify.c224
-rw-r--r--libblkid/src/version.c62
-rw-r--r--libblkid/src/widechar.h38
-rw-r--r--libblkid/src/xalloc.h118
287 files changed, 58138 insertions, 0 deletions
diff --git a/libblkid/Android.mk b/libblkid/Android.mk
new file mode 100644
index 000000000..42762e4e4
--- /dev/null
+++ b/libblkid/Android.mk
@@ -0,0 +1,198 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libutil-linux
+LOCAL_MODULE_TAGS := optional
+#LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_CFLAGS := -D_FILE_OFFSET_BITS=64 -DHAVE_LOFF_T -DHAVE_ERR_H -DHAVE_MEMPCPY -DHAVE_FSYNC
+LOCAL_CFLAGS += -Wno-missing-field-initializers -Wno-sign-compare -Wno-unused-parameter -Wno-format -Wno-pointer-arith
+LOCAL_SRC_FILES = lib/at.c \
+ lib/blkdev.c \
+ lib/canonicalize.c \
+ lib/colors.c \
+ lib/crc32.c \
+ lib/crc64.c \
+ lib/env.c \
+ lib/exec_shell.c \
+ lib/fileutils.c \
+ lib/ismounted.c \
+ lib/langinfo.c \
+ lib/linux_version.c \
+ lib/loopdev.c \
+ lib/mangle.c \
+ lib/match.c \
+ lib/mbsalign.c \
+ lib/md5.c \
+ lib/pager.c \
+ lib/path.c \
+ lib/procutils.c \
+ lib/randutils.c \
+ lib/setproctitle.c \
+ lib/strutils.c \
+ lib/sysfs.c \
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/src
+
+LOCAL_SHARED_LIBRARIES += libc
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libuuid
+LOCAL_MODULE_TAGS := optional
+#LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_CFLAGS := -D_FILE_OFFSET_BITS=64 -DHAVE_LOFF_T -DHAVE_ERR_H -DHAVE_MEMPCPY -DHAVE_FSYNC
+LOCAL_CFLAGS += -Wno-missing-field-initializers -Wno-sign-compare -Wno-unused-parameter -Wno-format -Wno-pointer-arith
+LOCAL_SRC_FILES = libuuid/src/clear.c \
+ libuuid/src/copy.c \
+ libuuid/src/isnull.c \
+ libuuid/src/parse.c \
+ libuuid/src/unpack.c \
+ libuuid/src/uuid_time.c \
+ libuuid/src/compare.c \
+ libuuid/src/gen_uuid.c \
+ libuuid/src/pack.c \
+ libuuid/src/test_uuid.c \
+ libuuid/src/unparse.c
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/libuuid/src \
+ $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/src
+
+LOCAL_SHARED_LIBRARIES += libc libutil-linux
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libfdisk
+LOCAL_MODULE_TAGS := optional
+#LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_CFLAGS := -D_FILE_OFFSET_BITS=64 -DHAVE_LOFF_T -DHAVE_ERR_H -DHAVE_MEMPCPY -DHAVE_FSYNC
+LOCAL_CFLAGS += -Wno-missing-field-initializers -Wno-sign-compare -Wno-unused-parameter -Wno-format -Wno-pointer-arith
+LOCAL_SRC_FILES = libfdisk/src/alignment.c \
+ libfdisk/src/context.c \
+ libfdisk/src/init.c \
+ libfdisk/src/partition.c \
+ libfdisk/src/sgi.c \
+ libfdisk/src/test.c \
+ libfdisk/src/ask.c \
+ libfdisk/src/dos.c \
+ libfdisk/src/iter.c \
+ libfdisk/src/parttype.c \
+ libfdisk/src/sun.c \
+ libfdisk/src/utils.c \
+ libfdisk/src/bsd.c \
+ libfdisk/src/gpt.c \
+ libfdisk/src/label.c \
+ libfdisk/src/script.c \
+ libfdisk/src/table.c
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/libfdisk/src \
+ $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/libuuid/src \
+ $(LOCAL_PATH)/src
+
+LOCAL_SHARED_LIBRARIES += libc libutil-linux libuuid
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libblkid
+LOCAL_MODULE_TAGS := optional
+#LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_CFLAGS := -D_FILE_OFFSET_BITS=64 -DHAVE_LOFF_T -DHAVE_ERR_H -DHAVE_MEMPCPY -DHAVE_FSYNC
+LOCAL_CFLAGS += -Wno-missing-field-initializers -Wno-sign-compare -Wno-unused-parameter -Wno-format -Wno-pointer-arith
+LOCAL_SRC_FILES = src/cache.c \
+ src/config.c \
+ src/dev.c \
+ src/devname.c \
+ src/devno.c \
+ src/encode.c \
+ src/evaluate.c \
+ src/getsize.c \
+ src/init.c \
+ src/llseek.c \
+ src/probe.c \
+ src/read.c \
+ src/resolve.c \
+ src/save.c \
+ src/tag.c \
+ src/verify.c \
+ src/version.c \
+ src/partitions/aix.c \
+ src/partitions/bsd.c \
+ src/partitions/dos.c \
+ src/partitions/gpt.c \
+ src/partitions/mac.c \
+ src/partitions/minix.c \
+ src/partitions/partitions.c \
+ src/partitions/sgi.c \
+ src/partitions/solaris_x86.c \
+ src/partitions/sun.c \
+ src/partitions/ultrix.c \
+ src/partitions/unixware.c \
+ src/superblocks/adaptec_raid.c \
+ src/superblocks/bcache.c \
+ src/superblocks/befs.c \
+ src/superblocks/bfs.c \
+ src/superblocks/btrfs.c \
+ src/superblocks/cramfs.c \
+ src/superblocks/ddf_raid.c \
+ src/superblocks/drbd.c \
+ src/superblocks/drbdproxy_datalog.c \
+ src/superblocks/exfat.c \
+ src/superblocks/ext.c \
+ src/superblocks/f2fs.c \
+ src/superblocks/gfs.c \
+ src/superblocks/hfs.c \
+ src/superblocks/highpoint_raid.c \
+ src/superblocks/hpfs.c \
+ src/superblocks/iso9660.c \
+ src/superblocks/isw_raid.c \
+ src/superblocks/jfs.c \
+ src/superblocks/jmicron_raid.c \
+ src/superblocks/linux_raid.c \
+ src/superblocks/lsi_raid.c \
+ src/superblocks/luks.c \
+ src/superblocks/lvm.c \
+ src/superblocks/minix.c \
+ src/superblocks/netware.c \
+ src/superblocks/nilfs.c \
+ src/superblocks/ntfs.c \
+ src/superblocks/nvidia_raid.c \
+ src/superblocks/ocfs.c \
+ src/superblocks/promise_raid.c \
+ src/superblocks/refs.c \
+ src/superblocks/reiserfs.c \
+ src/superblocks/romfs.c \
+ src/superblocks/silicon_raid.c \
+ src/superblocks/squashfs.c \
+ src/superblocks/superblocks.c \
+ src/superblocks/swap.c \
+ src/superblocks/sysv.c \
+ src/superblocks/ubifs.c \
+ src/superblocks/udf.c \
+ src/superblocks/ufs.c \
+ src/superblocks/vfat.c \
+ src/superblocks/via_raid.c \
+ src/superblocks/vmfs.c \
+ src/superblocks/vxfs.c \
+ src/superblocks/xfs.c \
+ src/superblocks/zfs.c \
+ src/topology/dm.c \
+ src/topology/evms.c \
+ src/topology/ioctl.c \
+ src/topology/lvm.c \
+ src/topology/md.c \
+ src/topology/sysfs.c \
+ src/topology/topology.c \
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include \
+ $(LOCAL_PATH)/src
+
+LOCAL_SHARED_LIBRARIES += libc libutil-linux
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libblkid/COPYING b/libblkid/COPYING
new file mode 100644
index 000000000..be1a5b3a1
--- /dev/null
+++ b/libblkid/COPYING
@@ -0,0 +1,8 @@
+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.
+
+The complete text of the license is available in the
+../Documentation/licenses/COPYING.LGPLv2.1 file.
diff --git a/libblkid/Makemodule.am b/libblkid/Makemodule.am
new file mode 100644
index 000000000..b4f6f9c4e
--- /dev/null
+++ b/libblkid/Makemodule.am
@@ -0,0 +1,16 @@
+if BUILD_LIBBLKID
+
+include libblkid/src/Makemodule.am
+include libblkid/samples/Makemodule.am
+
+if ENABLE_GTK_DOC
+# Docs uses separate Makefiles
+SUBDIRS += libblkid/docs
+endif
+
+pkgconfig_DATA += libblkid/blkid.pc
+PATHFILES += libblkid/blkid.pc
+dist_man_MANS += libblkid/libblkid.3
+EXTRA_DIST += libblkid/libblkid.3 libblkid/COPYING
+
+endif # BUILD_LIBBLKID
diff --git a/libblkid/blkid.pc.in b/libblkid/blkid.pc.in
new file mode 100644
index 000000000..40ec8a9d7
--- /dev/null
+++ b/libblkid/blkid.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@usrlib_execdir@
+includedir=@includedir@
+
+Name: blkid
+Description: Block device id library
+Version: @LIBBLKID_VERSION@
+Requires.private: uuid
+Cflags: -I${includedir}/blkid
+Libs: -L${libdir} -lblkid
diff --git a/libblkid/docs/.gitignore b/libblkid/docs/.gitignore
new file mode 100644
index 000000000..f91f93db7
--- /dev/null
+++ b/libblkid/docs/.gitignore
@@ -0,0 +1,18 @@
+*-decl-list.txt
+*-decl.txt
+*-overrides.txt
+*-undeclared.txt
+*-undocumented.txt
+*-unused.txt
+*.args
+*.bak
+*.hierarchy
+*.interfaces
+*.prerequisites
+*.signals
+*.stamp
+*.types
+html/*
+tmpl/*
+version.xml
+xml/*
diff --git a/libblkid/docs/Makefile.am b/libblkid/docs/Makefile.am
new file mode 100644
index 000000000..dc6dd3e5e
--- /dev/null
+++ b/libblkid/docs/Makefile.am
@@ -0,0 +1,95 @@
+## Process this file with automake to produce Makefile.in
+
+# We require automake 1.10 at least.
+AUTOMAKE_OPTIONS = 1.10
+
+# This is a blank Makefile.am for using gtk-doc.
+# Copy this to your project's API docs directory and modify the variables to
+# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
+# of using the various options.
+
+# The name of the module, e.g. 'glib'.
+DOC_MODULE=libblkid
+
+# Uncomment for versioned docs and specify the version of the module, e.g. '2'.
+#DOC_MODULE_VERSION=2
+
+# The top-level SGML file. You can change this if you want to.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting the functions and macros.
+# e.g. DOC_SOURCE_DIR=../../../gtk
+DOC_SOURCE_DIR=../src
+
+# Extra options to pass to gtkdoc-scangobj. Not normally needed.
+SCANGOBJ_OPTIONS=
+
+# Extra options to supply to gtkdoc-scan.
+# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
+SCAN_OPTIONS=--deprecated-guards="BLKID_DISABLE_DEPRECATED"
+
+# Extra options to supply to gtkdoc-mkdb.
+# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml
+MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space blkid
+
+# Extra options to supply to gtkdoc-mktmpl
+# e.g. MKTMPL_OPTIONS=--only-section-tmpl
+MKTMPL_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkhtml
+MKHTML_OPTIONS=
+
+# Extra options to supply to gtkdoc-fixref. Not normally needed.
+# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
+FIXXREF_OPTIONS=
+
+# Used for dependencies. The docs will be rebuilt if any of these change.
+# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
+# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
+HFILE_GLOB=$(top_builddir)/libblkid/src/blkid.h
+CFILE_GLOB=$(top_srcdir)/libblkid/src/*.c
+
+# Extra header to include when scanning, which are not under DOC_SOURCE_DIR
+# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
+EXTRA_HFILES=
+
+# Header files to ignore when scanning. Use base file name, no paths
+# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
+IGNORE_HFILES=blkidP.h partitions.h superblocks.h \
+ topology.h aix.h dos.h iso9660.h
+
+# Images to copy into HTML directory.
+# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
+HTML_IMAGES=
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
+content_files = $(builddir)/version.xml $(srcdir)/libblkid-config.xml
+
+# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
+# These files must be listed here *and* in content_files
+# e.g. expand_content_files=running.sgml
+expand_content_files=
+
+# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
+# Only needed if you are using gtkdoc-scangobj to dynamically query widget
+# signals and properties.
+# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
+# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
+GTKDOC_CFLAGS=
+GTKDOC_LIBS=
+
+# This includes the standard gtk-doc make rules, copied by gtkdocize.
+include $(top_srcdir)/config/gtk-doc.make
+
+# Other files to distribute
+# e.g. EXTRA_DIST += version.xml.in
+EXTRA_DIST += version.xml.in $(srcdir)/libblkid-config.xml
+
+# Files not to distribute
+# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
+# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
+DISTCLEANFILES += version.xml
+
diff --git a/libblkid/docs/libblkid-config.xml b/libblkid/docs/libblkid-config.xml
new file mode 100644
index 000000000..89fbb7f17
--- /dev/null
+++ b/libblkid/docs/libblkid-config.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+ <!ENTITY version SYSTEM "version.xml">
+]>
+<refentry id="libblkid-config">
+<refmeta>
+<refentrytitle role="top_of_page" id="libblkid-config.top_of_page">Config file</refentrytitle>
+<manvolnum>3</manvolnum>
+<refmiscinfo>LIBBLKID Library</refmiscinfo>
+</refmeta>
+
+<refnamediv>
+<refname>Config file</refname>
+<refpurpose>config file to control paths and basic library behavior</refpurpose>
+</refnamediv>
+
+<refsect1 id="libblkid-config.description" role="desc">
+<title role="desc.title">Description</title>
+<para>
+The standard location of the
+/etc/blkid.conf config file can be overridden by the environment variable
+BLKID_CONF. The following options control the libblkid library:
+</para>
+
+<variablelist role="params">
+ <varlistentry>
+ <term>SEND_UEVENT=<parameter>yes|not</parameter></term>
+ <listitem><simpara>
+ Sends uevent when /dev/disk/by-{label,uuid}/
+ symlink does not match with LABEL or UUID on the device. Default is "yes".
+ </simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>CACHE_FILE=<parameter>path</parameter></term>
+ <listitem><simpara>
+ Overrides the standard location of the cache file. This
+ setting can be overridden by the environment variable BLKID_FILE. Default is
+ /etc/blkid.tab.
+ </simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>EVALUATE=<parameter>method</parameter></term>
+ <listitem><simpara>
+ Defines LABEL and UUID evaluation method(s). Currently,
+ the libblkid library supports "udev" and "scan" methods. More than one methods
+ may be specified in a comma separated list. Default is "udev,scan". The "udev"
+ method uses udev /dev/disk/by-* symlinks and the "scan" method scans all
+ block devices from the /proc/partitions file.
+ </simpara></listitem>
+ </varlistentry>
+</variablelist>
+
+</refsect1>
+
+</refentry>
diff --git a/libblkid/docs/libblkid-docs.xml b/libblkid/docs/libblkid-docs.xml
new file mode 100644
index 000000000..1f412c105
--- /dev/null
+++ b/libblkid/docs/libblkid-docs.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+ <!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+ <bookinfo>
+ <title>libblkid Reference Manual</title>
+ <releaseinfo>for libblkid version &version;</releaseinfo>
+ <copyright>
+ <year>2009-2013</year>
+ <holder>Karel Zak &lt;kzak@redhat.com&gt;</holder>
+ </copyright>
+ </bookinfo>
+
+ <part id="gtk">
+ <title>libblkid Overview</title>
+ <partintro>
+ <para>
+The libblkid library is used to identify block devices (disks) as to their
+content (e.g. filesystem type, partitions) as well as extracting additional
+information such as filesystem labels/volume names, partitions, unique
+identifiers/serial numbers, etc. A common use is to allow use of LABEL= and
+UUID= tags instead of hard-coding specific block device names into
+configuration files.
+ </para>
+ <para>
+The libblkid librray
+was written by Andreas Dilger for the ext2 filesystem utilties, with input
+from Ted Ts'o. The library was subsequently heavily modified by Ted Ts'o.
+ </para>
+ <para>
+The low-level probing code, topology and partitions support was written
+by Karel Zak. Currently, the library is mainatned by Karel Zak.
+ </para>
+ <para>
+The library is part of the util-linux package since version 2.15 and is
+available from ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+ </para>
+ </partintro>
+ <xi:include href="xml/libblkid-config.xml"/>
+ </part>
+
+ <part>
+ <title>High-level</title>
+ <xi:include href="xml/evaluate.xml"/>
+ <xi:include href="xml/cache.xml"/>
+ <xi:include href="xml/search.xml"/>
+ </part>
+ <part>
+ <title>Low-level</title>
+ <xi:include href="xml/init.xml"/>
+ <xi:include href="xml/lowprobe.xml"/>
+ <xi:include href="xml/lowprobe-tags.xml"/>
+ <xi:include href="xml/superblocks.xml"/>
+ <xi:include href="xml/partitions.xml"/>
+ <xi:include href="xml/topology.xml"/>
+ </part>
+ <part>
+ <title>Common utils</title>
+ <xi:include href="xml/encode.xml"/>
+ <xi:include href="xml/misc.xml"/>
+ </part>
+
+ <index id="api-index-full">
+ <title>API Index</title>
+ <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+ </index>
+</book>
diff --git a/libblkid/docs/libblkid-sections.txt b/libblkid/docs/libblkid-sections.txt
new file mode 100644
index 000000000..53cf84b78
--- /dev/null
+++ b/libblkid/docs/libblkid-sections.txt
@@ -0,0 +1,201 @@
+<SECTION>
+<FILE>evaluate</FILE>
+blkid_evaluate_tag
+blkid_evaluate_spec
+</SECTION>
+
+<SECTION>
+<FILE>init</FILE>
+blkid_init_debug
+</SECTION>
+
+<SECTION>
+<FILE>cache</FILE>
+blkid_cache
+blkid_gc_cache
+blkid_get_cache
+blkid_put_cache
+blkid_probe_all
+blkid_probe_all_removable
+blkid_probe_all_new
+blkid_verify
+</SECTION>
+
+<SECTION>
+<FILE>search</FILE>
+blkid_dev
+blkid_dev_devname
+blkid_dev_has_tag
+blkid_dev_iterate
+blkid_dev_iterate_begin
+blkid_dev_iterate_end
+blkid_dev_next
+blkid_dev_set_search
+blkid_find_dev_with_tag
+blkid_get_dev
+blkid_get_devname
+blkid_get_tag_value
+blkid_tag_iterate
+blkid_tag_iterate_begin
+blkid_tag_iterate_end
+blkid_tag_next
+</SECTION>
+
+<SECTION>
+<FILE>lowprobe</FILE>
+blkid_probe
+blkid_free_probe
+blkid_new_probe
+blkid_new_probe_from_filename
+blkid_probe_get_devno
+blkid_probe_get_fd
+blkid_probe_get_offset
+blkid_probe_get_sectors
+blkid_probe_get_sectorsize
+blkid_probe_get_size
+blkid_probe_get_wholedisk_devno
+blkid_probe_is_wholedisk
+blkid_probe_set_device
+blkid_probe_step_back
+blkid_reset_probe
+</SECTION>
+
+<SECTION>
+<FILE>lowprobe-tags</FILE>
+blkid_do_fullprobe
+blkid_do_wipe
+blkid_do_probe
+blkid_do_safeprobe
+<SUBSECTION>
+blkid_probe_get_value
+blkid_probe_has_value
+blkid_probe_lookup_value
+blkid_probe_numof_values
+</SECTION>
+
+<SECTION>
+<FILE>partitions</FILE>
+blkid_partlist
+blkid_partition
+blkid_parttable
+blkid_probe_enable_partitions
+blkid_probe_set_partitions_flags
+blkid_probe_filter_partitions_type
+blkid_probe_invert_partitions_filter
+blkid_probe_reset_partitions_filter
+<SUBSECTION>
+blkid_known_pttype
+<SUBSECTION>
+blkid_partition_get_name
+blkid_partition_get_flags
+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
+<SUBSECTION>
+blkid_partlist_get_partition
+blkid_partlist_get_partition_by_partno
+blkid_partlist_numof_partitions
+blkid_partlist_devno_to_partition
+blkid_partlist_get_table
+<SUBSECTION>
+blkid_parttable_get_id
+blkid_parttable_get_offset
+blkid_parttable_get_parent
+blkid_parttable_get_type
+<SUBSECTION>
+blkid_probe_get_partitions
+</SECTION>
+
+<SECTION>
+<FILE>superblocks</FILE>
+blkid_probe_enable_superblocks
+<SUBSECTION>
+blkid_known_fstype
+blkid_superblocks_get_name
+<SUBSECTION>
+blkid_probe_filter_superblocks_type
+blkid_probe_filter_superblocks_usage
+blkid_probe_invert_superblocks_filter
+blkid_probe_reset_superblocks_filter
+blkid_probe_set_superblocks_flags
+<SUBSECTION>
+blkid_probe_reset_filter
+blkid_probe_filter_types
+blkid_probe_filter_usage
+blkid_probe_invert_filter
+blkid_probe_set_request
+</SECTION>
+
+<SECTION>
+<FILE>topology</FILE>
+blkid_topology
+blkid_probe_enable_topology
+<SUBSECTION>
+blkid_probe_get_topology
+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
+</SECTION>
+
+<SECTION>
+<FILE>encode</FILE>
+blkid_encode_string
+blkid_safe_string
+</SECTION>
+
+<SECTION>
+<FILE>misc</FILE>
+blkid_loff_t
+blkid_devno_to_devname
+blkid_devno_to_wholedisk
+blkid_get_dev_size
+blkid_get_library_version
+blkid_parse_tag_string
+blkid_parse_version_string
+blkid_send_uevent
+BLKID_VERSION
+BLKID_DATE
+BLKID_FLTR_NOTIN
+BLKID_FLTR_ONLYIN
+BLKID_DEV_CREATE
+BLKID_DEV_FIND
+BLKID_DEV_NORMAL
+BLKID_DEV_VERIFY
+BLKID_PARTS_ENTRY_DETAILS
+BLKID_PARTS_FORCE_GPT
+BLKID_PARTS_MAGIC
+BLKID_PROBREQ_LABEL
+BLKID_PROBREQ_LABELRAW
+BLKID_PROBREQ_SECTYPE
+BLKID_PROBREQ_TYPE
+BLKID_PROBREQ_USAGE
+BLKID_PROBREQ_UUID
+BLKID_PROBREQ_UUIDRAW
+BLKID_PROBREQ_VERSION
+BLKID_SUBLKS_BADCSUM
+BLKID_SUBLKS_DEFAULT
+BLKID_SUBLKS_LABEL
+BLKID_SUBLKS_LABELRAW
+BLKID_SUBLKS_MAGIC
+BLKID_SUBLKS_SECTYPE
+BLKID_SUBLKS_TYPE
+BLKID_SUBLKS_USAGE
+BLKID_SUBLKS_UUID
+BLKID_SUBLKS_UUIDRAW
+BLKID_SUBLKS_VERSION
+BLKID_USAGE_CRYPTO
+BLKID_USAGE_FILESYSTEM
+BLKID_USAGE_OTHER
+BLKID_USAGE_RAID
+</SECTION>
+
+
diff --git a/libblkid/docs/version.xml.in b/libblkid/docs/version.xml.in
new file mode 100644
index 000000000..d78bda934
--- /dev/null
+++ b/libblkid/docs/version.xml.in
@@ -0,0 +1 @@
+@VERSION@
diff --git a/libblkid/include/Makemodule.am b/libblkid/include/Makemodule.am
new file mode 100644
index 000000000..c4a52e4cf
--- /dev/null
+++ b/libblkid/include/Makemodule.am
@@ -0,0 +1,57 @@
+
+dist_noinst_HEADERS += \
+ include/all-io.h \
+ include/at.h \
+ include/bitops.h \
+ include/blkdev.h \
+ include/monotonic.h \
+ include/c.h \
+ include/canonicalize.h \
+ include/carefulputc.h \
+ include/closestream.h \
+ include/colors.h \
+ include/cpuset.h \
+ include/crc32.h \
+ include/crc64.h \
+ include/debug.h \
+ include/env.h \
+ include/exec_shell.h \
+ include/exitcodes.h \
+ include/fileutils.h \
+ include/ismounted.h \
+ include/linux_reboot.h \
+ include/linux_version.h \
+ include/list.h \
+ include/loopdev.h \
+ include/mangle.h \
+ include/match.h \
+ include/mbsalign.h \
+ include/md5.h \
+ include/minix.h \
+ include/namespace.h \
+ include/nls.h \
+ include/optutils.h \
+ include/pager.h \
+ include/pamfail.h \
+ include/path.h \
+ include/pathnames.h \
+ include/procutils.h \
+ include/randutils.h \
+ include/readutmp.h \
+ include/rpmatch.h \
+ include/setproctitle.h \
+ include/strutils.h \
+ include/swapprober.h \
+ include/swapheader.h \
+ include/sysfs.h \
+ include/timer.h \
+ include/timeutils.h \
+ include/ttyutils.h \
+ include/widechar.h \
+ include/xalloc.h \
+ include/pt-sgi.h \
+ include/pt-bsd.h \
+ include/pt-mbr.h \
+ include/pt-mbr-partnames.h \
+ include/pt-sun.h \
+ include/statfs_magic.h
diff --git a/libblkid/include/all-io.h b/libblkid/include/all-io.h
new file mode 100644
index 000000000..9a4aeba3b
--- /dev/null
+++ b/libblkid/include/all-io.h
@@ -0,0 +1,84 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ * Petr Uzel <petr.uzel@suse.cz>
+ */
+
+#ifndef UTIL_LINUX_ALL_IO_H
+#define UTIL_LINUX_ALL_IO_H
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "c.h"
+
+static inline int write_all(int fd, const void *buf, size_t count)
+{
+ while (count) {
+ ssize_t tmp;
+
+ errno = 0;
+ tmp = write(fd, buf, count);
+ if (tmp > 0) {
+ count -= tmp;
+ if (count)
+ buf = (void *) ((char *) buf + tmp);
+ } else if (errno != EINTR && errno != EAGAIN)
+ return -1;
+ if (errno == EAGAIN) /* Try later, *sigh* */
+ usleep(250000);
+ }
+ return 0;
+}
+
+static inline int fwrite_all(const void *ptr, size_t size,
+ size_t nmemb, FILE *stream)
+{
+ while (nmemb) {
+ size_t tmp;
+
+ errno = 0;
+ tmp = fwrite(ptr, size, nmemb, stream);
+ if (tmp > 0) {
+ nmemb -= tmp;
+ if (nmemb)
+ ptr = (void *) ((char *) ptr + (tmp * size));
+ } else if (errno != EINTR && errno != EAGAIN)
+ return -1;
+ if (errno == EAGAIN) /* Try later, *sigh* */
+ usleep(250000);
+ }
+ return 0;
+}
+
+static inline ssize_t read_all(int fd, char *buf, size_t count)
+{
+ ssize_t ret;
+ ssize_t c = 0;
+ int tries = 0;
+
+ memset(buf, 0, count);
+ while (count > 0) {
+ ret = read(fd, buf, count);
+ if (ret <= 0) {
+ if ((errno == EAGAIN || errno == EINTR || ret == 0) &&
+ (tries++ < 5)) {
+ usleep(250000);
+ continue;
+ }
+ return c ? c : -1;
+ }
+ if (ret > 0)
+ tries = 0;
+ count -= ret;
+ buf += ret;
+ c += ret;
+ }
+ return c;
+}
+
+
+#endif /* UTIL_LINUX_ALL_IO_H */
diff --git a/libblkid/include/at.h b/libblkid/include/at.h
new file mode 100644
index 000000000..63a80f0c0
--- /dev/null
+++ b/libblkid/include/at.h
@@ -0,0 +1,32 @@
+/*
+ * wrappers for "at" functions.
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_AT_H
+#define UTIL_LINUX_AT_H
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "c.h"
+
+extern int fstat_at(int dir, const char *dirname,
+ const char *filename, struct stat *st, int nofollow);
+
+extern int open_at(int dir, const char *dirname,
+ const char *filename, int flags);
+
+extern FILE *fopen_at(int dir, const char *dirname, const char *filename,
+ int flags, const char *mode);
+
+extern ssize_t readlink_at(int dir, const char *dirname, const char *pathname,
+ char *buf, size_t bufsiz);
+
+
+#endif /* UTIL_LINUX_AT_H */
diff --git a/libblkid/include/bitops.h b/libblkid/include/bitops.h
new file mode 100644
index 000000000..498ec63e7
--- /dev/null
+++ b/libblkid/include/bitops.h
@@ -0,0 +1,124 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#ifndef BITOPS_H
+#define BITOPS_H
+
+#include <stdint.h>
+#include <sys/param.h>
+
+#if defined(HAVE_BYTESWAP_H)
+# include <byteswap.h>
+#endif
+
+#if defined(HAVE_ENDIAN_H)
+# include <endian.h>
+#elif defined(HAVE_SYS_ENDIAN_H) /* BSDs have them here */
+# include <sys/endian.h>
+#endif
+
+#if defined(__OpenBSD__)
+# include <sys/types.h>
+# define be16toh(x) betoh16(x)
+# define be32toh(x) betoh32(x)
+# define be64toh(x) betoh64(x)
+#endif
+
+/*
+ * Fallbacks
+ */
+#ifndef bswap_16
+# define bswap_16(x) ((((x) & 0x00FF) << 8) | \
+ (((x) & 0xFF00) >> 8))
+#endif
+
+#ifndef bswap_32
+# define bswap_32(x) ((((x) & 0x000000FF) << 24) | \
+ (((x) & 0x0000FF00) << 8) | \
+ (((x) & 0x00FF0000) >> 8) | \
+ (((x) & 0xFF000000) >> 24))
+#endif
+
+#ifndef bswap_64
+# define bswap_64(x) ((((x) & 0x00000000000000FFULL) << 56) | \
+ (((x) & 0x000000000000FF00ULL) << 40) | \
+ (((x) & 0x0000000000FF0000ULL) << 24) | \
+ (((x) & 0x00000000FF000000ULL) << 8) | \
+ (((x) & 0x000000FF00000000ULL) >> 8) | \
+ (((x) & 0x0000FF0000000000ULL) >> 24) | \
+ (((x) & 0x00FF000000000000ULL) >> 40) | \
+ (((x) & 0xFF00000000000000ULL) >> 56))
+#endif
+
+#ifndef htobe16
+# if !defined(WORDS_BIGENDIAN)
+# define htobe16(x) bswap_16 (x)
+# define htole16(x) (x)
+# define be16toh(x) bswap_16 (x)
+# define le16toh(x) (x)
+# define htobe32(x) bswap_32 (x)
+# define htole32(x) (x)
+# define be32toh(x) bswap_32 (x)
+# define le32toh(x) (x)
+# define htobe64(x) bswap_64 (x)
+# define htole64(x) (x)
+# define be64toh(x) bswap_64 (x)
+# define le64toh(x) (x)
+# else
+# define htobe16(x) (x)
+# define htole16(x) bswap_16 (x)
+# define be16toh(x) (x)
+# define le16toh(x) bswap_16 (x)
+# define htobe32(x) (x)
+# define htole32(x) bswap_32 (x)
+# define be32toh(x) (x)
+# define le32toh(x) bswap_32 (x)
+# define htobe64(x) (x)
+# define htole64(x) bswap_64 (x)
+# define be64toh(x) (x)
+# define le64toh(x) bswap_64 (x)
+# endif
+#endif
+
+/*
+ * Byte swab macros (based on linux/byteorder/swab.h)
+ */
+#define swab16(x) bswap_16(x)
+#define swab32(x) bswap_32(x)
+#define swab64(x) bswap_64(x)
+
+#define cpu_to_le16(x) ((uint16_t) htole16(x))
+#define cpu_to_le32(x) ((uint32_t) htole32(x))
+#define cpu_to_le64(x) ((uint64_t) htole64(x))
+
+#define cpu_to_be16(x) ((uint16_t) htobe16(x))
+#define cpu_to_be32(x) ((uint32_t) htobe32(x))
+#define cpu_to_be64(x) ((uint64_t) htobe64(x))
+
+#define le16_to_cpu(x) ((uint16_t) le16toh(x))
+#define le32_to_cpu(x) ((uint32_t) le32toh(x))
+#define le64_to_cpu(x) ((uint64_t) le64toh(x))
+
+#define be16_to_cpu(x) ((uint16_t) be16toh(x))
+#define be32_to_cpu(x) ((uint32_t) be32toh(x))
+#define be64_to_cpu(x) ((uint64_t) be64toh(x))
+
+/*
+ * Bit map related macros. Usually provided by libc.
+ */
+#ifndef NBBY
+# define NBBY CHAR_BIT
+#endif
+
+#ifndef setbit
+# define setbit(a,i) ((a)[(i)/NBBY] |= 1<<((i)%NBBY))
+# define clrbit(a,i) ((a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
+# define isset(a,i) ((a)[(i)/NBBY] & (1<<((i)%NBBY)))
+# define isclr(a,i) (((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
+#endif
+
+#endif /* BITOPS_H */
+
diff --git a/libblkid/include/blkdev.h b/libblkid/include/blkdev.h
new file mode 100644
index 000000000..c994795a2
--- /dev/null
+++ b/libblkid/include/blkdev.h
@@ -0,0 +1,146 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#ifndef BLKDEV_H
+#define BLKDEV_H
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_IOCCOM_H
+# include <sys/ioccom.h> /* for _IO macro on e.g. Solaris */
+#endif
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifdef HAVE_SYS_MKDEV_H
+# include <sys/mkdev.h> /* major and minor on Solaris */
+#endif
+
+#define DEFAULT_SECTOR_SIZE 512
+
+#ifdef __linux__
+/* very basic ioctls, should be available everywhere */
+# ifndef BLKROSET
+# define BLKROSET _IO(0x12,93) /* set device read-only (0 = read-write) */
+# define BLKROGET _IO(0x12,94) /* get read-only status (0 = read_write) */
+# define BLKRRPART _IO(0x12,95) /* re-read partition table */
+# define BLKGETSIZE _IO(0x12,96) /* return device size /512 (long *arg) */
+# define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */
+# define BLKRASET _IO(0x12,98) /* set read ahead for block device */
+# define BLKRAGET _IO(0x12,99) /* get current read ahead setting */
+# define BLKFRASET _IO(0x12,100) /* set filesystem (mm/filemap.c) read-ahead */
+# define BLKFRAGET _IO(0x12,101) /* get filesystem (mm/filemap.c) read-ahead */
+# define BLKSECTSET _IO(0x12,102) /* set max sectors per request (ll_rw_blk.c) */
+# define BLKSECTGET _IO(0x12,103) /* get max sectors per request (ll_rw_blk.c) */
+# define BLKSSZGET _IO(0x12,104) /* get block device sector size */
+
+/* ioctls introduced in 2.2.16, removed in 2.5.58 */
+# define BLKELVGET _IOR(0x12,106,size_t) /* elevator get */
+# define BLKELVSET _IOW(0x12,107,size_t) /* elevator set */
+
+# define BLKBSZGET _IOR(0x12,112,size_t)
+# define BLKBSZSET _IOW(0x12,113,size_t)
+# endif /* !BLKROSET */
+
+# ifndef BLKGETSIZE64
+# define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */
+# endif
+
+/* block device topology ioctls, introduced in 2.6.32 (commit ac481c20) */
+# ifndef BLKIOMIN
+# define BLKIOMIN _IO(0x12,120)
+# define BLKIOOPT _IO(0x12,121)
+# define BLKALIGNOFF _IO(0x12,122)
+# define BLKPBSZGET _IO(0x12,123)
+# endif
+
+/* discard zeroes support, introduced in 2.6.33 (commit 98262f27) */
+# ifndef BLKDISCARDZEROES
+# define BLKDISCARDZEROES _IO(0x12,124)
+# endif
+
+/* filesystem freeze, introduced in 2.6.29 (commit fcccf502) */
+# ifndef FIFREEZE
+# define FIFREEZE _IOWR('X', 119, int) /* Freeze */
+# define FITHAW _IOWR('X', 120, int) /* Thaw */
+# endif
+
+/* uniform CD-ROM information */
+# ifndef CDROM_GET_CAPABILITY
+# define CDROM_GET_CAPABILITY 0x5331
+# endif
+
+#endif /* __linux */
+
+
+#ifdef APPLE_DARWIN
+# define BLKGETSIZE DKIOCGETBLOCKCOUNT32
+#endif
+
+#ifndef HDIO_GETGEO
+# ifdef __linux__
+# define HDIO_GETGEO 0x0301
+# endif
+
+struct hd_geometry {
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders; /* truncated */
+ unsigned long start;
+};
+#endif /* HDIO_GETGEO */
+
+
+/* are we working with block device? */
+int is_blkdev(int fd);
+
+/* Determine size in bytes */
+off_t blkdev_find_size (int fd);
+
+/* get size in bytes */
+int blkdev_get_size(int fd, unsigned long long *bytes);
+
+/* get 512-byte sector count */
+int blkdev_get_sectors(int fd, unsigned long long *sectors);
+
+/* get hardware sector size */
+int blkdev_get_sector_size(int fd, int *sector_size);
+
+/* specifies whether or not the device is misaligned */
+int blkdev_is_misaligned(int fd);
+
+/* get physical block device size */
+int blkdev_get_physector_size(int fd, int *sector_size);
+
+/* is the device cdrom capable? */
+int blkdev_is_cdrom(int fd);
+
+/* get device's geometry - legacy */
+int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s);
+
+/* SCSI device types. Copied almost as-is from kernel header.
+ * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/scsi/scsi.h */
+#define SCSI_TYPE_DISK 0x00
+#define SCSI_TYPE_TAPE 0x01
+#define SCSI_TYPE_PRINTER 0x02
+#define SCSI_TYPE_PROCESSOR 0x03 /* HP scanners use this */
+#define SCSI_TYPE_WORM 0x04 /* Treated as ROM by our system */
+#define SCSI_TYPE_ROM 0x05
+#define SCSI_TYPE_SCANNER 0x06
+#define SCSI_TYPE_MOD 0x07 /* Magneto-optical disk - treated as SCSI_TYPE_DISK */
+#define SCSI_TYPE_MEDIUM_CHANGER 0x08
+#define SCSI_TYPE_COMM 0x09 /* Communications device */
+#define SCSI_TYPE_RAID 0x0c
+#define SCSI_TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */
+#define SCSI_TYPE_RBC 0x0e
+#define SCSI_TYPE_OSD 0x11
+#define SCSI_TYPE_NO_LUN 0x7f
+
+/* convert scsi type code to name */
+const char *blkdev_scsi_type_to_name(int type);
+
+
+#endif /* BLKDEV_H */
diff --git a/libblkid/include/blkid.h b/libblkid/include/blkid.h
new file mode 100644
index 000000000..4d4313021
--- /dev/null
+++ b/libblkid/include/blkid.h
@@ -0,0 +1,414 @@
+/*
+ * 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 LIBBLKID_VERSION "2.25.0"
+#define LIBBLKID_DATE "22-Jul-2014"
+
+/**
+ * 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
+
+/* cache.c */
+extern void blkid_init_debug(int mask);
+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,
+ char *search_type, 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));
+extern void blkid_free_probe(blkid_probe pr);
+
+extern void blkid_reset_probe(blkid_probe pr);
+
+extern int blkid_probe_set_device(blkid_probe pr, int fd,
+ blkid_loff_t off, blkid_loff_t size);
+
+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);
+extern blkid_loff_t blkid_probe_get_offset(blkid_probe pr);
+extern unsigned int blkid_probe_get_sectorsize(blkid_probe pr);
+extern blkid_loff_t blkid_probe_get_sectors(blkid_probe pr);
+
+extern int blkid_probe_get_fd(blkid_probe pr);
+
+/*
+ * superblocks probing
+ */
+extern int blkid_known_fstype(const char *fstype);
+
+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);
+
+#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_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);
+extern int blkid_probe_reset_superblocks_filter(blkid_probe pr);
+extern int blkid_probe_invert_superblocks_filter(blkid_probe pr);
+
+/**
+ * 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[]);
+
+#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);
+
+/*
+ * topology probing
+ */
+extern int blkid_probe_enable_topology(blkid_probe pr, int enable);
+
+/* binary interface */
+extern blkid_topology blkid_probe_get_topology(blkid_probe pr);
+
+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));
+
+/*
+ * partitions probing
+ */
+extern int blkid_known_pttype(const char *pttype);
+
+extern int blkid_probe_enable_partitions(blkid_probe pr, int enable);
+
+extern int blkid_probe_reset_partitions_filter(blkid_probe pr);
+extern int blkid_probe_invert_partitions_filter(blkid_probe pr);
+extern int blkid_probe_filter_partitions_type(blkid_probe pr, int flag, char *names[]);
+
+/* 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);
+
+/* binary interface */
+extern blkid_partlist blkid_probe_get_partitions(blkid_probe pr);
+
+extern int blkid_partlist_numof_partitions(blkid_partlist ls);
+extern blkid_parttable blkid_partlist_get_table(blkid_partlist ls);
+extern blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n);
+extern blkid_partition blkid_partlist_get_partition_by_partno(blkid_partlist ls, int n);
+extern blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno);
+extern blkid_parttable blkid_partition_get_table(blkid_partition par);
+
+extern const char *blkid_partition_get_name(blkid_partition par);
+extern const char *blkid_partition_get_uuid(blkid_partition par);
+extern int blkid_partition_get_partno(blkid_partition par);
+extern blkid_loff_t blkid_partition_get_start(blkid_partition par);
+extern blkid_loff_t blkid_partition_get_size(blkid_partition par);
+
+extern int blkid_partition_get_type(blkid_partition par)
+ __ul_attribute__((nonnull));
+
+extern const char *blkid_partition_get_type_string(blkid_partition par);
+
+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);
+extern const char *blkid_parttable_get_id(blkid_parttable tab);
+
+extern blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab);
+extern blkid_partition blkid_parttable_get_parent(blkid_parttable tab);
+
+/*
+ * NAME=value low-level interface
+ */
+extern int blkid_do_probe(blkid_probe pr);
+extern int blkid_do_safeprobe(blkid_probe pr);
+extern int blkid_do_fullprobe(blkid_probe pr);
+
+extern int blkid_probe_numof_values(blkid_probe pr);
+extern int blkid_probe_get_value(blkid_probe pr, int num, const char **name,
+ const char **data, size_t *len);
+extern int blkid_probe_lookup_value(blkid_probe pr, const char *name,
+ const char **data, size_t *len);
+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);
+extern int blkid_probe_step_back(blkid_probe pr);
+
+/*
+ * 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/include/canonicalize.h b/libblkid/include/canonicalize.h
new file mode 100644
index 000000000..7a18aca09
--- /dev/null
+++ b/libblkid/include/canonicalize.h
@@ -0,0 +1,21 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 Library Public License for more details.
+ */
+#ifndef CANONICALIZE_H
+#define CANONICALIZE_H
+
+#include "c.h" /* for PATH_MAX */
+
+extern char *canonicalize_path(const char *path);
+extern char *canonicalize_path_restricted(const char *path);
+extern char *canonicalize_dm_name(const char *ptname);
+
+#endif /* CANONICALIZE_H */
diff --git a/libblkid/include/carefulputc.h b/libblkid/include/carefulputc.h
new file mode 100644
index 000000000..3a0ec5b66
--- /dev/null
+++ b/libblkid/include/carefulputc.h
@@ -0,0 +1,67 @@
+#ifndef UTIL_LINUX_CAREFUULPUTC_H
+#define UTIL_LINUX_CAREFUULPUTC_H
+
+/*
+ * A putc() for use in write and wall (that sometimes are sgid tty).
+ * It avoids control characters in our locale, and also ASCII control
+ * characters. Note that the locale of the recipient is unknown.
+*/
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+static inline int fputc_careful(int c, FILE *fp, const char fail)
+{
+ int ret;
+
+ if (isprint(c) || c == '\a' || c == '\t' || c == '\r' || c == '\n')
+ ret = putc(c, fp);
+ else if (!isascii(c))
+ ret = fprintf(fp, "\\%3o", (unsigned char)c);
+ else {
+ ret = putc(fail, fp);
+ if (ret != EOF)
+ ret = putc(c ^ 0x40, fp);
+ }
+ return (ret < 0) ? EOF : 0;
+}
+
+static inline void fputs_quoted(const char *data, FILE *out)
+{
+ const char *p;
+
+ fputc('"', out);
+ for (p = data; p && *p; p++) {
+ if ((unsigned char) *p == 0x22 || /* " */
+ (unsigned char) *p == 0x5c || /* \ */
+ (unsigned char) *p == 0x60 || /* ` */
+ (unsigned char) *p == 0x24 || /* $ */
+ !isprint((unsigned char) *p) ||
+ iscntrl((unsigned char) *p)) {
+
+ fprintf(out, "\\x%02x", (unsigned char) *p);
+ } else
+ fputc(*p, out);
+ }
+ fputc('"', out);
+}
+
+static inline void fputs_nonblank(const char *data, FILE *out)
+{
+ const char *p;
+
+ for (p = data; p && *p; p++) {
+ if (isblank((unsigned char) *p) ||
+ (unsigned char) *p == 0x5c || /* \ */
+ !isprint((unsigned char) *p) ||
+ iscntrl((unsigned char) *p)) {
+
+ fprintf(out, "\\x%02x", (unsigned char) *p);
+
+ } else
+ fputc(*p, out);
+ }
+}
+
+
+#endif /* _CAREFUULPUTC_H */
diff --git a/libblkid/include/closestream.h b/libblkid/include/closestream.h
new file mode 100644
index 000000000..7842456fb
--- /dev/null
+++ b/libblkid/include/closestream.h
@@ -0,0 +1,71 @@
+#ifndef UTIL_LINUX_CLOSESTREAM_H
+#define UTIL_LINUX_CLOSESTREAM_H
+
+#include <stdio.h>
+#ifdef HAVE_STDIO_EXT_H
+#include <stdio_ext.h>
+#endif
+#include <unistd.h>
+
+#include "c.h"
+#include "nls.h"
+
+#ifndef HAVE___FPENDING
+static inline int
+__fpending(FILE *stream __attribute__((__unused__)))
+{
+ return 0;
+}
+#endif
+
+static inline int
+close_stream(FILE * stream)
+{
+ const int some_pending = (__fpending(stream) != 0);
+ const int prev_fail = (ferror(stream) != 0);
+ const int fclose_fail = (fclose(stream) != 0);
+
+ if (prev_fail || (fclose_fail && (some_pending || errno != EBADF))) {
+ if (!fclose_fail && !(errno == EPIPE))
+ errno = 0;
+ return EOF;
+ }
+ return 0;
+}
+
+/* Meant to be used atexit(close_stdout); */
+static inline void
+close_stdout(void)
+{
+ if (close_stream(stdout) != 0 && !(errno == EPIPE)) {
+ if (errno)
+ warn(_("write error"));
+ else
+ warnx(_("write error"));
+ _exit(EXIT_FAILURE);
+ }
+
+ if (close_stream(stderr) != 0)
+ _exit(EXIT_FAILURE);
+}
+
+#ifndef HAVE_FSYNC
+static inline int
+fsync(int fd __attribute__((__unused__)))
+{
+ return 0;
+}
+#endif
+
+static inline int
+close_fd(int fd)
+{
+ const int fsync_fail = (fsync(fd) != 0);
+ const int close_fail = (close(fd) != 0);
+
+ if (fsync_fail || close_fail)
+ return EOF;
+ return 0;
+}
+
+#endif /* UTIL_LINUX_CLOSESTREAM_H */
diff --git a/libblkid/include/colors.h b/libblkid/include/colors.h
new file mode 100644
index 000000000..97efc486a
--- /dev/null
+++ b/libblkid/include/colors.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2012-2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_COLORS_H
+#define UTIL_LINUX_COLORS_H
+
+#include <stdio.h>
+#include <unistd.h>
+
+#define UL_COLOR_RESET "\033[0m"
+#define UL_COLOR_BOLD "\033[1m"
+#define UL_COLOR_HALFBRIGHT "\033[2m"
+#define UL_COLOR_UNDERSCORE "\033[4m"
+#define UL_COLOR_BLINK "\033[5m"
+#define UL_COLOR_REVERSE "\033[7m"
+
+/* Standard colors */
+#define UL_COLOR_BLACK "\033[30m"
+#define UL_COLOR_RED "\033[31m"
+#define UL_COLOR_GREEN "\033[32m"
+#define UL_COLOR_BROWN "\033[33m" /* well, brown */
+#define UL_COLOR_BLUE "\033[34m"
+#define UL_COLOR_MAGENTA "\033[35m"
+#define UL_COLOR_CYAN "\033[36m"
+#define UL_COLOR_GRAY "\033[37m"
+
+/* Bold variants */
+#define UL_COLOR_DARK_GRAY "\033[1;30m"
+#define UL_COLOR_BOLD_RED "\033[1;31m"
+#define UL_COLOR_BOLD_GREEN "\033[1;32m"
+#define UL_COLOR_BOLD_YELLOW "\033[1;33m"
+#define UL_COLOR_BOLD_BLUE "\033[1;34m"
+#define UL_COLOR_BOLD_MAGENTA "\033[1;35m"
+#define UL_COLOR_BOLD_CYAN "\033[1;36m"
+
+#define UL_COLOR_WHITE "\033[1;37m"
+
+/* --color[=WHEN] */
+enum colortmode {
+ UL_COLORMODE_AUTO = 0,
+ UL_COLORMODE_NEVER,
+ UL_COLORMODE_ALWAYS,
+ UL_COLORMODE_UNDEF,
+
+ __UL_NCOLORMODES /* last */
+};
+
+extern int colormode_from_string(const char *str);
+extern int colormode_or_err(const char *str, const char *errmsg);
+
+/* Initialize the global variable UL_COLOR_TERM_OK */
+extern int colors_init(int mode, const char *util_name);
+
+/* Returns 1 or 0 */
+extern int colors_wanted(void);
+
+/* temporary enable/disable colors */
+extern void colors_off(void);
+extern void colors_on(void);
+
+
+/* Set the color */
+extern void color_fenable(const char *seq, FILE *f);
+
+extern void color_scheme_fenable(const char *name, const char *dflt, FILE *f);
+extern const char *color_scheme_get_sequence(const char *name, const char *dflt);
+
+static inline void color_enable(const char *seq)
+{
+ color_fenable(seq, stdout);
+}
+
+static inline void color_scheme_enable(const char *name, const char *dflt)
+{
+ color_scheme_fenable(name, dflt, stdout);
+}
+
+/* Reset colors to default */
+extern void color_fdisable(FILE *f);
+
+static inline void color_disable(void)
+{
+ color_fdisable(stdout);
+}
+
+/* converts "red" to UL_COLOR_RED, etc. */
+extern const char *color_sequence_from_colorname(const char *str);
+
+
+#endif /* UTIL_LINUX_COLORS_H */
diff --git a/libblkid/include/config.h b/libblkid/include/config.h
new file mode 100644
index 000000000..0a71fcf9c
--- /dev/null
+++ b/libblkid/include/config.h
@@ -0,0 +1,687 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Enable agetty --reload feature */
+#define AGETTY_RELOAD 1
+
+/* Should chfn and chsh require the user to enter the password? */
+#define CHFN_CHSH_PASSWORD 1
+
+/* Path to hwclock adjtime file */
+#define CONFIG_ADJTIME_PATH "/etc/adjtime"
+
+/* Define to 1 if translation of program messages to the user's native
+ language is requested. */
+#define ENABLE_NLS 1
+
+/* search path for fs helpers */
+#define FS_SEARCH_PATH "/sbin:/sbin/fs.d:/sbin/fs"
+
+/* Define to 1 if you have the <asm/io.h> header file. */
+/* #undef HAVE_ASM_IO_H */
+
+/* Define to 1 if you have the <byteswap.h> header file. */
+#define HAVE_BYTESWAP_H 1
+
+/* Define to 1 if you have the Mac OS X function CFLocaleCopyCurrent in the
+ CoreFoundation framework. */
+/* #undef HAVE_CFLOCALECOPYCURRENT */
+
+/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in
+ the CoreFoundation framework. */
+/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#define HAVE_CLOCK_GETTIME 1
+
+/* Define to 1 if the system has the type `cpu_set_t'. */
+#define HAVE_CPU_SET_T 1
+
+/* Define to 1 if you have the <crypt.h> header file. */
+#define HAVE_CRYPT_H 1
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+ */
+#define HAVE_DCGETTEXT 1
+
+/* Define to 1 if you have the declaration of `CPU_ALLOC', and to 0 if you
+ don't. */
+#define HAVE_DECL_CPU_ALLOC 1
+
+/* Define to 1 if you have the declaration of `dirfd', and to 0 if you don't.
+ */
+/* #undef HAVE_DECL_DIRFD */
+
+/* Define to 1 if you have the declaration of `_NL_TIME_WEEK_1STDAY', and to 0
+ if you don't. */
+#define HAVE_DECL__NL_TIME_WEEK_1STDAY 1
+
+/* Define to 1 if you have the `dirfd' function. */
+#define HAVE_DIRFD 1
+
+/* Define to 1 if `dd_fd' is a member of `DIR'. */
+/* #undef HAVE_DIR_DD_FD */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <endian.h> header file. */
+#define HAVE_ENDIAN_H 1
+
+/* Define to 1 if have **environ prototype */
+#define HAVE_ENVIRON_DECL 1
+
+/* Define to 1 if you have the `err' function. */
+#define HAVE_ERR 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the `errx' function. */
+#define HAVE_ERRX 1
+
+/* Define to 1 if you have the <err.h> header file. */
+#define HAVE_ERR_H 1
+
+/* Have valid fallocate() function */
+#define HAVE_FALLOCATE 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#define HAVE_FSEEKO 1
+
+/* Define to 1 if you have the `fstatat' function. */
+#define HAVE_FSTATAT 1
+
+/* Define to 1 if you have the `fsync' function. */
+#define HAVE_FSYNC 1
+
+/* Define to 1 if you have the `futimens' function. */
+#define HAVE_FUTIMENS 1
+
+/* Define to 1 if you have the `getdomainname' function. */
+#define HAVE_GETDOMAINNAME 1
+
+/* Define to 1 if you have the `getdtablesize' function. */
+#define HAVE_GETDTABLESIZE 1
+
+/* Define to 1 if you have the `getexecname' function. */
+/* #undef HAVE_GETEXECNAME */
+
+/* Define to 1 if you have the `getmntinfo' function. */
+/* #undef HAVE_GETMNTINFO */
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#define HAVE_GETOPT_H 1
+
+/* Define to 1 if you have the `getrlimit' function. */
+#define HAVE_GETRLIMIT 1
+
+/* Define to 1 if you have the `getsgnam' function. */
+#define HAVE_GETSGNAM 1
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+#define HAVE_GETTEXT 1
+
+/* Define if you have the iconv() function and it works. */
+/* #undef HAVE_ICONV */
+
+/* Define to 1 if you have the `inotify_init' function. */
+#define HAVE_INOTIFY_INIT 1
+
+/* Define to 1 if you have the `inotify_init1' function. */
+#define HAVE_INOTIFY_INIT1 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `ioperm' function. */
+#define HAVE_IOPERM 1
+
+/* Define to 1 if you have the `iopl' function. */
+#define HAVE_IOPL 1
+
+/* Define to 1 if you have the `jrand48' function. */
+#define HAVE_JRAND48 1
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#define HAVE_LANGINFO_H 1
+
+/* Define to 1 if you have the `lchown' function. */
+#define HAVE_LCHOWN 1
+
+/* Define to 1 if you have the `audit' library (-laudit). */
+/* #undef HAVE_LIBAUDIT */
+
+/* Define to 1 if you have the -lblkid. */
+#define HAVE_LIBBLKID 1
+
+/* Define to 1 if you have the `cap-ng' library (-lcap-ng). */
+/* #undef HAVE_LIBCAP_NG */
+
+/* Do we need -lcrypt? */
+#define HAVE_LIBCRYPT 1
+
+/* Define to 1 if you have the `ncurses' library (-lncurses). */
+#define HAVE_LIBNCURSES 1
+
+/* Define to 1 if you have the `ncursesw' library (-lncursesw). */
+/* #undef HAVE_LIBNCURSESW */
+
+/* Define if SELinux is available */
+/* #undef HAVE_LIBSELINUX */
+
+/* Define if libsystemd is available */
+/* #undef HAVE_LIBSYSTEMD */
+
+/* Define to 1 if you have the `termcap' library (-ltermcap). */
+#define HAVE_LIBTERMCAP 1
+
+/* Define to 1 if you have the `udev' library (-ludev). */
+/* #undef HAVE_LIBUDEV */
+
+/* Define if libuser is available */
+/* #undef HAVE_LIBUSER */
+
+/* Define to 1 if you have the `utempter' library (-lutempter). */
+/* #undef HAVE_LIBUTEMPTER */
+
+/* Define to 1 if you have the `util' library (-lutil). */
+#define HAVE_LIBUTIL 1
+
+/* Define to 1 if you have the -luuid. */
+#define HAVE_LIBUUID 1
+
+/* Define to 1 if you have the <linux/blkpg.h> header file. */
+#define HAVE_LINUX_BLKPG_H 1
+
+/* Define to 1 if you have the <linux/cdrom.h> header file. */
+#define HAVE_LINUX_CDROM_H 1
+
+/* Define to 1 if you have the <linux/compiler.h> header file. */
+/* #undef HAVE_LINUX_COMPILER_H */
+
+/* Define to 1 if you have the <linux/falloc.h> header file. */
+#define HAVE_LINUX_FALLOC_H 1
+
+/* Define to 1 if you have the <linux/fd.h> header file. */
+#define HAVE_LINUX_FD_H 1
+
+/* Define to 1 if you have the <linux/gsmmux.h> header file. */
+/* #undef HAVE_LINUX_GSMMUX_H */
+
+/* Define to 1 if you have the <linux/major.h> header file. */
+#define HAVE_LINUX_MAJOR_H 1
+
+/* Define to 1 if you have the <linux/raw.h> header file. */
+#define HAVE_LINUX_RAW_H 1
+
+/* Define to 1 if you have the <linux/securebits.h> header file. */
+#define HAVE_LINUX_SECUREBITS_H 1
+
+/* Define to 1 if you have the <linux/tiocl.h> header file. */
+#define HAVE_LINUX_TIOCL_H 1
+
+/* Define to 1 if you have the <linux/version.h> header file. */
+#define HAVE_LINUX_VERSION_H 1
+
+/* Define to 1 if you have the <linux/watchdog.h> header file. */
+#define HAVE_LINUX_WATCHDOG_H 1
+
+/* Define to 1 if you have the `llseek' function. */
+#define HAVE_LLSEEK 1
+
+/* Define to 1 if have llseek prototype */
+/* #undef HAVE_LLSEEK_PROTOTYPE */
+
+/* Define to 1 if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if the system has the type `loff_t'. */
+#define HAVE_LOFF_T 1
+
+/* Define to 1 if you have the `lseek64' function. */
+#define HAVE_LSEEK64 1
+
+/* Define to 1 if have lseek64 prototype */
+#define HAVE_LSEEK64_PROTOTYPE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `mempcpy' function. */
+#define HAVE_MEMPCPY 1
+
+/* Define to 1 if you have the <mntent.h> header file. */
+#define HAVE_MNTENT_H 1
+
+/* Define to 1 if you have the `nanosleep' function. */
+#define HAVE_NANOSLEEP 1
+
+/* Define to 1 if you have the <ncursesw/ncurses.h> header file. */
+/* #undef HAVE_NCURSESW_NCURSES_H */
+
+/* Define to 1 if you have the <ncurses.h> header file. */
+#define HAVE_NCURSES_H 1
+
+/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
+/* #undef HAVE_NCURSES_NCURSES_H */
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <net/if_dl.h> header file. */
+/* #undef HAVE_NET_IF_DL_H */
+
+/* Define to 1 if you have the <net/if.h> header file. */
+#define HAVE_NET_IF_H 1
+
+/* Define to 1 if you have the `openat' function. */
+#define HAVE_OPENAT 1
+
+/* Define to 1 if you have the `open_memstream' function. */
+#define HAVE_OPEN_MEMSTREAM 1
+
+/* Define to 1 if you have the <paths.h> header file. */
+#define HAVE_PATHS_H 1
+
+/* Define to 1 if you have the `personality' function. */
+#define HAVE_PERSONALITY 1
+
+/* Define to 1 if you have the `posix_fadvise' function. */
+#define HAVE_POSIX_FADVISE 1
+
+/* Define to 1 if you have the `prctl' function. */
+#define HAVE_PRCTL 1
+
+/* Define to 1 if you have the `prlimit' function. */
+#define HAVE_PRLIMIT 1
+
+/* Define if program_invocation_short_name is defined */
+#define HAVE_PROGRAM_INVOCATION_SHORT_NAME 1
+
+/* Define to 1 if you have the <pty.h> header file. */
+#define HAVE_PTY_H 1
+
+/* Define to 1 if you have the `qsort_r' function. */
+#define HAVE_QSORT_R 1
+
+/* Define if curses library has the resizeterm(). */
+/* #undef HAVE_RESIZETERM */
+
+/* Define to 1 if you have the `rpmatch' function. */
+#define HAVE_RPMATCH 1
+
+/* Define if struct sockaddr contains sa_len */
+/* #undef HAVE_SA_LEN */
+
+/* Define to 1 if you have the `scandirat' function. */
+#define HAVE_SCANDIRAT 1
+
+/* scanf %as modifier */
+/* #undef HAVE_SCANF_AS_MODIFIER */
+
+/* scanf %ms modifier */
+#define HAVE_SCANF_MS_MODIFIER 1
+
+/* Define to 1 if you have the `secure_getenv' function. */
+#define HAVE_SECURE_GETENV 1
+
+/* Define to 1 if you have the `security_get_initial_context' function. */
+/* #undef HAVE_SECURITY_GET_INITIAL_CONTEXT */
+
+/* Define to 1 if you have the <security/openpam.h> header file. */
+/* #undef HAVE_SECURITY_OPENPAM_H */
+
+/* Define to 1 if you have the <security/pam_appl.h> header file. */
+/* #undef HAVE_SECURITY_PAM_APPL_H */
+
+/* Define to 1 if you have the <security/pam_misc.h> header file. */
+/* #undef HAVE_SECURITY_PAM_MISC_H */
+
+/* Define to 1 if you have the `setns' function. */
+#define HAVE_SETNS 1
+
+/* Define to 1 if you have the `setresgid' function. */
+#define HAVE_SETRESGID 1
+
+/* Define to 1 if you have the `setresuid' function. */
+#define HAVE_SETRESUID 1
+
+/* Define to 1 if the system has the type `sighandler_t'. */
+#define HAVE_SIGHANDLER_T 1
+
+/* Define to 1 if you have the `sigqueue' function. */
+#define HAVE_SIGQUEUE 1
+
+/* Define to 1 if you have the <slang.h> header file. */
+/* #undef HAVE_SLANG_H */
+
+/* Define to 1 if you have the <slang/slang.h> header file. */
+/* #undef HAVE_SLANG_SLANG_H */
+
+/* Define to 1 if you have the <slang/slcurses.h> header file. */
+/* #undef HAVE_SLANG_SLCURSES_H */
+
+/* Define to 1 if you have the <slcurses.h> header file. */
+/* #undef HAVE_SLCURSES_H */
+
+/* Add SMACK support */
+/* #undef HAVE_SMACK */
+
+/* Define to 1 if you have the `srandom' function. */
+#define HAVE_SRANDOM 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio_ext.h> header file. */
+#define HAVE_STDIO_EXT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strnchr' function. */
+/* #undef HAVE_STRNCHR */
+
+/* Define to 1 if you have the `strndup' function. */
+#define HAVE_STRNDUP 1
+
+/* Define to 1 if you have the `strnlen' function. */
+#define HAVE_STRNLEN 1
+
+/* Define to 1 if have strsignal function prototype */
+#define HAVE_STRSIGNAL_DECL 1
+
+/* Define to 1 if you have the `strtoull' function. */
+#define HAVE_STRTOULL 1
+
+/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1
+
+/* Define to 1 if `c_line' is a member of `struct termios'. */
+#define HAVE_STRUCT_TERMIOS_C_LINE 1
+
+/* Define to 1 if you have the `sysconf' function. */
+#define HAVE_SYSCONF 1
+
+/* Define to 1 if you have the `sysinfo' function. */
+#define HAVE_SYSINFO 1
+
+/* Define to 1 if you have the <sys/disklabel.h> header file. */
+/* #undef HAVE_SYS_DISKLABEL_H */
+
+/* Define to 1 if you have the <sys/disk.h> header file. */
+/* #undef HAVE_SYS_DISK_H */
+
+/* Define to 1 if you have the <sys/endian.h> header file. */
+/* #undef HAVE_SYS_ENDIAN_H */
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define to 1 if you have the <sys/ioccom.h> header file. */
+/* #undef HAVE_SYS_IOCCOM_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/io.h> header file. */
+#define HAVE_SYS_IO_H 1
+
+/* Define to 1 if you have the <sys/mkdev.h> header file. */
+/* #undef HAVE_SYS_MKDEV_H */
+
+/* Define to 1 if you have the <sys/prctl.h> header file. */
+#define HAVE_SYS_PRCTL_H 1
+
+/* Define to 1 if you have the <sys/queue.h> header file. */
+#define HAVE_SYS_QUEUE_H 1
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#define HAVE_SYS_RESOURCE_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/sockio.h> header file. */
+/* #undef HAVE_SYS_SOCKIO_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/swap.h> header file. */
+#define HAVE_SYS_SWAP_H 1
+
+/* Define to 1 if you have the <sys/syscall.h> header file. */
+#define HAVE_SYS_SYSCALL_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/ttydefaults.h> header file. */
+#define HAVE_SYS_TTYDEFAULTS_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#define HAVE_SYS_UN_H 1
+
+/* Define to 1 if the target supports thread-local storage. */
+#define HAVE_TLS 1
+
+/* Does struct tm have a field tm_gmtoff? */
+#define HAVE_TM_GMTOFF 1
+
+/* Define to 1 if the system has the type `union semun'. */
+/* #undef HAVE_UNION_SEMUN */
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `unlinkat' function. */
+#define HAVE_UNLINKAT 1
+
+/* Define to 1 if you have the `unshare' function. */
+#define HAVE_UNSHARE 1
+
+/* Define to 1 if you have the `updwtmp' function. */
+#define HAVE_UPDWTMP 1
+
+/* Define if curses library has the use_default_colors(). */
+/* #undef HAVE_USE_DEFAULT_COLORS */
+
+/* Define to 1 if you have the `usleep' function. */
+#define HAVE_USLEEP 1
+
+/* Define to 1 if you have the `utimensat' function. */
+#define HAVE_UTIMENSAT 1
+
+/* Define to 1 if you want to use uuid daemon. */
+#define HAVE_UUIDD 1
+
+/* Define to 1 if you have the `warn' function. */
+#define HAVE_WARN 1
+
+/* Define to 1 if you have the `warnx' function. */
+#define HAVE_WARNX 1
+
+/* Do we have wide character support? */
+#define HAVE_WIDECHAR 1
+
+/* Define to 1 if you have the `__fpending' function. */
+#define HAVE___FPENDING 1
+
+/* Define if __progname is defined */
+#define HAVE___PROGNAME 1
+
+/* Define to 1 if you have the `__secure_getenv' function. */
+/* #undef HAVE___SECURE_GETENV */
+
+/* libblkid date string */
+#define LIBBLKID_DATE "22-Jul-2014"
+
+/* libblkid version string */
+#define LIBBLKID_VERSION "2.25.0"
+
+/* libfdisk version string */
+#define LIBFDISK_VERSION "2.25.0"
+
+/* libmount version string */
+#define LIBMOUNT_VERSION "2.25.0"
+
+/* libsmartcols version string */
+#define LIBSMARTCOLS_VERSION "2.25.0"
+
+/* Should login chown /dev/vcsN? */
+/* #undef LOGIN_CHOWN_VCS */
+
+/* Should login stat() the mailbox? */
+/* #undef LOGIN_STAT_MAIL */
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#define LT_OBJDIR ".libs/"
+
+/* Should chsh allow only shells in /etc/shells? */
+#define ONLY_LISTED_SHELLS 1
+
+/* Name of package */
+#define PACKAGE "util-linux"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "kzak@redhat.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "util-linux"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "util-linux 2.25.590-bf6c"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "util-linux"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL "http://www.kernel.org/pub/linux/utils/util-linux/"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "2.25.590-bf6c"
+
+/* Should pg ring the bell on invalid keys? */
+#define PG_BELL 1
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Is swapon() declared with two parameters? */
+#define SWAPON_HAS_TWO_ARGS 1
+
+/* Fallback syscall number for fallocate */
+/* #undef SYS_fallocate */
+
+/* Fallback syscall number for ioprio_get */
+/* #undef SYS_ioprio_get */
+
+/* Fallback syscall number for ioprio_set */
+/* #undef SYS_ioprio_set */
+
+/* Fallback syscall number for pivot_root */
+/* #undef SYS_pivot_root */
+
+/* Fallback syscall number for prlimit64 */
+/* #undef SYS_prlimit64 */
+
+/* Fallback syscall number for sched_getaffinity */
+/* #undef SYS_sched_getaffinity */
+
+/* Fallback syscall number for setns */
+/* #undef SYS_setns */
+
+/* Fallback syscall number for unshare */
+/* #undef SYS_unshare */
+
+/* Should sulogin use a emergency mount of /dev and /proc? */
+/* #undef USE_SULOGIN_EMERGENCY_MOUNT */
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
+
+/* Should wall and write be installed setgid tty? */
+#define USE_TTY_GROUP 1
+
+/* Version number of package */
+#define VERSION "2.25.590-bf6c"
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* # undef WORDS_BIGENDIAN */
+# endif
+#endif
+
+/* Enable large inode numbers on Mac OS X 10.5. */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* #undef _LARGEFILE_SOURCE */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to empty if the keyword `volatile' does not work. Warning: valid
+ code using `volatile' can become incorrect without. Disable with care. */
+/* #undef volatile */
diff --git a/libblkid/include/cpuset.h b/libblkid/include/cpuset.h
new file mode 100644
index 000000000..f8948a984
--- /dev/null
+++ b/libblkid/include/cpuset.h
@@ -0,0 +1,99 @@
+/*
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_CPUSET_H
+#define UTIL_LINUX_CPUSET_H
+
+#include <sched.h>
+
+/*
+ * Fallback for old or obscure libcs without dynamically allocated cpusets
+ *
+ * The following macros are based on code from glibc.
+ *
+ * The GNU C 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.
+ */
+#if !HAVE_DECL_CPU_ALLOC
+
+# define CPU_ZERO_S(setsize, cpusetp) \
+ do { \
+ size_t __i; \
+ size_t __imax = (setsize) / sizeof (__cpu_mask); \
+ __cpu_mask *__bits = (cpusetp)->__bits; \
+ for (__i = 0; __i < __imax; ++__i) \
+ __bits[__i] = 0; \
+ } while (0)
+
+# define CPU_SET_S(cpu, setsize, cpusetp) \
+ ({ size_t __cpu = (cpu); \
+ __cpu < 8 * (setsize) \
+ ? (((__cpu_mask *) ((cpusetp)->__bits))[__CPUELT (__cpu)] \
+ |= __CPUMASK (__cpu)) \
+ : 0; })
+
+# define CPU_ISSET_S(cpu, setsize, cpusetp) \
+ ({ size_t __cpu = (cpu); \
+ __cpu < 8 * (setsize) \
+ ? ((((__cpu_mask *) ((cpusetp)->__bits))[__CPUELT (__cpu)] \
+ & __CPUMASK (__cpu))) != 0 \
+ : 0; })
+
+# define CPU_EQUAL_S(setsize, cpusetp1, cpusetp2) \
+ ({ __cpu_mask *__arr1 = (cpusetp1)->__bits; \
+ __cpu_mask *__arr2 = (cpusetp2)->__bits; \
+ size_t __imax = (setsize) / sizeof (__cpu_mask); \
+ size_t __i; \
+ for (__i = 0; __i < __imax; ++__i) \
+ if (__arr1[__i] != __arr2[__i]) \
+ break; \
+ __i == __imax; })
+
+extern int __cpuset_count_s(size_t setsize, const cpu_set_t *set);
+# define CPU_COUNT_S(setsize, cpusetp) __cpuset_count_s(setsize, cpusetp)
+
+# define CPU_ALLOC_SIZE(count) \
+ ((((count) + __NCPUBITS - 1) / __NCPUBITS) * sizeof (__cpu_mask))
+# define CPU_ALLOC(count) (malloc(CPU_ALLOC_SIZE(count)))
+# define CPU_FREE(cpuset) (free(cpuset))
+
+#endif /* !HAVE_DECL_CPU_ALLOC */
+
+
+#define cpuset_nbits(setsize) (8 * (setsize))
+
+/*
+ * The @idx parametr returns an index of the first mask from @ary array where
+ * the @cpu is set.
+ *
+ * Returns: 0 if found, otherwise 1.
+ */
+static inline int cpuset_ary_isset(size_t cpu, cpu_set_t **ary, size_t nmemb,
+ size_t setsize, size_t *idx)
+{
+ size_t i;
+
+ for (i = 0; i < nmemb; i++) {
+ if (CPU_ISSET_S(cpu, setsize, ary[i])) {
+ *idx = i;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+extern int get_max_number_of_cpus(void);
+
+extern cpu_set_t *cpuset_alloc(int ncpus, size_t *setsize, size_t *nbits);
+extern void cpuset_free(cpu_set_t *set);
+
+extern char *cpulist_create(char *str, size_t len, cpu_set_t *set, size_t setsize);
+extern int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail);
+
+extern char *cpumask_create(char *str, size_t len, cpu_set_t *set, size_t setsize);
+extern int cpumask_parse(const char *str, cpu_set_t *set, size_t setsize);
+
+#endif /* UTIL_LINUX_CPUSET_H */
diff --git a/libblkid/include/crc32.h b/libblkid/include/crc32.h
new file mode 100644
index 000000000..26169109e
--- /dev/null
+++ b/libblkid/include/crc32.h
@@ -0,0 +1,10 @@
+#ifndef UL_NG_CRC32_H
+#define UL_NG_CRC32_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+extern uint32_t crc32(uint32_t seed, const unsigned char *buf, size_t len);
+
+#endif
+
diff --git a/libblkid/include/crc64.h b/libblkid/include/crc64.h
new file mode 100644
index 000000000..40475d56f
--- /dev/null
+++ b/libblkid/include/crc64.h
@@ -0,0 +1,9 @@
+#ifndef UTIL_LINUX_CRC64_H
+#define UTIL_LINUX_CRC64_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+extern uint64_t crc64(uint64_t seed, const unsigned char *data, size_t len);
+
+#endif
diff --git a/libblkid/include/debug.h b/libblkid/include/debug.h
new file mode 100644
index 000000000..0229ab329
--- /dev/null
+++ b/libblkid/include/debug.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_DEBUG_H
+#define UTIL_LINUX_DEBUG_H
+
+
+/*
+ * util-linux debug macros
+ *
+ * The debug stuff is based on <name>_debug_mask that controls what outputs is
+ * expected. The mask is usually initialized by <NAME>_DEBUG= env.variable
+ *
+ * After successful initialization the flag <PREFIX>_DEBUG_INIT is always set
+ * to the mask (this flag is required). The <PREFIX> is usually library API
+ * prefix (e.g. MNT_) or program name (e.g. CFDISK_)
+ *
+ * In the code is possible to use
+ *
+ * DBG(FOO, ul_debug("this is output for foo"));
+ *
+ * where for the FOO has to be defined <PREFIX>_DEBUG_FOO.
+ *
+ * It's possible to initialize the mask by comma delimited strings with
+ * subsystem names (e.g. "LIBMOUNT_DEBUG=options,tab"). In this case is
+ * necessary to define mask names array. This functionality is optional.
+ *
+ * It's stringly recommended to use UL_* macros to define/declare/use
+ * the debug stuff.
+ *
+ * See disk-utils/cfdisk.c: cfdisk_init_debug() for programs debug
+ * or libmount/src/init.c: mnt_init_debug() for library debug
+ *
+ */
+
+#include <stdarg.h>
+#include <string.h>
+
+struct ul_debug_maskname {
+ const char *name;
+ int mask;
+ const char *help;
+};
+#define UL_DEBUG_EMPTY_MASKNAMES {{ NULL, 0, NULL }}
+#define UL_DEBUG_DEFINE_MASKNAMES(m) static const struct ul_debug_maskname m ## _masknames[]
+#define UL_DEBUG_MASKNAMES(m) m ## _masknames
+
+#define UL_DEBUG_DEFINE_MASK(m) int m ## _debug_mask
+#define UL_DEBUG_DECLARE_MASK(m) extern UL_DEBUG_DEFINE_MASK(m)
+
+/* p - flag prefix, m - flag postfix */
+#define UL_DEBUG_DEFINE_FLAG(p, m) p ## m
+
+/* l - library name, p - flag prefix, m - flag postfix, x - function */
+#define __UL_DBG(l, p, m, x) \
+ do { \
+ if ((p ## m) & l ## _debug_mask) { \
+ fprintf(stderr, "%d: %s: %8s: ", getpid(), # l, # m); \
+ x; \
+ } \
+ } while (0)
+
+#define __UL_DBG_CALL(l, p, m, x) \
+ do { \
+ if ((p ## m) & l ## _debug_mask) { \
+ x; \
+ } \
+ } while (0)
+
+#define __UL_DBG_FLUSH(l, p) \
+ do { \
+ if (l ## _debug_mask && \
+ l ## _debug_mask != p ## INIT) { \
+ fflush(stderr); \
+ } \
+ } while (0)
+
+
+#define __UL_INIT_DEBUG(lib, pref, mask, env) \
+ do { \
+ if (lib ## _debug_mask & pref ## INIT) \
+ ; \
+ else if (!mask) { \
+ char *str = getenv(# env); \
+ if (str) \
+ lib ## _debug_mask = ul_debug_parse_envmask(lib ## _masknames, str); \
+ } else \
+ lib ## _debug_mask = mask; \
+ lib ## _debug_mask |= pref ## INIT; \
+ } while (0)
+
+
+static inline void __attribute__ ((__format__ (__printf__, 1, 2)))
+ul_debug(const char *mesg, ...)
+{
+ va_list ap;
+ va_start(ap, mesg);
+ vfprintf(stderr, mesg, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+}
+
+static inline void __attribute__ ((__format__ (__printf__, 2, 3)))
+ul_debugobj(void *handler, const char *mesg, ...)
+{
+ va_list ap;
+
+ if (handler)
+ fprintf(stderr, "[%p]: ", handler);
+ va_start(ap, mesg);
+ vfprintf(stderr, mesg, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+}
+
+static inline int ul_debug_parse_envmask(
+ const struct ul_debug_maskname flagnames[],
+ const char *mask)
+{
+ int res;
+ char *ptr;
+
+ /* let's check for a numeric mask first */
+ res = strtoul(mask, &ptr, 0);
+
+ /* perhaps it's a comma-separated string? */
+ if (ptr && *ptr && flagnames && flagnames[0].name) {
+ char *msbuf, *ms, *name;
+ res = 0;
+
+ ms = msbuf = strdup(mask);
+ if (!ms)
+ return res;
+
+ while ((name = strtok_r(ms, ",", &ptr))) {
+ const struct ul_debug_maskname *d;
+ ms = ptr;
+
+ for (d = flagnames; d && d->name; d++) {
+ if (strcmp(name, d->name) == 0) {
+ res |= d->mask;
+ break;
+ }
+ }
+ /* nothing else we can do by OR-ing the mask */
+ if (res == 0xffff)
+ break;
+ }
+ free(msbuf);
+ } else if (ptr && strcmp(ptr, "all") == 0)
+ res = 0xffff;
+
+ return res;
+}
+
+static inline void ul_debug_print_masks(
+ const char *env,
+ const struct ul_debug_maskname flagnames[])
+{
+ const struct ul_debug_maskname *d;
+
+ if (!flagnames)
+ return;
+
+ fprintf(stderr, "Available \"%s=<name>[,...]|<mask>\" debug masks:\n",
+ env);
+ for (d = flagnames; d && d->name; d++) {
+ if (!d->help)
+ continue;
+ fprintf(stderr, " %-8s [0x%04x] : %s\n",
+ d->name, d->mask, d->help);
+ }
+}
+
+#endif /* UTIL_LINUX_DEBUG_H */
diff --git a/libblkid/include/env.h b/libblkid/include/env.h
new file mode 100644
index 000000000..a53d31027
--- /dev/null
+++ b/libblkid/include/env.h
@@ -0,0 +1,16 @@
+#ifndef UTIL_LINUX_ENV_H
+#define UTIL_LINUX_ENV_H
+
+#include "c.h"
+
+extern void sanitize_env(void);
+extern char *safe_getenv(const char *arg);
+
+static inline void xsetenv(char const *name, char const *val, int overwrite)
+{
+ if (setenv(name, val, overwrite) != 0)
+ err(EXIT_FAILURE, "failed to set the %s environment variable", name);
+}
+
+#endif /* UTIL_LINUX_ENV_H */
+
diff --git a/libblkid/include/exec_shell.h b/libblkid/include/exec_shell.h
new file mode 100644
index 000000000..a2aa757de
--- /dev/null
+++ b/libblkid/include/exec_shell.h
@@ -0,0 +1 @@
+extern void __attribute__((__noreturn__)) exec_shell(void);
diff --git a/libblkid/include/exitcodes.h b/libblkid/include/exitcodes.h
new file mode 100644
index 000000000..24ee12364
--- /dev/null
+++ b/libblkid/include/exitcodes.h
@@ -0,0 +1,35 @@
+#ifndef UTIL_LINUX_EXITCODES_H
+#define UTIL_LINUX_EXITCODES_H
+/*
+ * BE CAREFUL
+ *
+ * These exit codes are part of the official interface for mount,
+ * fsck, mkfs, etc. wrappers.
+ */
+
+/* Exit codes used by mkfs-type programs */
+#define MKFS_EX_OK 0 /* No errors */
+#define MKFS_EX_ERROR 8 /* Operational error */
+#define MKFS_EX_USAGE 16 /* Usage or syntax error */
+
+/* Exit codes used by fsck-type programs */
+#define FSCK_EX_OK 0 /* No errors */
+#define FSCK_EX_NONDESTRUCT 1 /* File system errors corrected */
+#define FSCK_EX_REBOOT 2 /* System should be rebooted */
+#define FSCK_EX_DESTRUCT FSCK_EX_REBOOT /* Alias */
+#define FSCK_EX_UNCORRECTED 4 /* File system errors left uncorrected */
+#define FSCK_EX_ERROR 8 /* Operational error */
+#define FSCK_EX_USAGE 16 /* Usage or syntax error */
+#define FSCK_EX_LIBRARY 128 /* Shared library error */
+
+/* Exit codes used by mount-line programs */
+#define MOUNT_EX_SUCCESS 0 /* No errors */
+#define MOUNT_EX_USAGE 1 /* incorrect invocation or permission */
+#define MOUNT_EX_SYSERR 2 /* out of memory, cannot fork, ... */
+#define MOUNT_EX_SOFTWARE 4 /* internal mount bug or wrong version */
+#define MOUNT_EX_USER 8 /* user interrupt */
+#define MOUNT_EX_FILEIO 16 /* problems writing, locking, ... mtab/fstab */
+#define MOUNT_EX_FAIL 32 /* mount failure */
+#define MOUNT_EX_SOMEOK 64 /* some mount succeeded */
+
+#endif /* UTIL_LINUX_EXITCODES_H */
diff --git a/libblkid/include/fileutils.h b/libblkid/include/fileutils.h
new file mode 100644
index 000000000..b4e0a4adb
--- /dev/null
+++ b/libblkid/include/fileutils.h
@@ -0,0 +1,33 @@
+#ifndef UTIL_LINUX_FILEUTILS
+#define UTIL_LINUX_FILEUTILS
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "c.h"
+
+extern int xmkstemp(char **tmpname, char *dir);
+
+static inline FILE *xfmkstemp(char **tmpname, char *dir)
+{
+ int fd;
+ FILE *ret;
+
+ fd = xmkstemp(tmpname, dir);
+ if (fd == -1)
+ return NULL;
+
+ if (!(ret = fdopen(fd, "w+"))) {
+ close(fd);
+ return NULL;
+ }
+ return ret;
+}
+
+extern int get_fd_tabsize(void);
+
+extern int mkdir_p(const char *path, mode_t mode);
+extern char *stripoff_last_component(char *path);
+
+#endif /* UTIL_LINUX_FILEUTILS */
diff --git a/libblkid/include/ismounted.h b/libblkid/include/ismounted.h
new file mode 100644
index 000000000..57918cb3a
--- /dev/null
+++ b/libblkid/include/ismounted.h
@@ -0,0 +1,14 @@
+#ifndef IS_MOUNTED_H
+#define IS_MOUNTED_H
+
+#define MF_MOUNTED 1
+#define MF_ISROOT 2
+#define MF_READONLY 4
+#define MF_SWAP 8
+#define MF_BUSY 16
+
+extern int is_mounted(const char *file);
+extern int check_mount_point(const char *device, int *mount_flags,
+ char *mtpt, int mtlen);
+
+#endif /* IS_MOUNTED_H */
diff --git a/libblkid/include/linux_reboot.h b/libblkid/include/linux_reboot.h
new file mode 100644
index 000000000..9cebc67e8
--- /dev/null
+++ b/libblkid/include/linux_reboot.h
@@ -0,0 +1,72 @@
+#ifndef _LINUX_REBOOT_H
+#define _LINUX_REBOOT_H
+
+/*
+ * Magic values required to use _reboot() system call.
+ */
+
+#define LINUX_REBOOT_MAGIC1 0xfee1dead
+#define LINUX_REBOOT_MAGIC2 672274793
+#define LINUX_REBOOT_MAGIC2A 85072278
+#define LINUX_REBOOT_MAGIC2B 369367448
+
+
+/*
+ * Commands accepted by the _reboot() system call.
+ *
+ * RESTART Restart system using default command and mode.
+ * HALT Stop OS and give system control to ROM monitor, if any.
+ * CAD_ON Ctrl-Alt-Del sequence causes RESTART command.
+ * CAD_OFF Ctrl-Alt-Del sequence sends SIGINT to init task.
+ * POWER_OFF Stop OS and remove all power from system, if possible.
+ * RESTART2 Restart system using given command string.
+ */
+
+#define LINUX_REBOOT_CMD_RESTART 0x01234567
+#define LINUX_REBOOT_CMD_HALT 0xCDEF0123
+#define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF
+#define LINUX_REBOOT_CMD_CAD_OFF 0x00000000
+#define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC
+#define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4
+
+/* Including <unistd.h> makes sure that on a glibc system
+ <features.h> is included, which again defines __GLIBC__ */
+#include <unistd.h>
+#include "linux_reboot.h"
+
+#define USE_LIBC
+
+#ifdef USE_LIBC
+
+/* libc version */
+#if defined __GLIBC__ && __GLIBC__ >= 2
+# include <sys/reboot.h>
+# define REBOOT(cmd) reboot(cmd)
+#else
+extern int reboot(int, int, int);
+# define REBOOT(cmd) reboot(LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,(cmd))
+#endif
+static inline int my_reboot(int cmd) {
+ return REBOOT(cmd);
+}
+
+#else /* no USE_LIBC */
+
+/* direct syscall version */
+#include <linux/unistd.h>
+
+#ifdef _syscall3
+_syscall3(int, reboot, int, magic, int, magic_too, int, cmd);
+#else
+/* Let us hope we have a 3-argument reboot here */
+extern int reboot(int, int, int);
+#endif
+
+static inline int my_reboot(int cmd) {
+ return reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd);
+}
+
+#endif
+
+
+#endif /* _LINUX_REBOOT_H */
diff --git a/libblkid/include/linux_version.h b/libblkid/include/linux_version.h
new file mode 100644
index 000000000..a6a1e99c7
--- /dev/null
+++ b/libblkid/include/linux_version.h
@@ -0,0 +1,14 @@
+#ifndef LINUX_VERSION_H
+#define LINUX_VERSION_H
+
+#ifdef HAVE_LINUX_VERSION_H
+# include <linux/version.h>
+#endif
+
+#ifndef KERNEL_VERSION
+# define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+int get_linux_version(void);
+
+#endif /* LINUX_VERSION_H */
diff --git a/libblkid/include/list.h b/libblkid/include/list.h
new file mode 100644
index 000000000..3c08aa5f2
--- /dev/null
+++ b/libblkid/include/list.h
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 1999-2008 by Theodore Ts'o
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * (based on list.h from e2fsprogs)
+ * Merge sort based on kernel's implementation.
+ */
+
+#ifndef UTIL_LINUX_LIST_H
+#define UTIL_LINUX_LIST_H
+
+/* TODO: use AC_C_INLINE */
+#ifdef __GNUC__
+#define _INLINE_ static __inline__
+#else /* For Watcom C */
+#define _INLINE_ static inline
+#endif
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+_INLINE_ void __list_add(struct list_head * add,
+ struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = add;
+ add->next = next;
+ add->prev = prev;
+ prev->next = add;
+}
+
+/**
+ * list_add - add a new entry
+ * @add: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+_INLINE_ void list_add(struct list_head *add, struct list_head *head)
+{
+ __list_add(add, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @add: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+_INLINE_ void list_add_tail(struct list_head *add, struct list_head *head)
+{
+ __list_add(add, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+_INLINE_ void __list_del(struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ *
+ * list_empty() on @entry does not return true after this, @entry is
+ * in an undefined state.
+ */
+_INLINE_ void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+_INLINE_ void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+_INLINE_ int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_entry_is_last - tests whether is entry last in the list
+ * @entry: the entry to test.
+ * @head: the list to test.
+ */
+_INLINE_ int list_entry_is_last(struct list_head *entry, struct list_head *head)
+{
+ return head->prev == entry;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+_INLINE_ void list_splice(struct list_head *list, struct list_head *head)
+{
+ struct list_head *first = list->next;
+
+ if (first != list) {
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) __extension__ ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+
+#define list_first_entry(head, type, member) \
+ ((head) && (head)->next != (head) ? list_entry((head)->next, type, member) : NULL)
+
+#define list_last_entry(head, type, member) \
+ ((head) && (head)->prev != (head) ? list_entry((head)->prev, type, member) : NULL)
+
+/**
+ * list_for_each - iterate over elements in a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_backwardly - iterate over elements in a list in reverse
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each_backwardly(pos, head) \
+ for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over elements in a list, but don't dereference
+ * pos after the body is done (in case it is freed)
+ * @pos: the &struct list_head to use as a loop counter.
+ * @pnext: the &struct list_head to use as a pointer to the next item.
+ * @head: the head for your list (not included in iteration).
+ */
+#define list_for_each_safe(pos, pnext, head) \
+ for (pos = (head)->next, pnext = pos->next; pos != (head); \
+ pos = pnext, pnext = pos->next)
+
+#define MAX_LIST_LENGTH_BITS 20
+
+/*
+ * Returns a list organized in an intermediate format suited
+ * to chaining of merge() calls: null-terminated, no reserved or
+ * sentinel head node, "prev" links not maintained.
+ */
+_INLINE_ struct list_head *merge(int (*cmp)(struct list_head *a,
+ struct list_head *b,
+ void *data),
+ void *data,
+ struct list_head *a, struct list_head *b)
+{
+ struct list_head head, *tail = &head;
+
+ while (a && b) {
+ /* if equal, take 'a' -- important for sort stability */
+ if ((*cmp)(a, b, data) <= 0) {
+ tail->next = a;
+ a = a->next;
+ } else {
+ tail->next = b;
+ b = b->next;
+ }
+ tail = tail->next;
+ }
+ tail->next = a ? a : b;
+ return head.next;
+}
+
+/*
+ * Combine final list merge with restoration of standard doubly-linked
+ * list structure. This approach duplicates code from merge(), but
+ * runs faster than the tidier alternatives of either a separate final
+ * prev-link restoration pass, or maintaining the prev links
+ * throughout.
+ */
+_INLINE_ void merge_and_restore_back_links(int (*cmp)(struct list_head *a,
+ struct list_head *b,
+ void *data),
+ void *data,
+ struct list_head *head,
+ struct list_head *a, struct list_head *b)
+{
+ struct list_head *tail = head;
+
+ while (a && b) {
+ /* if equal, take 'a' -- important for sort stability */
+ if ((*cmp)(a, b, data) <= 0) {
+ tail->next = a;
+ a->prev = tail;
+ a = a->next;
+ } else {
+ tail->next = b;
+ b->prev = tail;
+ b = b->next;
+ }
+ tail = tail->next;
+ }
+ tail->next = a ? a : b;
+
+ do {
+ /*
+ * In worst cases this loop may run many iterations.
+ * Continue callbacks to the client even though no
+ * element comparison is needed, so the client's cmp()
+ * routine can invoke cond_resched() periodically.
+ */
+ (*cmp)(tail->next, tail->next, data);
+
+ tail->next->prev = tail;
+ tail = tail->next;
+ } while (tail->next);
+
+ tail->next = head;
+ head->prev = tail;
+}
+
+
+/**
+ * list_sort - sort a list
+ * @head: the list to sort
+ * @cmp: the elements comparison function
+ *
+ * This function implements "merge sort", which has O(nlog(n))
+ * complexity.
+ *
+ * The comparison function @cmp must return a negative value if @a
+ * should sort before @b, and a positive value if @a should sort after
+ * @b. If @a and @b are equivalent, and their original relative
+ * ordering is to be preserved, @cmp must return 0.
+ */
+_INLINE_ void list_sort(struct list_head *head,
+ int (*cmp)(struct list_head *a,
+ struct list_head *b,
+ void *data),
+ void *data)
+{
+ struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
+ -- last slot is a sentinel */
+ size_t lev; /* index into part[] */
+ size_t max_lev = 0;
+ struct list_head *list;
+
+ if (list_empty(head))
+ return;
+
+ memset(part, 0, sizeof(part));
+
+ head->prev->next = NULL;
+ list = head->next;
+
+ while (list) {
+ struct list_head *cur = list;
+ list = list->next;
+ cur->next = NULL;
+
+ for (lev = 0; part[lev]; lev++) {
+ cur = merge(cmp, data, part[lev], cur);
+ part[lev] = NULL;
+ }
+ if (lev > max_lev) {
+ /* list passed to list_sort() too long for efficiency */
+ if (lev >= ARRAY_SIZE(part) - 1)
+ lev--;
+ max_lev = lev;
+ }
+ part[lev] = cur;
+ }
+
+ for (lev = 0; lev < max_lev; lev++)
+ if (part[lev])
+ list = merge(cmp, data, part[lev], list);
+
+ merge_and_restore_back_links(cmp, data, head, part[max_lev], list);
+}
+
+#undef _INLINE_
+
+#endif /* UTIL_LINUX_LIST_H */
diff --git a/libblkid/include/loopdev.h b/libblkid/include/loopdev.h
new file mode 100644
index 000000000..573a5699d
--- /dev/null
+++ b/libblkid/include/loopdev.h
@@ -0,0 +1,193 @@
+#ifndef UTIL_LINUX_LOOPDEV_H
+#define UTIL_LINUX_LOOPDEV_H
+
+#include "sysfs.h"
+
+/*
+ * loop_info.lo_encrypt_type
+ */
+#define LO_CRYPT_NONE 0
+#define LO_CRYPT_XOR 1
+#define LO_CRYPT_DES 2
+#define LO_CRYPT_CRYPTOAPI 18
+
+#define LOOP_SET_FD 0x4C00
+#define LOOP_CLR_FD 0x4C01
+/*
+ * Obsolete (kernel < 2.6)
+ *
+ * #define LOOP_SET_STATUS 0x4C02
+ * #define LOOP_GET_STATUS 0x4C03
+ */
+#define LOOP_SET_STATUS64 0x4C04
+#define LOOP_GET_STATUS64 0x4C05
+/* #define LOOP_CHANGE_FD 0x4C06 */
+#define LOOP_SET_CAPACITY 0x4C07
+
+/* /dev/loop-control interface */
+#ifndef LOOP_CTL_ADD
+# define LOOP_CTL_ADD 0x4C80
+# define LOOP_CTL_REMOVE 0x4C81
+# define LOOP_CTL_GET_FREE 0x4C82
+#endif
+
+/*
+ * loop_info.lo_flags
+ */
+enum {
+ LO_FLAGS_READ_ONLY = 1,
+ LO_FLAGS_USE_AOPS = 2,
+ LO_FLAGS_AUTOCLEAR = 4, /* kernel >= 2.6.25 */
+ LO_FLAGS_PARTSCAN = 8, /* kernel >= 3.2 */
+};
+
+#define LO_NAME_SIZE 64
+#define LO_KEY_SIZE 32
+
+/*
+ * Linux LOOP_{SET,GET}_STATUS64 ioctl struct
+ */
+struct loop_info64 {
+ uint64_t lo_device;
+ uint64_t lo_inode;
+ uint64_t lo_rdevice;
+ uint64_t lo_offset;
+ uint64_t lo_sizelimit; /* bytes, 0 == max available */
+ uint32_t lo_number;
+ uint32_t lo_encrypt_type;
+ uint32_t lo_encrypt_key_size;
+ uint32_t lo_flags;
+ uint8_t lo_file_name[LO_NAME_SIZE];
+ uint8_t lo_crypt_name[LO_NAME_SIZE];
+ uint8_t lo_encrypt_key[LO_KEY_SIZE];
+ uint64_t lo_init[2];
+};
+
+#define LOOPDEV_MAJOR 7 /* loop major number */
+#define LOOPDEV_DEFAULT_NNODES 8 /* default number of loop devices */
+
+struct loopdev_iter {
+ FILE *proc; /* /proc/partitions */
+ DIR *sysblock; /* /sys/block */
+ int ncur; /* current position */
+ int *minors; /* ary of minor numbers (when scan whole /dev) */
+ int nminors; /* number of items in *minors */
+ int ct_perm; /* count permission problems */
+ int ct_succ; /* count number of detected devices */
+
+ unsigned int done:1; /* scanning done */
+ unsigned int default_check:1;/* check first LOOPDEV_NLOOPS */
+ int flags; /* LOOPITER_FL_* flags */
+};
+
+enum {
+ LOOPITER_FL_FREE = (1 << 0),
+ LOOPITER_FL_USED = (1 << 1)
+};
+
+/*
+ * handler for work with loop devices
+ */
+struct loopdev_cxt {
+ char device[128]; /* device path (e.g. /dev/loop<N>) */
+ char *filename; /* backing file for loopcxt_set_... */
+ int fd; /* open(/dev/looo<N>) */
+ int mode; /* fd mode O_{RDONLY,RDWR} */
+
+ int flags; /* LOOPDEV_FL_* flags */
+ unsigned int has_info:1; /* .info contains data */
+ unsigned int extra_check:1; /* unusual stuff for iterator */
+ unsigned int info_failed:1; /* LOOP_GET_STATUS ioctl failed */
+ unsigned int control_ok:1; /* /dev/loop-control success */
+
+ struct sysfs_cxt sysfs; /* pointer to /sys/dev/block/<maj:min>/ */
+ struct loop_info64 info; /* for GET/SET ioctl */
+ struct loopdev_iter iter; /* scans /sys or /dev for used/free devices */
+};
+
+#define UL_LOOPDEVCXT_EMPTY { .fd = -1, .sysfs = UL_SYSFSCXT_EMPTY }
+
+/*
+ * loopdev_cxt.flags
+ */
+enum {
+ LOOPDEV_FL_RDONLY = (1 << 0), /* open(/dev/loop) mode; default */
+ LOOPDEV_FL_RDWR = (1 << 1), /* necessary for loop setup only */
+ LOOPDEV_FL_OFFSET = (1 << 4),
+ LOOPDEV_FL_NOSYSFS = (1 << 5),
+ LOOPDEV_FL_NOIOCTL = (1 << 6),
+ LOOPDEV_FL_DEVSUBDIR = (1 << 7),
+ LOOPDEV_FL_CONTROL = (1 << 8), /* system with /dev/loop-control */
+ LOOPDEV_FL_SIZELIMIT = (1 << 9)
+};
+
+/*
+ * High-level
+ */
+extern int loopmod_supports_partscan(void);
+
+extern int is_loopdev(const char *device);
+extern int loopdev_is_autoclear(const char *device);
+
+extern char *loopdev_get_backing_file(const char *device);
+extern int loopdev_is_used(const char *device, const char *filename,
+ uint64_t offset, int flags);
+extern char *loopdev_find_by_backing_file(const char *filename,
+ uint64_t offset, int flags);
+extern int loopcxt_find_unused(struct loopdev_cxt *lc);
+extern int loopdev_delete(const char *device);
+extern int loopdev_count_by_backing_file(const char *filename, char **loopdev);
+
+/*
+ * Low-level
+ */
+extern int loopcxt_init(struct loopdev_cxt *lc, int flags)
+ __attribute__ ((warn_unused_result));
+extern void loopcxt_deinit(struct loopdev_cxt *lc);
+
+extern int loopcxt_set_device(struct loopdev_cxt *lc, const char *device)
+ __attribute__ ((warn_unused_result));
+extern int loopcxt_has_device(struct loopdev_cxt *lc);
+extern int loopcxt_add_device(struct loopdev_cxt *lc);
+extern char *loopcxt_strdup_device(struct loopdev_cxt *lc);
+extern const char *loopcxt_get_device(struct loopdev_cxt *lc);
+extern struct sysfs_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc);
+extern struct loop_info64 *loopcxt_get_info(struct loopdev_cxt *lc);
+
+extern int loopcxt_get_fd(struct loopdev_cxt *lc);
+extern int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, int mode);
+
+extern int loopcxt_init_iterator(struct loopdev_cxt *lc, int flags);
+extern int loopcxt_deinit_iterator(struct loopdev_cxt *lc);
+extern int loopcxt_next(struct loopdev_cxt *lc);
+
+extern int loopcxt_setup_device(struct loopdev_cxt *lc);
+extern int loopcxt_delete_device(struct loopdev_cxt *lc);
+extern int loopcxt_set_capacity(struct loopdev_cxt *lc);
+
+int loopcxt_set_offset(struct loopdev_cxt *lc, uint64_t offset);
+int loopcxt_set_sizelimit(struct loopdev_cxt *lc, uint64_t sizelimit);
+int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags);
+int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename);
+
+extern char *loopcxt_get_backing_file(struct loopdev_cxt *lc);
+extern int loopcxt_get_backing_devno(struct loopdev_cxt *lc, dev_t *devno);
+extern int loopcxt_get_backing_inode(struct loopdev_cxt *lc, ino_t *ino);
+extern int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset);
+extern int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size);
+extern int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type);
+extern const char *loopcxt_get_crypt_name(struct loopdev_cxt *lc);
+extern int loopcxt_is_autoclear(struct loopdev_cxt *lc);
+extern int loopcxt_is_readonly(struct loopdev_cxt *lc);
+extern int loopcxt_is_partscan(struct loopdev_cxt *lc);
+extern int loopcxt_find_by_backing_file(struct loopdev_cxt *lc,
+ const char *filename,
+ uint64_t offset, int flags);
+
+extern int loopcxt_is_used(struct loopdev_cxt *lc,
+ struct stat *st,
+ const char *backing_file,
+ uint64_t offset,
+ int flags);
+
+#endif /* UTIL_LINUX_LOOPDEV_H */
diff --git a/libblkid/include/mangle.h b/libblkid/include/mangle.h
new file mode 100644
index 000000000..ec492b556
--- /dev/null
+++ b/libblkid/include/mangle.h
@@ -0,0 +1,26 @@
+#ifndef UTIL_LINUX_MANGLE_H
+#define UTIL_LINUX_MANGLE_H
+
+/*
+ * Functions for \oct encoding used in mtab/fstab/swaps/etc.
+ */
+
+extern char *mangle(const char *s);
+
+extern void unmangle_to_buffer(const char *s, char *buf, size_t len);
+void unhexmangle_to_buffer(const char *s, char *buf, size_t len);
+
+extern char *unmangle(const char *s, char **end);
+
+static inline void unmangle_string(char *s)
+{
+ unmangle_to_buffer(s, s, strlen(s) + 1);
+}
+
+static inline void unhexmangle_string(char *s)
+{
+ unhexmangle_to_buffer(s, s, strlen(s) + 1);
+}
+
+#endif /* UTIL_LINUX_MANGLE_H */
+
diff --git a/libblkid/include/match.h b/libblkid/include/match.h
new file mode 100644
index 000000000..94440c22e
--- /dev/null
+++ b/libblkid/include/match.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_MATCH_H
+#define UTIL_LINUX_MATCH_H
+
+extern int match_fstype(const char *type, const char *pattern);
+
+#endif /* UTIL_LINUX_MATCH_H */
diff --git a/libblkid/include/mbsalign.h b/libblkid/include/mbsalign.h
new file mode 100644
index 000000000..5eaf606e5
--- /dev/null
+++ b/libblkid/include/mbsalign.h
@@ -0,0 +1,56 @@
+/* Align/Truncate a string in a given screen width
+ Copyright (C) 2009-2010 Free Software Foundation, Inc.
+ Copyright (C) 2010-2013 Karel Zak <kzak@redhat.com>
+
+ This program 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 program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+#ifndef UTIL_LINUX_MBSALIGN_H
+# define UTIL_LINUX_MBSALIGN_H
+# include <stddef.h>
+
+typedef enum { MBS_ALIGN_LEFT, MBS_ALIGN_RIGHT, MBS_ALIGN_CENTER } mbs_align_t;
+
+enum {
+ /* Use unibyte mode for invalid multibyte strings or
+ or when heap memory is exhausted. */
+ MBA_UNIBYTE_FALLBACK = 0x0001,
+
+#if 0 /* Other possible options. */
+ /* Skip invalid multibyte chars rather than failing */
+ MBA_IGNORE_INVALID = 0x0002,
+
+ /* Align multibyte strings using "figure space" (\u2007) */
+ MBA_USE_FIGURE_SPACE = 0x0004,
+
+ /* Don't add any padding */
+ MBA_TRUNCATE_ONLY = 0x0008,
+
+ /* Don't truncate */
+ MBA_PAD_ONLY = 0x0010,
+#endif
+};
+
+extern size_t mbs_truncate(char *str, size_t *width);
+
+extern size_t mbsalign (const char *src, char *dest,
+ size_t dest_size, size_t *width,
+ mbs_align_t align, int flags);
+
+extern size_t mbs_safe_nwidth(const char *buf, size_t bufsz, size_t *sz);
+extern size_t mbs_safe_width(const char *s);
+
+extern char *mbs_safe_encode(const char *s, size_t *width);
+extern char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf);
+extern size_t mbs_safe_encode_size(size_t bytes);
+
+#endif /* UTIL_LINUX_MBSALIGN_H */
diff --git a/libblkid/include/md5.h b/libblkid/include/md5.h
new file mode 100644
index 000000000..d997e379d
--- /dev/null
+++ b/libblkid/include/md5.h
@@ -0,0 +1,29 @@
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+#endif
+
+#define MD5LENGTH 16
+
+struct MD5Context {
+ uint32_t buf[4];
+ uint32_t bits[2];
+ unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+ unsigned len);
+void MD5Final(unsigned char digest[MD5LENGTH], struct MD5Context *context);
+void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+typedef struct MD5Context MD5_CTX;
+
+#endif /* !MD5_H */
diff --git a/libblkid/include/minix.h b/libblkid/include/minix.h
new file mode 100644
index 000000000..f28991ce9
--- /dev/null
+++ b/libblkid/include/minix.h
@@ -0,0 +1,85 @@
+#ifndef UTIL_LINUX_MINIX_H
+#define UTIL_LINUX_MINIX_H
+
+#include <stdint.h>
+
+struct minix_inode {
+ uint16_t i_mode;
+ uint16_t i_uid;
+ uint32_t i_size;
+ uint32_t i_time;
+ uint8_t i_gid;
+ uint8_t i_nlinks;
+ uint16_t i_zone[9];
+};
+
+struct minix2_inode {
+ uint16_t i_mode;
+ uint16_t i_nlinks;
+ uint16_t i_uid;
+ uint16_t i_gid;
+ uint32_t i_size;
+ uint32_t i_atime;
+ uint32_t i_mtime;
+ uint32_t i_ctime;
+ uint32_t i_zone[10];
+};
+
+struct minix_super_block {
+ uint16_t s_ninodes;
+ uint16_t s_nzones;
+ uint16_t s_imap_blocks;
+ uint16_t s_zmap_blocks;
+ uint16_t s_firstdatazone;
+ uint16_t s_log_zone_size;
+ uint32_t s_max_size;
+ uint16_t s_magic;
+ uint16_t s_state;
+ uint32_t s_zones;
+};
+
+/* V3 minix super-block data on disk */
+struct minix3_super_block {
+ uint32_t s_ninodes;
+ uint16_t s_pad0;
+ uint16_t s_imap_blocks;
+ uint16_t s_zmap_blocks;
+ uint16_t s_firstdatazone;
+ uint16_t s_log_zone_size;
+ uint16_t s_pad1;
+ uint32_t s_max_size;
+ uint32_t s_zones;
+ uint16_t s_magic;
+ uint16_t s_pad2;
+ uint16_t s_blocksize;
+ uint8_t s_disk_version;
+};
+
+/*
+ * Minix subpartitions are always within primary dos partition.
+ */
+#define MINIX_MAXPARTITIONS 4
+
+#define MINIX_BLOCK_SIZE_BITS 10
+#define MINIX_BLOCK_SIZE (1 << MINIX_BLOCK_SIZE_BITS)
+
+#define MINIX_NAME_MAX 255 /* # chars in a file name */
+#define MINIX_MAX_INODES 65535
+
+#define MINIX_INODES_PER_BLOCK ((MINIX_BLOCK_SIZE)/(sizeof (struct minix_inode)))
+#define MINIX2_INODES_PER_BLOCK ((MINIX_BLOCK_SIZE)/(sizeof (struct minix2_inode)))
+
+/* minix_super_block.s_state */
+#define MINIX_VALID_FS 0x0001 /* Clean fs. */
+#define MINIX_ERROR_FS 0x0002 /* fs has errors. */
+
+
+#define MINIX_SUPER_MAGIC 0x137F /* minix V1 fs, 14 char names */
+#define MINIX_SUPER_MAGIC2 0x138F /* minix V1 fs, 30 char names */
+
+#define MINIX2_SUPER_MAGIC 0x2468 /* minix V2 fs, 14 char names */
+#define MINIX2_SUPER_MAGIC2 0x2478 /* minix V2 fs, 30 char names */
+
+#define MINIX3_SUPER_MAGIC 0x4d5a /* minix V3 fs (60 char names) */
+
+#endif /* UTIL_LINUX_MINIX_H */
diff --git a/libblkid/include/monotonic.h b/libblkid/include/monotonic.h
new file mode 100644
index 000000000..f3b03d3d0
--- /dev/null
+++ b/libblkid/include/monotonic.h
@@ -0,0 +1,11 @@
+#ifndef UTIL_LINUX_BOOTTIME_H
+#define UTIL_LINUX_BOOTTIME_H
+
+/*
+ * Uses clock_gettime() that requires $CLOCKGETTIME_LIBS
+ */
+extern int get_boot_time(struct timeval *boot_time);
+
+extern int gettime_monotonic(struct timeval *tv);
+
+#endif /* UTIL_LINUX_BOOTTIME_H */
diff --git a/libblkid/include/namespace.h b/libblkid/include/namespace.h
new file mode 100644
index 000000000..ea231cacb
--- /dev/null
+++ b/libblkid/include/namespace.h
@@ -0,0 +1,44 @@
+/* Compat code so unshare and setns can be used with older libcs */
+#ifndef UTIL_LINUX_NAMESPACE_H
+# define UTIL_LINUX_NAMESPACE_H
+
+# include <sched.h>
+
+# ifndef CLONE_NEWNS
+# define CLONE_NEWNS 0x00020000
+# endif
+# ifndef CLONE_NEWUTS
+# define CLONE_NEWUTS 0x04000000
+# endif
+# ifndef CLONE_NEWIPC
+# define CLONE_NEWIPC 0x08000000
+# endif
+# ifndef CLONE_NEWNET
+# define CLONE_NEWNET 0x40000000
+# endif
+# ifndef CLONE_NEWUSER
+# define CLONE_NEWUSER 0x10000000
+# endif
+# ifndef CLONE_NEWPID
+# define CLONE_NEWPID 0x20000000
+# endif
+
+# if !defined(HAVE_UNSHARE) || !defined(HAVE_SETNS)
+# include <sys/syscall.h>
+# endif
+
+# if !defined(HAVE_UNSHARE) && defined(SYS_unshare)
+static inline int unshare(int flags)
+{
+ return syscall(SYS_unshare, flags);
+}
+# endif
+
+# if !defined(HAVE_SETNS) && defined(SYS_setns)
+static inline int setns(int fd, int nstype)
+{
+ return syscall(SYS_setns, fd, nstype);
+}
+# endif
+
+#endif /* UTIL_LINUX_NAMESPACE_H */
diff --git a/libblkid/include/nls.h b/libblkid/include/nls.h
new file mode 100644
index 000000000..3eabfe63b
--- /dev/null
+++ b/libblkid/include/nls.h
@@ -0,0 +1,115 @@
+#ifndef UTIL_LINUX_NLS_H
+#define UTIL_LINUX_NLS_H
+
+int main(int argc, char *argv[]);
+
+#ifndef LOCALEDIR
+#define LOCALEDIR "/usr/share/locale"
+#endif
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#else
+# undef setlocale
+# define setlocale(Category, Locale) /* empty */
+struct lconv
+{
+ char *decimal_point;
+};
+# undef localeconv
+# define localeconv() NULL
+#endif
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# define _(Text) gettext (Text)
+# ifdef gettext_noop
+# define N_(String) gettext_noop (String)
+# else
+# define N_(String) (String)
+# endif
+# define P_(Singular, Plural, n) ngettext (Singular, Plural, n)
+#else
+# undef bindtextdomain
+# define bindtextdomain(Domain, Directory) /* empty */
+# undef textdomain
+# define textdomain(Domain) /* empty */
+# define _(Text) (Text)
+# define N_(Text) (Text)
+# define P_(Singular, Plural, n) ((n) == 1 ? (Singular) : (Plural))
+#endif
+
+#ifdef HAVE_LANGINFO_H
+# include <langinfo.h>
+#else
+
+typedef int nl_item;
+extern char *langinfo_fallback(nl_item item);
+
+# define nl_langinfo langinfo_fallback
+
+enum {
+ CODESET = 1,
+ RADIXCHAR,
+ THOUSEP,
+ D_T_FMT,
+ D_FMT,
+ T_FMT,
+ T_FMT_AMPM,
+ AM_STR,
+ PM_STR,
+
+ DAY_1,
+ DAY_2,
+ DAY_3,
+ DAY_4,
+ DAY_5,
+ DAY_6,
+ DAY_7,
+
+ ABDAY_1,
+ ABDAY_2,
+ ABDAY_3,
+ ABDAY_4,
+ ABDAY_5,
+ ABDAY_6,
+ ABDAY_7,
+
+ MON_1,
+ MON_2,
+ MON_3,
+ MON_4,
+ MON_5,
+ MON_6,
+ MON_7,
+ MON_8,
+ MON_9,
+ MON_10,
+ MON_11,
+ MON_12,
+
+ ABMON_1,
+ ABMON_2,
+ ABMON_3,
+ ABMON_4,
+ ABMON_5,
+ ABMON_6,
+ ABMON_7,
+ ABMON_8,
+ ABMON_9,
+ ABMON_10,
+ ABMON_11,
+ ABMON_12,
+
+ ERA_D_FMT,
+ ERA_D_T_FMT,
+ ERA_T_FMT,
+ ALT_DIGITS,
+ CRNCYSTR,
+ YESEXPR,
+ NOEXPR
+};
+
+#endif /* !HAVE_LANGINFO_H */
+
+#endif /* UTIL_LINUX_NLS_H */
diff --git a/libblkid/include/optutils.h b/libblkid/include/optutils.h
new file mode 100644
index 000000000..99ad7e4b4
--- /dev/null
+++ b/libblkid/include/optutils.h
@@ -0,0 +1,103 @@
+#ifndef UTIL_LINUX_OPTUTILS_H
+#define UTIL_LINUX_OPTUTILS_H
+
+#include "c.h"
+#include "nls.h"
+
+static inline const char *option_to_longopt(int c, const struct option *opts)
+{
+ const struct option *o;
+
+ for (o = opts; o->name; o++)
+ if (o->val == c)
+ return o->name;
+ return NULL;
+}
+
+#ifndef OPTUTILS_EXIT_CODE
+# define OPTUTILS_EXIT_CODE EXIT_FAILURE
+#endif
+
+/*
+ * Check collisions between options.
+ *
+ * The conflicts between options are described in ul_excl_t array. The
+ * array contains groups of mutually exclusive options. For example
+ *
+ * static const ul_excl_t excl[] = {
+ * { 'Z','b','c' }, // first group
+ * { 'b','x' }, // second group
+ * { 0 }
+ * };
+ *
+ * int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
+ *
+ * while ((c = getopt_long(argc, argv, "Zbcx", longopts, NULL)) != -1) {
+ *
+ * err_exclusive_options(c, longopts, excl, excl_st);
+ *
+ * switch (c) {
+ * case 'Z':
+ * ....
+ * }
+ * }
+ *
+ * The array excl[] defines two groups of the mutually exclusive options. The
+ * option '-b' is in the both groups.
+ *
+ * Note that the options in the group have to be in ASCII order (ABC..abc..) and
+ * groups have to be also in ASCII order.
+ *
+ * The maximal number of the options in the group is 15 (size of the array is
+ * 16, last is zero).
+ *
+ * The current status of options is stored in excl_st array. The size of the array
+ * must be the same as number of the groups in the ul_excl_t array.
+ *
+ * If you're unsure then see sys-utils/mount.c or misc-utils/findmnt.c.
+ */
+#define UL_EXCL_STATUS_INIT { 0 }
+typedef int ul_excl_t[16];
+
+static inline void err_exclusive_options(
+ int c,
+ const struct option *opts,
+ const ul_excl_t *excl,
+ int *status)
+{
+ int e;
+
+ for (e = 0; excl[e][0] && excl[e][0] <= c; e++) {
+ const int *op = excl[e];
+
+ for (; *op && *op <= c; op++) {
+ if (*op != c)
+ continue;
+ if (status[e] == 0)
+ status[e] = c;
+ else if (status[e] != c) {
+ size_t ct = 0;
+
+ fprintf(stderr, _("%s: these options are "
+ "mutually exclusive:"),
+ program_invocation_short_name);
+
+ for (op = excl[e];
+ ct + 1 < ARRAY_SIZE(excl[0]) && *op;
+ op++, ct++) {
+ const char *n = option_to_longopt(*op, opts);
+ if (n)
+ fprintf(stderr, " --%s", n);
+ else
+ fprintf(stderr, " -%c", *op);
+ }
+ fputc('\n', stderr);
+ exit(OPTUTILS_EXIT_CODE);
+ }
+ break;
+ }
+ }
+}
+
+#endif
+
diff --git a/libblkid/include/pager.h b/libblkid/include/pager.h
new file mode 100644
index 000000000..9ca42eb35
--- /dev/null
+++ b/libblkid/include/pager.h
@@ -0,0 +1,6 @@
+#ifndef UTIL_LINUX_PAGER
+#define UTIL_LINUX_PAGER
+
+void setup_pager(void);
+
+#endif
diff --git a/libblkid/include/pamfail.h b/libblkid/include/pamfail.h
new file mode 100644
index 000000000..bb83b9404
--- /dev/null
+++ b/libblkid/include/pamfail.h
@@ -0,0 +1,26 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#ifndef UTIL_LINUX_PAMFAIL_H
+#include <security/pam_appl.h>
+#ifdef HAVE_SECURITY_PAM_MISC_H
+# include <security/pam_misc.h>
+#elif defined(HAVE_SECURITY_OPENPAM_H)
+# include <security/openpam.h>
+#endif
+#include "c.h"
+
+static inline int
+pam_fail_check(pam_handle_t *pamh, int retcode)
+{
+ if (retcode == PAM_SUCCESS)
+ return 0;
+ warnx("%s", pam_strerror(pamh, retcode));
+ pam_end(pamh, retcode);
+ return 1;
+}
+
+#endif /* UTIL_LINUX_PAMFAIL_H */
diff --git a/libblkid/include/path.h b/libblkid/include/path.h
new file mode 100644
index 000000000..45da692f8
--- /dev/null
+++ b/libblkid/include/path.h
@@ -0,0 +1,33 @@
+#ifndef UTIL_LINUX_PATH_H
+#define UTIL_LINUX_PATH_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+extern char *path_strdup(const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 1, 2)));
+extern FILE *path_fopen(const char *mode, int exit_on_err, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+extern void path_read_str(char *result, size_t len, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+extern int path_write_str(const char *str, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)));
+extern int path_read_s32(const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 1, 2)));
+extern uint64_t path_read_u64(const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 1, 2)));
+
+extern int path_exist(const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 1, 2)));
+
+#ifdef HAVE_CPU_SET_T
+# include "cpuset.h"
+
+extern cpu_set_t *path_read_cpuset(int, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)));
+extern cpu_set_t *path_read_cpulist(int, const char *path, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)));
+extern void path_set_prefix(const char *);
+#endif /* HAVE_CPU_SET_T */
+
+#endif /* UTIL_LINUX_PATH_H */
diff --git a/libblkid/include/pathnames.h b/libblkid/include/pathnames.h
new file mode 100644
index 000000000..0d21b980b
--- /dev/null
+++ b/libblkid/include/pathnames.h
@@ -0,0 +1,196 @@
+/*
+ * Vaguely based on
+ * @(#)pathnames.h 5.3 (Berkeley) 5/9/89
+ * This code is in the public domain.
+ */
+#ifndef PATHNAMES_H
+#define PATHNAMES_H
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifndef __STDC__
+# error "we need an ANSI compiler"
+#endif
+
+/* used by kernel in /proc (e.g. /proc/swaps) for deleted files */
+#define PATH_DELETED_SUFFIX "\\040(deleted)"
+#define PATH_DELETED_SUFFIX_SZ (sizeof(PATH_DELETED_SUFFIX) - 1)
+
+/* DEFPATHs from <paths.h> don't include /usr/local */
+#undef _PATH_DEFPATH
+#define _PATH_DEFPATH "/usr/local/bin:/bin:/usr/bin"
+
+#undef _PATH_DEFPATH_ROOT
+#define _PATH_DEFPATH_ROOT "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
+
+#define _PATH_SECURETTY "/etc/securetty"
+#define _PATH_WTMPLOCK "/etc/wtmplock"
+
+#define _PATH_HUSHLOGIN ".hushlogin"
+#define _PATH_HUSHLOGINS "/etc/hushlogins"
+
+#define _PATH_NOLOGIN_TXT "/etc/nologin.txt"
+
+#ifndef _PATH_MAILDIR
+#define _PATH_MAILDIR "/var/spool/mail"
+#endif
+#define _PATH_MOTDFILE "/etc/motd"
+#define _PATH_NOLOGIN "/etc/nologin"
+#define _PATH_VAR_NOLOGIN "/var/run/nologin"
+
+#define _PATH_LOGIN "/bin/login"
+#define _PATH_INITTAB "/etc/inittab"
+#define _PATH_RC "/etc/rc"
+#define _PATH_REBOOT "/sbin/reboot"
+#define _PATH_SHUTDOWN "/sbin/shutdown"
+#define _PATH_SINGLE "/etc/singleboot"
+#define _PATH_SHUTDOWN_CONF "/etc/shutdown.conf"
+
+#define _PATH_SECURE "/etc/securesingle"
+#define _PATH_USERTTY "/etc/usertty"
+
+#define _PATH_TERMCOLORS_DIRNAME "terminal-colors.d"
+#define _PATH_TERMCOLORS_DIR "/etc/" _PATH_TERMCOLORS_DIRNAME
+
+/* used in login-utils/shutdown.c */
+
+/* used in login-utils/setpwnam.h and login-utils/islocal.c */
+#define _PATH_PASSWD "/etc/passwd"
+
+/* used in login-utils/newgrp and login-utils/setpwnam.h*/
+#define _PATH_GSHADOW "/etc/gshadow"
+
+/* used in login-utils/setpwnam.h */
+#define _PATH_GROUP "/etc/group"
+#define _PATH_SHADOW_PASSWD "/etc/shadow"
+#define _PATH_SHELLS "/etc/shells"
+
+/* used in term-utils/agetty.c */
+#define _PATH_ISSUE "/etc/issue"
+#define _PATH_OS_RELEASE "/etc/os-release"
+#define _PATH_NUMLOCK_ON _PATH_LOCALSTATEDIR "/numlock-on"
+
+#define _PATH_LOGINDEFS "/etc/login.defs"
+
+/* used in misc-utils/look.c */
+#define _PATH_WORDS "/usr/share/dict/words"
+#define _PATH_WORDS_ALT "/usr/share/dict/web2"
+
+/* mount paths */
+#define _PATH_UMOUNT "/bin/umount"
+
+#define _PATH_FILESYSTEMS "/etc/filesystems"
+#define _PATH_PROC_SWAPS "/proc/swaps"
+#define _PATH_PROC_FILESYSTEMS "/proc/filesystems"
+#define _PATH_PROC_MOUNTS "/proc/mounts"
+#define _PATH_PROC_PARTITIONS "/proc/partitions"
+#define _PATH_PROC_DEVICES "/proc/devices"
+#define _PATH_PROC_MOUNTINFO "/proc/self/mountinfo"
+#define _PATH_PROC_LOCKS "/proc/locks"
+#define _PATH_PROC_CDROMINFO "/proc/sys/dev/cdrom/info"
+
+#define _PATH_PROC_UIDMAP "/proc/self/uid_map"
+#define _PATH_PROC_GIDMAP "/proc/self/gid_map"
+
+#define _PATH_PROC_ATTR_CURRENT "/proc/self/attr/current"
+#define _PATH_PROC_ATTR_EXEC "/proc/self/attr/exec"
+#define _PATH_PROC_CAPLASTCAP "/proc/sys/kernel/cap_last_cap"
+
+
+#define _PATH_SYS_BLOCK "/sys/block"
+#define _PATH_SYS_DEVBLOCK "/sys/dev/block"
+#define _PATH_SYS_CLASS "/sys/class"
+#define _PATH_SYS_SCSI "/sys/bus/scsi"
+
+#define _PATH_SYS_SELINUX "/sys/fs/selinux"
+#define _PATH_SYS_APPARMOR "/sys/kernel/security/apparmor"
+
+#ifndef _PATH_MOUNTED
+# ifdef MOUNTED /* deprecated */
+# define _PATH_MOUNTED MOUNTED
+# else
+# define _PATH_MOUNTED "/etc/mtab"
+# endif
+#endif
+
+#ifndef _PATH_MNTTAB
+# ifdef MNTTAB /* deprecated */
+# define _PATH_MNTTAB MNTTAB
+# else
+# define _PATH_MNTTAB "/etc/fstab"
+# endif
+#endif
+
+#define _PATH_MNTTAB_DIR _PATH_MNTTAB ".d"
+
+#define _PATH_MOUNTED_LOCK _PATH_MOUNTED "~"
+#define _PATH_MOUNTED_TMP _PATH_MOUNTED ".tmp"
+
+#ifndef _PATH_DEV
+ /*
+ * The tailing '/' in _PATH_DEV is there for compatibility with libc.
+ */
+# define _PATH_DEV "/dev/"
+#endif
+
+#define _PATH_DEV_MEM "/dev/mem"
+
+#define _PATH_DEV_LOOP "/dev/loop"
+#define _PATH_DEV_LOOPCTL "/dev/loop-control"
+#define _PATH_DEV_TTY "/dev/tty"
+
+
+/* udev paths */
+#define _PATH_DEV_BYLABEL "/dev/disk/by-label"
+#define _PATH_DEV_BYUUID "/dev/disk/by-uuid"
+#define _PATH_DEV_BYID "/dev/disk/by-id"
+#define _PATH_DEV_BYPATH "/dev/disk/by-path"
+#define _PATH_DEV_BYPARTLABEL "/dev/disk/by-partlabel"
+#define _PATH_DEV_BYPARTUUID "/dev/disk/by-partuuid"
+
+/* hwclock paths */
+#ifdef CONFIG_ADJTIME_PATH
+# define _PATH_ADJTIME CONFIG_ADJTIME_PATH
+#else
+# define _PATH_ADJTIME "/etc/adjtime"
+#endif
+
+#define _PATH_LASTDATE "/var/lib/lastdate"
+#ifdef __ia64__
+# define _PATH_RTC_DEV "/dev/efirtc"
+#else
+# define _PATH_RTC_DEV "/dev/rtc"
+#endif
+
+#ifndef _PATH_BTMP
+#define _PATH_BTMP "/var/log/btmp"
+#endif
+
+/* raw paths*/
+#define _PATH_RAWDEVDIR "/dev/raw/"
+#define _PATH_RAWDEVCTL _PATH_RAWDEVDIR "rawctl"
+/* deprecated */
+#define _PATH_RAWDEVCTL_OLD "/dev/rawctl"
+
+/* wdctl path */
+#define _PATH_WATCHDOG_DEV "/dev/watchdog"
+
+/* ipc paths */
+#define _PATH_PROC_SYSV_MSG "/proc/sysvipc/msg"
+#define _PATH_PROC_SYSV_SEM "/proc/sysvipc/sem"
+#define _PATH_PROC_SYSV_SHM "/proc/sysvipc/shm"
+#define _PATH_PROC_IPC_MSGMAX "/proc/sys/kernel/msgmax"
+#define _PATH_PROC_IPC_MSGMNB "/proc/sys/kernel/msgmnb"
+#define _PATH_PROC_IPC_MSGMNI "/proc/sys/kernel/msgmni"
+#define _PATH_PROC_IPC_SEM "/proc/sys/kernel/sem"
+#define _PATH_PROC_IPC_SHMALL "/proc/sys/kernel/shmall"
+#define _PATH_PROC_IPC_SHMMAX "/proc/sys/kernel/shmmax"
+#define _PATH_PROC_IPC_SHMMNI "/proc/sys/kernel/shmmni"
+
+/* kernel command line */
+#define _PATH_PROC_CMDLINE "/proc/cmdline"
+
+#endif /* PATHNAMES_H */
+
diff --git a/libblkid/include/procutils.h b/libblkid/include/procutils.h
new file mode 100644
index 000000000..14b766cb7
--- /dev/null
+++ b/libblkid/include/procutils.h
@@ -0,0 +1,32 @@
+#ifndef UTIL_LINUX_PROCUTILS
+#define UTIL_LINUX_PROCUTILS
+
+#include <dirent.h>
+
+struct proc_tasks {
+ DIR *dir;
+};
+
+extern struct proc_tasks *proc_open_tasks(pid_t pid);
+extern void proc_close_tasks(struct proc_tasks *tasks);
+extern int proc_next_tid(struct proc_tasks *tasks, pid_t *tid);
+
+struct proc_processes {
+ DIR *dir;
+
+ const char *fltr_name;
+ uid_t fltr_uid;
+
+ unsigned int has_fltr_name : 1,
+ has_fltr_uid : 1;
+};
+
+extern struct proc_processes *proc_open_processes(void);
+extern void proc_close_processes(struct proc_processes *ps);
+
+extern void proc_processes_filter_by_name(struct proc_processes *ps, const char *name);
+extern void proc_processes_filter_by_uid(struct proc_processes *ps, uid_t uid);
+extern int proc_next_pid(struct proc_processes *ps, pid_t *pid);
+
+
+#endif /* UTIL_LINUX_PROCUTILS */
diff --git a/libblkid/include/pt-bsd.h b/libblkid/include/pt-bsd.h
new file mode 100644
index 000000000..9bf47a576
--- /dev/null
+++ b/libblkid/include/pt-bsd.h
@@ -0,0 +1,156 @@
+#ifndef UTIL_LINUX_PT_BSD_H
+#define UTIL_LINUX_PT_BSD_H
+
+#define BSD_MAXPARTITIONS 16
+#define BSD_FS_UNUSED 0
+
+#ifndef BSD_DISKMAGIC
+# define BSD_DISKMAGIC ((uint32_t) 0x82564557)
+#endif
+
+#define BSD_LINUX_BOOTDIR "/usr/ucb/mdec"
+
+#if defined (__alpha__) || defined (__powerpc__) || \
+ defined (__ia64__) || defined (__hppa__)
+# define BSD_LABELSECTOR 0
+# define BSD_LABELOFFSET 64
+#else
+# define BSD_LABELSECTOR 1
+# define BSD_LABELOFFSET 0
+#endif
+
+#define BSD_BBSIZE 8192 /* size of boot area, with label */
+#define BSD_SBSIZE 8192 /* max size of fs superblock */
+
+struct bsd_disklabel {
+ uint32_t d_magic; /* the magic number */
+ int16_t d_type; /* drive type */
+ int16_t d_subtype; /* controller/d_type specific */
+ char d_typename[16]; /* type name, e.g. "eagle" */
+ char d_packname[16]; /* pack identifier */
+
+ /* disk geometry: */
+ uint32_t d_secsize; /* # of bytes per sector */
+ uint32_t d_nsectors; /* # of data sectors per track */
+ uint32_t d_ntracks; /* # of tracks per cylinder */
+ uint32_t d_ncylinders; /* # of data cylinders per unit */
+ uint32_t d_secpercyl; /* # of data sectors per cylinder */
+ uint32_t d_secperunit; /* # of data sectors per unit */
+
+ /*
+ * Spares (bad sector replacements) below
+ * are not counted in d_nsectors or d_secpercyl.
+ * Spare sectors are assumed to be physical sectors
+ * which occupy space at the end of each track and/or cylinder.
+ */
+ uint16_t d_sparespertrack; /* # of spare sectors per track */
+ uint16_t d_sparespercyl; /* # of spare sectors per cylinder */
+
+ /*
+ * Alternate cylinders include maintenance, replacement,
+ * configuration description areas, etc.
+ */
+ uint32_t d_acylinders; /* # of alt. cylinders per unit */
+
+ /* hardware characteristics: */
+ /*
+ * d_interleave, d_trackskew and d_cylskew describe perturbations
+ * in the media format used to compensate for a slow controller.
+ * Interleave is physical sector interleave, set up by the formatter
+ * or controller when formatting. When interleaving is in use,
+ * logically adjacent sectors are not physically contiguous,
+ * but instead are separated by some number of sectors.
+ * It is specified as the ratio of physical sectors traversed
+ * per logical sector. Thus an interleave of 1:1 implies contiguous
+ * layout, while 2:1 implies that logical sector 0 is separated
+ * by one sector from logical sector 1.
+ * d_trackskew is the offset of sector 0 on track N
+ * relative to sector 0 on track N-1 on the same cylinder.
+ * Finally, d_cylskew is the offset of sector 0 on cylinder N
+ * relative to sector 0 on cylinder N-1.
+ */
+ uint16_t d_rpm; /* rotational speed */
+ uint16_t d_interleave; /* hardware sector interleave */
+ uint16_t d_trackskew; /* sector 0 skew, per track */
+ uint16_t d_cylskew; /* sector 0 skew, per cylinder */
+ uint32_t d_headswitch; /* head switch time, usec */
+ uint32_t d_trkseek; /* track-to-track seek, usec */
+ uint32_t d_flags; /* generic flags */
+ uint32_t d_drivedata[5]; /* drive-type specific information */
+ uint32_t d_spare[5]; /* reserved for future use */
+ uint32_t d_magic2; /* the magic number (again) */
+ uint16_t d_checksum; /* xor of data incl. partitions */
+
+ /* filesystem and partition information: */
+ uint16_t d_npartitions; /* number of partitions in following */
+ uint32_t d_bbsize; /* size of boot area at sn0, bytes */
+ uint32_t d_sbsize; /* max size of fs superblock, bytes */
+
+ struct bsd_partition { /* the partition table */
+ uint32_t p_size; /* number of sectors in partition */
+ uint32_t p_offset; /* starting sector */
+ uint32_t p_fsize; /* filesystem basic fragment size */
+ uint8_t p_fstype; /* filesystem type, see below */
+ uint8_t p_frag; /* filesystem fragments per block */
+ uint16_t p_cpg; /* filesystem cylinders per group */
+ } __attribute__((packed)) d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */
+} __attribute__((packed));
+
+
+/* d_type values: */
+#define BSD_DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */
+#define BSD_DTYPE_MSCP 2 /* MSCP */
+#define BSD_DTYPE_DEC 3 /* other DEC (rk, rl) */
+#define BSD_DTYPE_SCSI 4 /* SCSI */
+#define BSD_DTYPE_ESDI 5 /* ESDI interface */
+#define BSD_DTYPE_ST506 6 /* ST506 etc. */
+#define BSD_DTYPE_HPIB 7 /* CS/80 on HP-IB */
+#define BSD_DTYPE_HPFL 8 /* HP Fiber-link */
+#define BSD_DTYPE_FLOPPY 10 /* floppy */
+
+/* d_subtype values: */
+#define BSD_DSTYPE_INDOSPART 0x8 /* is inside dos partition */
+#define BSD_DSTYPE_DOSPART(s) ((s) & 3) /* dos partition number */
+#define BSD_DSTYPE_GEOMETRY 0x10 /* drive params in label */
+
+/*
+ * Filesystem type and version.
+ * Used to interpret other filesystem-specific
+ * per-partition information.
+ */
+#define BSD_FS_UNUSED 0 /* unused */
+#define BSD_FS_SWAP 1 /* swap */
+#define BSD_FS_V6 2 /* Sixth Edition */
+#define BSD_FS_V7 3 /* Seventh Edition */
+#define BSD_FS_SYSV 4 /* System V */
+#define BSD_FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */
+#define BSD_FS_V8 6 /* Eighth Edition, 4K blocks */
+#define BSD_FS_BSDFFS 7 /* 4.2BSD fast file system */
+#define BSD_FS_BSDLFS 9 /* 4.4BSD log-structured file system */
+#define BSD_FS_OTHER 10 /* in use, but unknown/unsupported */
+#define BSD_FS_HPFS 11 /* OS/2 high-performance file system */
+#define BSD_FS_ISO9660 12 /* ISO-9660 filesystem (cdrom) */
+#define BSD_FS_ISOFS BSD_FS_ISO9660
+#define BSD_FS_BOOT 13 /* partition contains bootstrap */
+#define BSD_FS_ADOS 14 /* AmigaDOS fast file system */
+#define BSD_FS_HFS 15 /* Macintosh HFS */
+#define BSD_FS_ADVFS 16 /* Digital Unix AdvFS */
+
+/* this is annoying, but it's also the way it is :-( */
+#ifdef __alpha__
+#define BSD_FS_EXT2 8 /* ext2 file system */
+#else
+#define BSD_FS_MSDOS 8 /* MS-DOS file system */
+#endif
+
+/*
+ * flags shared by various drives:
+ */
+#define BSD_D_REMOVABLE 0x01 /* removable media */
+#define BSD_D_ECC 0x02 /* supports ECC */
+#define BSD_D_BADSECT 0x04 /* supports bad sector forw. */
+#define BSD_D_RAMDISK 0x08 /* disk emulator */
+#define BSD_D_CHAIN 0x10 /* can do back-back transfers */
+#define BSD_D_DOSPART 0x20 /* within MSDOS partition */
+
+#endif /* UTIL_LINUX_PT_BSD_H */
diff --git a/libblkid/include/pt-mbr-partnames.h b/libblkid/include/pt-mbr-partnames.h
new file mode 100644
index 000000000..282adba91
--- /dev/null
+++ b/libblkid/include/pt-mbr-partnames.h
@@ -0,0 +1,105 @@
+ {0x00, N_("Empty")},
+ {0x01, N_("FAT12")},
+ {0x02, N_("XENIX root")},
+ {0x03, N_("XENIX usr")},
+ {0x04, N_("FAT16 <32M")},
+ {0x05, N_("Extended")}, /* DOS 3.3+ extended partition */
+ {0x06, N_("FAT16")}, /* DOS 16-bit >=32M */
+ {0x07, N_("HPFS/NTFS/exFAT")}, /* OS/2 IFS, eg, HPFS or NTFS or QNX or exFAT */
+ {0x08, N_("AIX")}, /* AIX boot (AIX -- PS/2 port) or SplitDrive */
+ {0x09, N_("AIX bootable")}, /* AIX data or Coherent */
+ {0x0a, N_("OS/2 Boot Manager")},/* OS/2 Boot Manager */
+ {0x0b, N_("W95 FAT32")},
+ {0x0c, N_("W95 FAT32 (LBA)")},/* LBA really is `Extended Int 13h' */
+ {0x0e, N_("W95 FAT16 (LBA)")},
+ {0x0f, N_("W95 Ext'd (LBA)")},
+ {0x10, N_("OPUS")},
+ {0x11, N_("Hidden FAT12")},
+ {0x12, N_("Compaq diagnostics")},
+ {0x14, N_("Hidden FAT16 <32M")},
+ {0x16, N_("Hidden FAT16")},
+ {0x17, N_("Hidden HPFS/NTFS")},
+ {0x18, N_("AST SmartSleep")},
+ {0x1b, N_("Hidden W95 FAT32")},
+ {0x1c, N_("Hidden W95 FAT32 (LBA)")},
+ {0x1e, N_("Hidden W95 FAT16 (LBA)")},
+ {0x24, N_("NEC DOS")},
+ {0x27, N_("Hidden NTFS WinRE")},
+ {0x39, N_("Plan 9")},
+ {0x3c, N_("PartitionMagic recovery")},
+ {0x40, N_("Venix 80286")},
+ {0x41, N_("PPC PReP Boot")},
+ {0x42, N_("SFS")},
+ {0x4d, N_("QNX4.x")},
+ {0x4e, N_("QNX4.x 2nd part")},
+ {0x4f, N_("QNX4.x 3rd part")},
+ {0x50, N_("OnTrack DM")},
+ {0x51, N_("OnTrack DM6 Aux1")}, /* (or Novell) */
+ {0x52, N_("CP/M")}, /* CP/M or Microport SysV/AT */
+ {0x53, N_("OnTrack DM6 Aux3")},
+ {0x54, N_("OnTrackDM6")},
+ {0x55, N_("EZ-Drive")},
+ {0x56, N_("Golden Bow")},
+ {0x5c, N_("Priam Edisk")},
+ {0x61, N_("SpeedStor")},
+ {0x63, N_("GNU HURD or SysV")}, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+ {0x64, N_("Novell Netware 286")},
+ {0x65, N_("Novell Netware 386")},
+ {0x70, N_("DiskSecure Multi-Boot")},
+ {0x75, N_("PC/IX")},
+ {0x80, N_("Old Minix")}, /* Minix 1.4a and earlier */
+ {0x81, N_("Minix / old Linux")},/* Minix 1.4b and later */
+ {0x82, N_("Linux swap / Solaris")},
+ {0x83, N_("Linux")},
+ {0x84, N_("OS/2 hidden C: drive")},
+ {0x85, N_("Linux extended")},
+ {0x86, N_("NTFS volume set")},
+ {0x87, N_("NTFS volume set")},
+ {0x88, N_("Linux plaintext")},
+ {0x8e, N_("Linux LVM")},
+ {0x93, N_("Amoeba")},
+ {0x94, N_("Amoeba BBT")}, /* (bad block table) */
+ {0x9f, N_("BSD/OS")}, /* BSDI */
+ {0xa0, N_("IBM Thinkpad hibernation")},
+ {0xa5, N_("FreeBSD")}, /* various BSD flavours */
+ {0xa6, N_("OpenBSD")},
+ {0xa7, N_("NeXTSTEP")},
+ {0xa8, N_("Darwin UFS")},
+ {0xa9, N_("NetBSD")},
+ {0xab, N_("Darwin boot")},
+ {0xaf, N_("HFS / HFS+")},
+ {0xb7, N_("BSDI fs")},
+ {0xb8, N_("BSDI swap")},
+ {0xbb, N_("Boot Wizard hidden")},
+ {0xbe, N_("Solaris boot")},
+ {0xbf, N_("Solaris")},
+ {0xc1, N_("DRDOS/sec (FAT-12)")},
+ {0xc4, N_("DRDOS/sec (FAT-16 < 32M)")},
+ {0xc6, N_("DRDOS/sec (FAT-16)")},
+ {0xc7, N_("Syrinx")},
+ {0xda, N_("Non-FS data")},
+ {0xdb, N_("CP/M / CTOS / ...")},/* CP/M or Concurrent CP/M or
+ Concurrent DOS or CTOS */
+ {0xde, N_("Dell Utility")}, /* Dell PowerEdge Server utilities */
+ {0xdf, N_("BootIt")}, /* BootIt EMBRM */
+ {0xe1, N_("DOS access")}, /* DOS access or SpeedStor 12-bit FAT
+ extended partition */
+ {0xe3, N_("DOS R/O")}, /* DOS R/O or SpeedStor */
+ {0xe4, N_("SpeedStor")}, /* SpeedStor 16-bit FAT extended
+ partition < 1024 cyl. */
+ {0xeb, N_("BeOS fs")},
+ {0xee, N_("GPT")}, /* Intel EFI GUID Partition Table */
+ {0xef, N_("EFI (FAT-12/16/32)")},/* Intel EFI System Partition */
+ {0xf0, N_("Linux/PA-RISC boot")},/* Linux/PA-RISC boot loader */
+ {0xf1, N_("SpeedStor")},
+ {0xf4, N_("SpeedStor")}, /* SpeedStor large partition */
+ {0xf2, N_("DOS secondary")}, /* DOS 3.3+ secondary */
+ {0xfb, N_("VMware VMFS")},
+ {0xfc, N_("VMware VMKCORE")}, /* VMware kernel dump partition */
+ {0xfd, N_("Linux raid autodetect")},/* New (2.2.x) raid partition with
+ autodetect using persistent
+ superblock */
+ {0xfe, N_("LANstep")}, /* SpeedStor >1024 cyl. or LANstep */
+ {0xff, N_("BBT")}, /* Xenix Bad Block Table */
+
+ { 0, 0 }
diff --git a/libblkid/include/pt-mbr.h b/libblkid/include/pt-mbr.h
new file mode 100644
index 000000000..1279e3cf2
--- /dev/null
+++ b/libblkid/include/pt-mbr.h
@@ -0,0 +1,178 @@
+#ifndef UTIL_LINUX_PT_MBR_H
+#define UTIL_LINUX_PT_MBR_H
+
+struct dos_partition {
+ unsigned char boot_ind; /* 0x80 - active */
+ unsigned char bh, bs, bc; /* begin CHS */
+ unsigned char sys_ind;
+ unsigned char eh, es, ec; /* end CHS */
+ unsigned char start_sect[4];
+ unsigned char nr_sects[4];
+} __attribute__((packed));
+
+#define MBR_PT_OFFSET 0x1be
+
+static inline struct dos_partition *mbr_get_partition(unsigned char *mbr, int i)
+{
+ return (struct dos_partition *)
+ (mbr + MBR_PT_OFFSET + (i * sizeof(struct dos_partition)));
+}
+
+/* assemble badly aligned little endian integer */
+static inline unsigned int __dos_assemble_4le(const unsigned char *p)
+{
+ return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+}
+
+static inline void __dos_store_4le(unsigned char *p, unsigned int val)
+{
+ p[0] = (val & 0xff);
+ p[1] = ((val >> 8) & 0xff);
+ p[2] = ((val >> 16) & 0xff);
+ p[3] = ((val >> 24) & 0xff);
+}
+
+static inline unsigned int dos_partition_get_start(struct dos_partition *p)
+{
+ return __dos_assemble_4le(&(p->start_sect[0]));
+}
+
+static inline void dos_partition_set_start(struct dos_partition *p, unsigned int n)
+{
+ __dos_store_4le(p->start_sect, n);
+}
+
+static inline unsigned int dos_partition_get_size(struct dos_partition *p)
+{
+ return __dos_assemble_4le(&(p->nr_sects[0]));
+}
+
+static inline void dos_partition_set_size(struct dos_partition *p, unsigned int n)
+{
+ __dos_store_4le(p->nr_sects, n);
+}
+
+static inline int mbr_is_valid_magic(const unsigned char *mbr)
+{
+ return mbr[510] == 0x55 && mbr[511] == 0xaa ? 1 : 0;
+}
+
+static inline void mbr_set_magic(unsigned char *b)
+{
+ b[510] = 0x55;
+ b[511] = 0xaa;
+}
+
+static inline unsigned int mbr_get_id(const unsigned char *mbr)
+{
+ return __dos_assemble_4le(&mbr[440]);
+}
+
+static inline void mbr_set_id(unsigned char *b, unsigned int id)
+{
+ __dos_store_4le(&b[440], id);
+}
+
+enum {
+ MBR_EMPTY_PARTITION = 0x00,
+ MBR_FAT12_PARTITION = 0x01,
+ MBR_XENIX_ROOT_PARTITION = 0x02,
+ MBR_XENIX_USR_PARTITION = 0x03,
+ MBR_FAT16_LESS32M_PARTITION = 0x04,
+ MBR_DOS_EXTENDED_PARTITION = 0x05,
+ MBR_FAT16_PARTITION = 0x06, /* DOS 16-bit >=32M */
+ MBR_HPFS_NTFS_PARTITION = 0x07, /* OS/2 IFS, eg, HPFS or NTFS or QNX */
+ MBR_AIX_PARTITION = 0x08, /* AIX boot (AIX -- PS/2 port) or SplitDrive */
+ MBR_AIX_BOOTABLE_PARTITION = 0x09, /* AIX data or Coherent */
+ MBR_OS2_BOOTMNGR_PARTITION = 0x0a, /* OS/2 Boot Manager */
+ MBR_W95_FAT32_PARTITION = 0x0b,
+ MBR_W95_FAT32_LBA_PARTITION = 0x0c, /* LBA really is `Extended Int 13h' */
+ MBR_W95_FAT16_LBA_PARTITION = 0x0e,
+ MBR_W95_EXTENDED_PARTITION = 0x0f,
+ MBR_OPUS_PARTITION = 0x10,
+ MBR_HIDDEN_FAT12_PARTITION = 0x11,
+ MBR_COMPAQ_DIAGNOSTICS_PARTITION = 0x12,
+ MBR_HIDDEN_FAT16_L32M_PARTITION = 0x14,
+ MBR_HIDDEN_FAT16_PARTITION = 0x16,
+ MBR_HIDDEN_HPFS_NTFS_PARTITION = 0x17,
+ MBR_AST_SMARTSLEEP_PARTITION = 0x18,
+ MBR_HIDDEN_W95_FAT32_PARTITION = 0x1b,
+ MBR_HIDDEN_W95_FAT32LBA_PARTITION = 0x1c,
+ MBR_HIDDEN_W95_FAT16LBA_PARTITION = 0x1e,
+ MBR_NEC_DOS_PARTITION = 0x24,
+ MBR_PLAN9_PARTITION = 0x39,
+ MBR_PARTITIONMAGIC_PARTITION = 0x3c,
+ MBR_VENIX80286_PARTITION = 0x40,
+ MBR_PPC_PREP_BOOT_PARTITION = 0x41,
+ MBR_SFS_PARTITION = 0x42,
+ MBR_QNX_4X_PARTITION = 0x4d,
+ MBR_QNX_4X_2ND_PARTITION = 0x4e,
+ MBR_QNX_4X_3RD_PARTITION = 0x4f,
+ MBR_DM_PARTITION = 0x50,
+ MBR_DM6_AUX1_PARTITION = 0x51, /* (or Novell) */
+ MBR_CPM_PARTITION = 0x52, /* CP/M or Microport SysV/AT */
+ MBR_DM6_AUX3_PARTITION = 0x53,
+ MBR_DM6_PARTITION = 0x54,
+ MBR_EZ_DRIVE_PARTITION = 0x55,
+ MBR_GOLDEN_BOW_PARTITION = 0x56,
+ MBR_PRIAM_EDISK_PARTITION = 0x5c,
+ MBR_SPEEDSTOR_PARTITION = 0x61,
+ MBR_GNU_HURD_PARTITION = 0x63, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+ MBR_UNIXWARE_PARTITION = MBR_GNU_HURD_PARTITION,
+ MBR_NETWARE_286_PARTITION = 0x64,
+ MBR_NETWARE_386_PARTITION = 0x65,
+ MBR_DISKSECURE_MULTIBOOT_PARTITION = 0x70,
+ MBR_PC_IX_PARTITION = 0x75,
+ MBR_OLD_MINIX_PARTITION = 0x80, /* Minix 1.4a and earlier */
+ MBR_MINIX_PARTITION = 0x81, /* Minix 1.4b and later */
+ MBR_LINUX_SWAP_PARTITION = 0x82,
+ MBR_SOLARIS_X86_PARTITION = MBR_LINUX_SWAP_PARTITION,
+ MBR_LINUX_DATA_PARTITION = 0x83,
+ MBR_OS2_HIDDEN_DRIVE_PARTITION = 0x84,
+ MBR_LINUX_EXTENDED_PARTITION = 0x85,
+ MBR_NTFS_VOL_SET1_PARTITION = 0x86,
+ MBR_NTFS_VOL_SET2_PARTITION = 0x87,
+ MBR_LINUX_PLAINTEXT_PARTITION = 0x88,
+ MBR_LINUX_LVM_PARTITION = 0x8e,
+ MBR_AMOEBA_PARTITION = 0x93,
+ MBR_AMOEBA_BBT_PARTITION = 0x94, /* (bad block table) */
+ MBR_BSD_OS_PARTITION = 0x9f, /* BSDI */
+ MBR_THINKPAD_HIBERNATION_PARTITION = 0xa0,
+ MBR_FREEBSD_PARTITION = 0xa5, /* various BSD flavours */
+ MBR_OPENBSD_PARTITION = 0xa6,
+ MBR_NEXTSTEP_PARTITION = 0xa7,
+ MBR_DARWIN_UFS_PARTITION = 0xa8,
+ MBR_NETBSD_PARTITION = 0xa9,
+ MBR_DARWIN_BOOT_PARTITION = 0xab,
+ MBR_HFS_HFS_PARTITION = 0xaf,
+ MBR_BSDI_FS_PARTITION = 0xb7,
+ MBR_BSDI_SWAP_PARTITION = 0xb8,
+ MBR_BOOTWIZARD_HIDDEN_PARTITION = 0xbb,
+ MBR_SOLARIS_BOOT_PARTITION = 0xbe,
+ MBR_SOLARIS_PARTITION = 0xbf,
+ MBR_DRDOS_FAT12_PARTITION = 0xc1,
+ MBR_DRDOS_FAT16_L32M_PARTITION = 0xc4,
+ MBR_DRDOS_FAT16_PARTITION = 0xc6,
+ MBR_SYRINX_PARTITION = 0xc7,
+ MBR_NONFS_DATA_PARTITION = 0xda,
+ MBR_CPM_CTOS_PARTITION = 0xdb, /* CP/M or Concurrent CP/M or Concurrent DOS or CTOS */
+ MBR_DELL_UTILITY_PARTITION = 0xde, /* Dell PowerEdge Server utilities */
+ MBR_BOOTIT_PARTITION = 0xdf, /* BootIt EMBRM */
+ MBR_DOS_ACCESS_PARTITION = 0xe1, /* DOS access or SpeedStor 12-bit FAT extended partition */
+ MBR_DOS_RO_PARTITION = 0xe3, /* DOS R/O or SpeedStor */
+ MBR_SPEEDSTOR_EXTENDED_PARTITION = 0xe4, /* SpeedStor 16-bit FAT extended partition < 1024 cyl. */
+ MBR_BEOS_FS_PARTITION = 0xeb,
+ MBR_GPT_PARTITION = 0xee, /* Intel EFI GUID Partition Table */
+ MBR_EFI_SYSTEM_PARTITION = 0xef, /* Intel EFI System Partition */
+ MBR_LINUX_PARISC_BOOT_PARTITION = 0xf0, /* Linux/PA-RISC boot loader */
+ MBR_SPEEDSTOR1_PARTITION = 0xf1,
+ MBR_SPEEDSTOR2_PARTITION = 0xf4, /* SpeedStor large partition */
+ MBR_DOS_SECONDARY_PARTITION = 0xf2, /* DOS 3.3+ secondary */
+ MBR_VMWARE_VMFS_PARTITION = 0xfb,
+ MBR_VMWARE_VMKCORE_PARTITION = 0xfc, /* VMware kernel dump partition */
+ MBR_LINUX_RAID_PARTITION = 0xfd, /* New (2.2.x) raid partition with autodetect using persistent superblock */
+ MBR_LANSTEP_PARTITION = 0xfe, /* SpeedStor >1024 cyl. or LANstep */
+ MBR_XENIX_BBT_PARTITION = 0xff, /* Xenix Bad Block Table */
+};
+
+#endif /* UTIL_LINUX_PT_MBR_H */
diff --git a/libblkid/include/pt-sgi.h b/libblkid/include/pt-sgi.h
new file mode 100644
index 000000000..547b37a87
--- /dev/null
+++ b/libblkid/include/pt-sgi.h
@@ -0,0 +1,110 @@
+#ifndef UTIL_LINUX_PT_SUN_H
+#define UTIL_LINUX_PT_SUN_H
+
+#include <stdint.h>
+
+#define SGI_LABEL_MAGIC 0x0be5a941
+
+#define SGI_MAXPARTITIONS 16
+#define SGI_MAXVOLUMES 15
+
+/* partition types */
+enum {
+ SGI_TYPE_VOLHDR = 0x00,
+ SGI_TYPE_TRKREPL = 0x01,
+ SGI_TYPE_SECREPL = 0x02,
+ SGI_TYPE_SWAP = 0x03,
+ SGI_TYPE_BSD = 0x04,
+ SGI_TYPE_SYSV = 0x05,
+ SGI_TYPE_ENTIRE_DISK = 0x06,
+ SGI_TYPE_EFS = 0x07,
+ SGI_TYPE_LVOL = 0x08,
+ SGI_TYPE_RLVOL = 0x09,
+ SGI_TYPE_XFS = 0x0a,
+ SGI_TYPE_XFSLOG = 0x0b,
+ SGI_TYPE_XLV = 0x0c,
+ SGI_TYPE_XVM = 0x0d
+};
+
+struct sgi_device_parameter {
+ unsigned char skew;
+ unsigned char gap1;
+ unsigned char gap2;
+ unsigned char sparecyl;
+
+ uint16_t pcylcount;
+ uint16_t head_vol0;
+ uint16_t ntrks; /* tracks in cyl 0 or vol 0 */
+
+ unsigned char cmd_tag_queue_depth;
+ unsigned char unused0;
+
+ uint16_t unused1;
+ uint16_t nsect; /* sectors/tracks in cyl 0 or vol 0 */
+ uint16_t bytes;
+ uint16_t ilfact;
+ uint32_t flags; /* SGI_DEVPARAM_* controller flags */
+ uint32_t datarate;
+ uint32_t retries_on_error;
+ uint32_t ms_per_word;
+ uint16_t xylogics_gap1;
+ uint16_t xylogics_syncdelay;
+ uint16_t xylogics_readdelay;
+ uint16_t xylogics_gap2;
+ uint16_t xylogics_readgate;
+ uint16_t xylogics_writecont;
+} __attribute__((packed));
+
+enum {
+ SGI_DEVPARAM_SECTOR_SLIP = 0x01,
+ SGI_DEVPARAM_SECTOR_FWD = 0x02,
+ SGI_DEVPARAM_TRACK_FWD = 0x04,
+ SGI_DEVPARAM_TRACK_MULTIVOL = 0x08,
+ SGI_DEVPARAM_IGNORE_ERRORS = 0x10,
+ SGI_DEVPARAM_RESEEK = 0x20,
+ SGI_DEVPARAM_CMDTAGQ_ENABLE = 0x40
+};
+
+
+struct sgi_disklabel {
+ uint32_t magic; /* magic number */
+ uint16_t root_part_num; /* # root partition */
+ uint16_t swap_part_num; /* # swap partition */
+ unsigned char boot_file[16]; /* name of boot file */
+
+ struct sgi_device_parameter devparam; /* not used now */
+
+ struct sgi_volume {
+ unsigned char name[8]; /* name of volume */
+ uint32_t block_num; /* logical block number */
+ uint32_t num_bytes; /* how big, in bytes */
+ } __attribute__((packed)) volume[SGI_MAXVOLUMES];
+
+ struct sgi_partition {
+ uint32_t num_blocks; /* size in logical blocks */
+ uint32_t first_block; /* first logical block */
+ uint32_t type; /* type of this partition */
+ } __attribute__((packed)) partitions[SGI_MAXPARTITIONS];
+
+ /* checksum is the 32bit 2's complement sum of the disklabel */
+ uint32_t csum; /* disk label checksum */
+ uint32_t padding; /* padding */
+} __attribute__((packed));
+
+static inline uint32_t sgi_pt_checksum(struct sgi_disklabel *label)
+{
+ int i;
+ uint32_t *ptr = (uint32_t *) label;
+ uint32_t sum = 0;
+
+ i = sizeof(*label) / sizeof(*ptr);
+
+ while (i) {
+ i--;
+ sum -= be32_to_cpu(ptr[i]);
+ }
+
+ return sum;
+}
+
+#endif /* UTIL_LINUX_PT_SUN_H */
diff --git a/libblkid/include/pt-sun.h b/libblkid/include/pt-sun.h
new file mode 100644
index 000000000..b085268ca
--- /dev/null
+++ b/libblkid/include/pt-sun.h
@@ -0,0 +1,90 @@
+#ifndef UTIL_LINUX_PT_SUN_H
+#define UTIL_LINUX_PT_SUN_H
+
+#include <stdint.h>
+
+#define SUN_LABEL_MAGIC 0xDABE
+
+/* Supported VTOC setting */
+#define SUN_VTOC_SANITY 0x600DDEEE /* magic number */
+#define SUN_VTOC_VERSION 1
+#define SUN_MAXPARTITIONS 8
+
+struct sun_disklabel {
+ unsigned char label_id[128]; /* Informative text string */
+
+ struct sun_vtoc {
+ uint32_t version; /* version */
+ char volume_id[8];/* volume name */
+ uint16_t nparts; /* num of partitions */
+
+ struct sun_info { /* partition information */
+ uint16_t id; /* SUN_TAG_* */
+ uint16_t flags; /* SUN_FLAG_* */
+ } __attribute__ ((packed)) infos[8];
+
+ uint16_t padding; /* padding */
+ uint32_t bootinfo[3]; /* info needed by mboot */
+ uint32_t sanity; /* magic number */
+ uint32_t reserved[10]; /* padding */
+ uint32_t timestamp[8]; /* partition timestamp */
+ } __attribute__ ((packed)) vtoc;
+
+ uint32_t write_reinstruct; /* sectors to skip, writes */
+ uint32_t read_reinstruct; /* sectors to skip, reads */
+ unsigned char spare[148]; /* padding */
+ uint16_t rpm; /* disk rotational speed */
+ uint16_t pcyl; /* physical cylinder count */
+ uint16_t apc; /* extra sects per cylinder */
+ uint16_t obs1;
+ uint16_t obs2;
+ uint16_t intrlv; /* interleave factor */
+ uint16_t ncyl; /* data cylinder count */
+ uint16_t acyl; /* alt. cylinder count */
+ uint16_t nhead; /* tracks per cylinder <---- */
+ uint16_t nsect; /* sectors per track <---- */
+ uint16_t obs3;
+ uint16_t obs4;
+
+ struct sun_partition { /* partitions */
+ uint32_t start_cylinder;
+ uint32_t num_sectors;
+ } __attribute__ ((packed)) partitions[8];
+
+ uint16_t magic; /* magic number */
+ uint16_t csum; /* label xor'd checksum */
+} __attribute__ ((packed));
+
+
+#define SUN_TAG_UNASSIGNED 0x00 /* Unassigned partition */
+#define SUN_TAG_BOOT 0x01 /* Boot partition */
+#define SUN_TAG_ROOT 0x02 /* Root filesystem */
+#define SUN_TAG_SWAP 0x03 /* Swap partition */
+#define SUN_TAG_USR 0x04 /* /usr filesystem */
+#define SUN_TAG_WHOLEDISK 0x05 /* Full-disk slice */
+#define SUN_TAG_STAND 0x06 /* Stand partition */
+#define SUN_TAG_VAR 0x07 /* /var filesystem */
+#define SUN_TAG_HOME 0x08 /* /home filesystem */
+#define SUN_TAG_ALTSCTR 0x09 /* Alt sector partition */
+#define SUN_TAG_CACHE 0x0a /* Cachefs partition */
+#define SUN_TAG_RESERVED 0x0b /* SMI reserved data */
+#define SUN_TAG_LINUX_SWAP 0x82 /* Linux SWAP */
+#define SUN_TAG_LINUX_NATIVE 0x83 /* Linux filesystem */
+#define SUN_TAG_LINUX_LVM 0x8e /* Linux LVM */
+#define SUN_TAG_LINUX_RAID 0xfd /* LInux RAID */
+
+#define SUN_FLAG_UNMNT 0x01 /* Unmountable partition*/
+#define SUN_FLAG_RONLY 0x10 /* Read only */
+
+static inline uint16_t sun_pt_checksum(struct sun_disklabel *label)
+{
+ uint16_t *ptr = ((uint16_t *) (label + 1)) - 1;
+ uint16_t sum;
+
+ for (sum = 0; ptr >= ((uint16_t *) label);)
+ sum ^= *ptr--;
+
+ return sum;
+}
+
+#endif /* UTIL_LINUX_PT_SUN_H */
diff --git a/libblkid/include/randutils.h b/libblkid/include/randutils.h
new file mode 100644
index 000000000..17e2a02fa
--- /dev/null
+++ b/libblkid/include/randutils.h
@@ -0,0 +1,13 @@
+#ifndef UTIL_LINUX_RANDUTILS
+#define UTIL_LINUX_RANDUTILS
+
+#ifdef HAVE_SRANDOM
+#define srand(x) srandom(x)
+#define rand() random()
+#endif
+
+extern int random_get_fd(void);
+extern void random_get_bytes(void *buf, size_t nbytes);
+extern const char *random_tell_source(void);
+
+#endif
diff --git a/libblkid/include/readutmp.h b/libblkid/include/readutmp.h
new file mode 100644
index 000000000..93251eac1
--- /dev/null
+++ b/libblkid/include/readutmp.h
@@ -0,0 +1,28 @@
+/* Declarations for GNU's read utmp module.
+
+ Copyright (C) 1992-2007, 2009-2014 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Written by jla; revised by djm */
+
+#ifndef READUTMP_H
+#define READUTMP_H
+
+#include <sys/types.h>
+#include <utmp.h>
+
+int read_utmp (char const *file, size_t *n_entries, struct utmp **utmp_buf);
+
+#endif /* READUTMP_H */
diff --git a/libblkid/include/rpmatch.h b/libblkid/include/rpmatch.h
new file mode 100644
index 000000000..d62634bd8
--- /dev/null
+++ b/libblkid/include/rpmatch.h
@@ -0,0 +1,9 @@
+#ifndef UTIL_LINUX_RPMATCH_H
+#define UTIL_LINUX_RPMATCH_H
+
+#ifndef HAVE_RPMATCH
+#define rpmatch(r) \
+ (*r == 'y' || *r == 'Y' ? 1 : *r == 'n' || *r == 'N' ? 0 : -1)
+#endif
+
+#endif /* UTIL_LINUX_RPMATCH_H */
diff --git a/libblkid/include/setproctitle.h b/libblkid/include/setproctitle.h
new file mode 100644
index 000000000..70a9efa10
--- /dev/null
+++ b/libblkid/include/setproctitle.h
@@ -0,0 +1,7 @@
+#ifndef UTIL_LINUX_SETPROCTITLE_H
+#define UTIL_LINUX_SETPROCTITLE_H
+
+extern void initproctitle (int argc, char **argv);
+extern void setproctitle (const char *prog, const char *txt);
+
+#endif
diff --git a/libblkid/include/statfs_magic.h b/libblkid/include/statfs_magic.h
new file mode 100644
index 000000000..7397a4e5d
--- /dev/null
+++ b/libblkid/include/statfs_magic.h
@@ -0,0 +1,99 @@
+#ifndef UTIL_LINUX_STATFS_MAGIC_H
+#define UTIL_LINUX_STATFS_MAGIC_H
+
+#include <sys/statfs.h>
+
+/*
+ * If possible then don't depend on internal libc __SWORD_TYPE type.
+ */
+#ifdef __GNUC__
+#define F_TYPE_EQUAL(a, b) (a == (__typeof__(a)) b)
+#else
+#define F_TYPE_EQUAL(a, b) (a == (__SWORD_TYPE) b)
+#endif
+
+/*
+ * Unfortunately, Linux kernel hedeader file <linux/magic.h> is incomplete
+ * mess and kernel returns by statfs f_type many numbers that are nowhere
+ * specified (in API).
+ *
+ * This is collection of the magic numbers.
+ */
+#define STATFS_ADFS_MAGIC 0xadf5
+#define STATFS_AFFS_MAGIC 0xadff
+#define STATFS_AFS_MAGIC 0x5346414F
+#define STATFS_AUTOFS_MAGIC 0x0187
+#define STATFS_BDEVFS_MAGIC 0x62646576
+#define STATFS_BEFS_MAGIC 0x42465331
+#define STATFS_BFS_MAGIC 0x1BADFACE
+#define STATFS_BINFMTFS_MAGIC 0x42494e4d
+#define STATFS_BTRFS_MAGIC 0x9123683E
+#define STATFS_CEPH_MAGIC 0x00c36400
+#define STATFS_CGROUP_MAGIC 0x27e0eb
+#define STATFS_CIFS_MAGIC 0xff534d42
+#define STATFS_CODA_MAGIC 0x73757245
+#define STATFS_CONFIGFS_MAGIC 0x62656570
+#define STATFS_CRAMFS_MAGIC 0x28cd3d45
+#define STATFS_DEBUGFS_MAGIC 0x64626720
+#define STATFS_DEVPTS_MAGIC 0x1cd1
+#define STATFS_ECRYPTFS_MAGIC 0xf15f
+#define STATFS_EFIVARFS_MAGIC 0xde5e81e4
+#define STATFS_EFS_MAGIC 0x414A53
+#define STATFS_EXOFS_MAGIC 0x5DF5
+#define STATFS_EXT2_MAGIC 0xEF53
+#define STATFS_EXT3_MAGIC 0xEF53
+#define STATFS_EXT4_MAGIC 0xEF53
+#define STATFS_F2FS_MAGIC 0xF2F52010
+#define STATFS_FUSE_MAGIC 0x65735546
+#define STATFS_FUTEXFS_MAGIC 0xBAD1DEA
+#define STATFS_GFS2_MAGIC 0x01161970
+#define STATFS_HFSPLUS_MAGIC 0x482b
+#define STATFS_HOSTFS_MAGIC 0x00c0ffee
+#define STATFS_HPFS_MAGIC 0xf995e849
+#define STATFS_HPPFS_MAGIC 0xb00000ee
+#define STATFS_HUGETLBFS_MAGIC 0x958458f6
+#define STATFS_ISOFS_MAGIC 0x9660
+#define STATFS_JFFS2_MAGIC 0x72b6
+#define STATFS_JFS_MAGIC 0x3153464a
+#define STATFS_LOGFS_MAGIC 0xc97e8168
+#define STATFS_MINIX2_MAGIC 0x2468
+#define STATFS_MINIX2_MAGIC2 0x2478
+#define STATFS_MINIX3_MAGIC 0x4d5a
+#define STATFS_MINIX_MAGIC 0x137F
+#define STATFS_MINIX_MAGIC2 0x138F
+#define STATFS_MQUEUE_MAGIC 0x19800202
+#define STATFS_MSDOS_MAGIC 0x4d44
+#define STATFS_NCP_MAGIC 0x564c
+#define STATFS_NFS_MAGIC 0x6969
+#define STATFS_NILFS_MAGIC 0x3434
+#define STATFS_NTFS_MAGIC 0x5346544e
+#define STATFS_OCFS2_MAGIC 0x7461636f
+#define STATFS_OMFS_MAGIC 0xC2993D87
+#define STATFS_OPENPROMFS_MAGIC 0x9fa1
+#define STATFS_PIPEFS_MAGIC 0x50495045
+#define STATFS_PROC_MAGIC 0x9fa0
+#define STATFS_PSTOREFS_MAGIC 0x6165676C
+#define STATFS_QNX4_MAGIC 0x002f
+#define STATFS_QNX6_MAGIC 0x68191122
+#define STATFS_RAMFS_MAGIC 0x858458f6
+#define STATFS_REISERFS_MAGIC 0x52654973
+#define STATFS_ROMFS_MAGIC 0x7275
+#define STATFS_SECURITYFS_MAGIC 0x73636673
+#define STATFS_SELINUXFS_MAGIC 0xf97cff8c
+#define STATFS_SMACKFS_MAGIC 0x43415d53
+#define STATFS_SMB_MAGIC 0x517B
+#define STATFS_SOCKFS_MAGIC 0x534F434B
+#define STATFS_SQUASHFS_MAGIC 0x73717368
+#define STATFS_SYSFS_MAGIC 0x62656572
+#define STATFS_TMPFS_MAGIC 0x01021994
+#define STATFS_UBIFS_MAGIC 0x24051905
+#define STATFS_UDF_MAGIC 0x15013346
+#define STATFS_UFS2_MAGIC 0x19540119
+#define STATFS_UFS_MAGIC 0x00011954
+#define STATFS_V9FS_MAGIC 0x01021997
+#define STATFS_VXFS_MAGIC 0xa501FCF5
+#define STATFS_XENFS_MAGIC 0xabba1974
+#define STATFS_XFS_MAGIC 0x58465342
+
+#endif /* UTIL_LINUX_STATFS_MAGIC_H */
+
diff --git a/libblkid/include/strutils.h b/libblkid/include/strutils.h
new file mode 100644
index 000000000..4d8463a6d
--- /dev/null
+++ b/libblkid/include/strutils.h
@@ -0,0 +1,204 @@
+#ifndef UTIL_LINUX_STRUTILS
+#define UTIL_LINUX_STRUTILS
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+/* default strtoxx_or_err() exit code */
+#ifndef STRTOXX_EXIT_CODE
+# define STRTOXX_EXIT_CODE EXIT_FAILURE
+#endif
+
+
+extern int parse_size(const char *str, uintmax_t *res, int *power);
+extern int strtosize(const char *str, uintmax_t *res);
+extern uintmax_t strtosize_or_err(const char *str, const char *errmesg);
+
+extern int16_t strtos16_or_err(const char *str, const char *errmesg);
+extern uint16_t strtou16_or_err(const char *str, const char *errmesg);
+
+extern int32_t strtos32_or_err(const char *str, const char *errmesg);
+extern uint32_t strtou32_or_err(const char *str, const char *errmesg);
+
+extern int64_t strtos64_or_err(const char *str, const char *errmesg);
+extern uint64_t strtou64_or_err(const char *str, const char *errmesg);
+
+extern double strtod_or_err(const char *str, const char *errmesg);
+
+extern long strtol_or_err(const char *str, const char *errmesg);
+extern unsigned long strtoul_or_err(const char *str, const char *errmesg);
+
+extern void strtotimeval_or_err(const char *str, struct timeval *tv,
+ const char *errmesg);
+
+extern int isdigit_string(const char *str);
+
+#ifndef HAVE_MEMPCPY
+extern void *mempcpy(void *restrict dest, const void *restrict src, size_t n);
+#endif
+#ifndef HAVE_STRNLEN
+extern size_t strnlen(const char *s, size_t maxlen);
+#endif
+#ifndef HAVE_STRNDUP
+extern char *strndup(const char *s, size_t n);
+#endif
+#ifndef HAVE_STRNCHR
+extern char *strnchr(const char *s, size_t maxlen, int c);
+#endif
+
+/* caller guarantees n > 0 */
+static inline void xstrncpy(char *dest, const char *src, size_t n)
+{
+ strncpy(dest, src, n-1);
+ dest[n-1] = 0;
+}
+
+static inline char *strdup_to_offset(void *stru, size_t offset, const char *str)
+{
+ char *n = NULL;
+ char **o = (char **) ((char *) stru + offset);
+
+ if (str) {
+ n = strdup(str);
+ if (!n)
+ return NULL;
+ }
+
+ free(*o);
+ *o = n;
+ return n;
+}
+
+#define strdup_to_struct_member(_s, _m, _str) \
+ strdup_to_offset((void *) _s, offsetof(__typeof__(*(_s)), _m), _str)
+
+extern void strmode(mode_t mode, char *str);
+
+/* Options for size_to_human_string() */
+enum
+{
+ SIZE_SUFFIX_1LETTER = 0,
+ SIZE_SUFFIX_3LETTER = 1,
+ SIZE_SUFFIX_SPACE = 2
+};
+
+extern char *size_to_human_string(int options, uint64_t bytes);
+
+extern int string_to_idarray(const char *list, int ary[], size_t arysz,
+ int (name2id)(const char *, size_t));
+extern int string_add_to_idarray(const char *list, int ary[],
+ size_t arysz, int *ary_pos,
+ int (name2id)(const char *, size_t));
+
+extern int string_to_bitarray(const char *list, char *ary,
+ int (*name2bit)(const char *, size_t));
+
+extern int string_to_bitmask(const char *list,
+ unsigned long *mask,
+ long (*name2flag)(const char *, size_t));
+extern int parse_range(const char *str, int *lower, int *upper, int def);
+
+extern int streq_except_trailing_slash(const char *s1, const char *s2);
+
+/*
+ * Match string beginning.
+ */
+static inline const char *startswith(const char *s, const char *prefix)
+{
+ size_t sz = prefix ? strlen(prefix) : 0;
+
+ if (s && sz && strncmp(s, prefix, sz) == 0)
+ return s + sz;
+ return NULL;
+}
+
+/*
+ * Case insensitive match string beginning.
+ */
+static inline const char *startswith_no_case(const char *s, const char *prefix)
+{
+ size_t sz = prefix ? strlen(prefix) : 0;
+
+ if (s && sz && strncasecmp(s, prefix, sz) == 0)
+ return s + sz;
+ return NULL;
+}
+
+/*
+ * Match string ending.
+ */
+static inline const char *endswith(const char *s, const char *postfix)
+{
+ size_t sl = s ? strlen(s) : 0;
+ size_t pl = postfix ? strlen(postfix) : 0;
+
+ if (pl == 0)
+ return (char *)s + sl;
+ if (sl < pl)
+ return NULL;
+ if (memcmp(s + sl - pl, postfix, pl) != 0)
+ return NULL;
+ return (char *)s + sl - pl;
+}
+
+/*
+ * Skip leading white space.
+ */
+static inline const char *skip_space(const char *p)
+{
+ while (isspace(*p))
+ ++p;
+ return p;
+}
+
+static inline const char *skip_blank(const char *p)
+{
+ while (isblank(*p))
+ ++p;
+ return p;
+}
+
+
+/* Removes whitespace from the right-hand side of a string (trailing
+ * whitespace).
+ *
+ * Returns size of the new string (without \0).
+ */
+static inline size_t rtrim_whitespace(unsigned char *str)
+{
+ size_t i = strlen((char *) str);
+
+ while (i) {
+ i--;
+ if (!isspace(str[i])) {
+ i++;
+ break;
+ }
+ }
+ str[i] = '\0';
+ return i;
+}
+
+/* Removes whitespace from the left-hand side of a string.
+ *
+ * Returns size of the new string (without \0).
+ */
+static inline size_t ltrim_whitespace(unsigned char *str)
+{
+ size_t len;
+ unsigned char *p;
+
+ for (p = str; p && isspace(*p); p++);
+
+ len = strlen((char *) p);
+
+ if (len && p > str)
+ memmove(str, p, len + 1);
+
+ return len;
+}
+
+#endif
diff --git a/libblkid/include/swapheader.h b/libblkid/include/swapheader.h
new file mode 100644
index 000000000..3fce0d0fb
--- /dev/null
+++ b/libblkid/include/swapheader.h
@@ -0,0 +1,23 @@
+#ifndef _SWAPHEADER_H
+#define _SWAPHEADER_H
+
+#define SWAP_VERSION 1
+#define SWAP_UUID_LENGTH 16
+#define SWAP_LABEL_LENGTH 16
+#define SWAP_SIGNATURE "SWAPSPACE2"
+#define SWAP_SIGNATURE_SZ (sizeof(SWAP_SIGNATURE) - 1)
+
+#include <stdint.h>
+
+struct swap_header_v1_2 {
+ char bootbits[1024]; /* Space for disklabel etc. */
+ uint32_t version;
+ uint32_t last_page;
+ uint32_t nr_badpages;
+ unsigned char uuid[SWAP_UUID_LENGTH];
+ char volume_name[SWAP_LABEL_LENGTH];
+ uint32_t padding[117];
+ uint32_t badpages[1];
+};
+
+#endif /* _SWAPHEADER_H */
diff --git a/libblkid/include/swapprober.h b/libblkid/include/swapprober.h
new file mode 100644
index 000000000..510770045
--- /dev/null
+++ b/libblkid/include/swapprober.h
@@ -0,0 +1,9 @@
+#ifndef UTIL_LINUX_SWAP_PROBER_H
+#define UTIL_LINUX_SWAP_PROBER_H
+
+#include <blkid.h>
+
+blkid_probe get_swap_prober(const char *devname);
+
+#endif /* UTIL_LINUX_SWAP_PROBER_H */
+
diff --git a/libblkid/include/sysfs.h b/libblkid/include/sysfs.h
new file mode 100644
index 000000000..1de624aad
--- /dev/null
+++ b/libblkid/include/sysfs.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ */
+#ifndef UTIL_LINUX_SYSFS_H
+#define UTIL_LINUX_SYSFS_H
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <inttypes.h>
+#include <dirent.h>
+
+struct sysfs_cxt {
+ dev_t devno;
+ int dir_fd; /* /sys/block/<name> */
+ char *dir_path;
+ struct sysfs_cxt *parent;
+
+ unsigned int scsi_host,
+ scsi_channel,
+ scsi_target,
+ scsi_lun;
+
+ unsigned int has_hctl : 1;
+};
+
+#define UL_SYSFSCXT_EMPTY { 0, -1, NULL, NULL, 0, 0, 0, 0, 0 }
+
+extern char *sysfs_devno_attribute_path(dev_t devno, char *buf,
+ size_t bufsiz, const char *attr);
+extern int sysfs_devno_has_attribute(dev_t devno, const char *attr);
+extern char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz);
+extern char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz);
+extern dev_t sysfs_devname_to_devno(const char *name, const char *parent);
+
+extern int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent)
+ __attribute__ ((warn_unused_result));
+extern void sysfs_deinit(struct sysfs_cxt *cxt);
+
+extern DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr);
+
+extern int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st);
+extern ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr,
+ char *buf, size_t bufsiz);
+extern int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr);
+
+extern int sysfs_scanf(struct sysfs_cxt *cxt, const char *attr,
+ const char *fmt, ...)
+ __attribute__ ((format (scanf, 3, 4)));
+
+extern int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res);
+extern int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res);
+extern int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res);
+
+extern int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str);
+extern int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num);
+
+extern char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz);
+
+extern char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr);
+
+extern int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr);
+extern int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname);
+extern dev_t sysfs_partno_to_devno(struct sysfs_cxt *cxt, int partno);
+extern char *sysfs_get_slave(struct sysfs_cxt *cxt);
+
+extern char *sysfs_get_devchain(struct sysfs_cxt *cxt, char *buf, size_t bufsz);
+extern int sysfs_next_subsystem(struct sysfs_cxt *cxt, char *devchain, char **subsys);
+extern int sysfs_is_hotpluggable(struct sysfs_cxt *cxt);
+
+extern int sysfs_is_partition_dirent(DIR *dir, struct dirent *d,
+ const char *parent_name);
+
+extern int sysfs_devno_to_wholedisk(dev_t dev, char *diskname,
+ size_t len, dev_t *diskdevno);
+
+extern int sysfs_devno_is_lvm_private(dev_t devno);
+extern int sysfs_devno_is_wholedisk(dev_t devno);
+
+extern int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h,
+ int *c, int *t, int *l);
+extern char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt *cxt,
+ const char *type, const char *attr);
+extern int sysfs_scsi_host_is(struct sysfs_cxt *cxt, const char *type);
+extern int sysfs_scsi_has_attribute(struct sysfs_cxt *cxt, const char *attr);
+extern int sysfs_scsi_path_contains(struct sysfs_cxt *cxt, const char *pattern);
+
+#endif /* UTIL_LINUX_SYSFS_H */
diff --git a/libblkid/include/timer.h b/libblkid/include/timer.h
new file mode 100644
index 000000000..79ef64919
--- /dev/null
+++ b/libblkid/include/timer.h
@@ -0,0 +1,31 @@
+#ifndef UTIL_LINUX_TIMER_H
+#define UTIL_LINUX_TIMER_H
+
+#include <signal.h>
+#include <sys/time.h>
+
+static inline int setup_timer(
+ struct itimerval *timer,
+ struct itimerval *old_timer,
+ struct sigaction *old_sa,
+ void (*timeout_handler)(int))
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof sa);
+ sa.sa_handler = timeout_handler;
+ sa.sa_flags = SA_RESETHAND;
+ sigaction(SIGALRM, &sa, old_sa);
+
+ return setitimer(ITIMER_REAL, timer, old_timer);
+}
+
+static inline void cancel_timer(
+ struct itimerval *old_timer,
+ struct sigaction *old_sa)
+{
+ setitimer(ITIMER_REAL, old_timer, NULL);
+ sigaction(SIGALRM, old_sa, NULL);
+}
+
+#endif
diff --git a/libblkid/include/timeutils.h b/libblkid/include/timeutils.h
new file mode 100644
index 000000000..8ed501b9d
--- /dev/null
+++ b/libblkid/include/timeutils.h
@@ -0,0 +1,56 @@
+/***
+ First set of functions in this file are part of systemd, and were
+ copied to util-linux at August 2013.
+
+ Copyright 2010 Lennart Poettering
+ Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+
+ systemd 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.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+#ifndef UTIL_LINUX_TIME_UTIL_H
+#define UTIL_LINUX_TIME_UTIL_H
+
+#include <stdio.h>
+#include <inttypes.h>
+
+typedef uint64_t usec_t;
+typedef uint64_t nsec_t;
+
+#define MSEC_PER_SEC 1000ULL
+#define USEC_PER_SEC 1000000ULL
+#define USEC_PER_MSEC 1000ULL
+#define NSEC_PER_SEC 1000000000ULL
+#define NSEC_PER_MSEC 1000000ULL
+#define NSEC_PER_USEC 1000ULL
+
+#define USEC_PER_MINUTE (60ULL*USEC_PER_SEC)
+#define NSEC_PER_MINUTE (60ULL*NSEC_PER_SEC)
+#define USEC_PER_HOUR (60ULL*USEC_PER_MINUTE)
+#define NSEC_PER_HOUR (60ULL*NSEC_PER_MINUTE)
+#define USEC_PER_DAY (24ULL*USEC_PER_HOUR)
+#define NSEC_PER_DAY (24ULL*NSEC_PER_HOUR)
+#define USEC_PER_WEEK (7ULL*USEC_PER_DAY)
+#define NSEC_PER_WEEK (7ULL*NSEC_PER_DAY)
+#define USEC_PER_MONTH (2629800ULL*USEC_PER_SEC)
+#define NSEC_PER_MONTH (2629800ULL*NSEC_PER_SEC)
+#define USEC_PER_YEAR (31557600ULL*USEC_PER_SEC)
+#define NSEC_PER_YEAR (31557600ULL*NSEC_PER_SEC)
+
+#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */
+#define FORMAT_TIMESTAMP_RELATIVE_MAX 256
+#define FORMAT_TIMESPAN_MAX 64
+
+int parse_timestamp(const char *t, usec_t *usec);
+
+#endif /* UTIL_LINUX_TIME_UTIL_H */
diff --git a/libblkid/include/ttyutils.h b/libblkid/include/ttyutils.h
new file mode 100644
index 000000000..e842f9f0d
--- /dev/null
+++ b/libblkid/include/ttyutils.h
@@ -0,0 +1,168 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#ifndef UTIL_LINUX_TTYUTILS_H
+#define UTIL_LINUX_TTYUTILS_H
+
+#include <stdlib.h>
+#include <termios.h>
+#include <limits.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_TTYDEFAULTS_H
+#include <sys/ttydefaults.h>
+#endif
+
+/* Some shorthands for control characters. */
+#define CTL(x) ((x) ^ 0100) /* Assumes ASCII dialect */
+#define CR CTL('M') /* carriage return */
+#define NL CTL('J') /* line feed */
+#define BS CTL('H') /* back space */
+#define DEL CTL('?') /* delete */
+
+/* Defaults for line-editing etc. characters; you may want to change these. */
+#define DEF_ERASE DEL /* default erase character */
+#define DEF_INTR CTL('C') /* default interrupt character */
+#define DEF_QUIT CTL('\\') /* default quit char */
+#define DEF_KILL CTL('U') /* default kill char */
+#define DEF_EOF CTL('D') /* default EOF char */
+#define DEF_EOL 0
+#define DEF_SWITCH 0 /* default switch char */
+
+/* Storage for things detected while the login name was read. */
+struct chardata {
+ int erase; /* erase character */
+ int kill; /* kill character */
+ int eol; /* end-of-line character */
+ int parity; /* what parity did we see */
+ int capslock; /* upper case without lower case */
+};
+
+#define INIT_CHARDATA(ptr) do { \
+ (ptr)->erase = DEF_ERASE; \
+ (ptr)->kill = DEF_KILL; \
+ (ptr)->eol = CTRL('r'); \
+ (ptr)->parity = 0; \
+ (ptr)->capslock = 0; \
+ } while (0)
+
+extern int get_terminal_width(void);
+extern int get_terminal_name(int fd, const char **path, const char **name,
+ const char **number);
+
+#define UL_TTY_KEEPCFLAGS (1 << 1)
+#define UL_TTY_UTF8 (1 << 2)
+
+static inline void reset_virtual_console(struct termios *tp, int flags)
+{
+ /* Use defaults of <sys/ttydefaults.h> for base settings */
+ tp->c_iflag |= TTYDEF_IFLAG;
+ tp->c_oflag |= TTYDEF_OFLAG;
+ tp->c_lflag |= TTYDEF_LFLAG;
+
+ if ((flags & UL_TTY_KEEPCFLAGS) == 0) {
+#ifdef CBAUD
+ tp->c_lflag &= ~CBAUD;
+#endif
+ tp->c_cflag |= (B38400 | TTYDEF_CFLAG);
+ }
+
+ /* Sane setting, allow eight bit characters, no carriage return delay
+ * the same result as `stty sane cr0 pass8'
+ */
+#ifndef IUCLC
+# define IUCLC 0
+#endif
+#ifndef NL0
+# define NL0 0
+#endif
+#ifndef CR0
+# define CR0 0
+#endif
+#ifndef BS0
+# define BS0 0
+#endif
+#ifndef VT0
+# define VT0 0
+#endif
+#ifndef FF0
+# define FF0 0
+#endif
+#ifndef OLCUC
+# define OLCUC 0
+#endif
+#ifndef OFILL
+# define OFILL 0
+#endif
+#ifndef NLDLY
+# define NLDLY 0
+#endif
+#ifndef CRDLY
+# define CRDLY 0
+#endif
+#ifndef BSDLY
+# define BSDLY 0
+#endif
+#ifndef VTDLY
+# define VTDLY 0
+#endif
+#ifndef FFDLY
+# define FFDLY 0
+#endif
+
+ tp->c_iflag |= (BRKINT | ICRNL | IMAXBEL);
+ tp->c_iflag &= ~(IGNBRK | INLCR | IGNCR | IXOFF | IUCLC | IXANY | ISTRIP);
+ tp->c_oflag |= (OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0);
+ tp->c_oflag &= ~(OLCUC | OCRNL | ONOCR | ONLRET | OFILL | \
+ NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
+ tp->c_lflag |= (ISIG | ICANON | IEXTEN | ECHO|ECHOE|ECHOK|ECHOKE|ECHOCTL);
+ tp->c_lflag &= ~(ECHONL|ECHOPRT | NOFLSH | TOSTOP);
+
+ if ((flags & UL_TTY_KEEPCFLAGS) == 0) {
+ tp->c_cflag |= (CREAD | CS8 | HUPCL);
+ tp->c_cflag &= ~(PARODD | PARENB);
+ }
+#ifdef OFDEL
+ tp->c_oflag &= ~OFDEL;
+#endif
+#ifdef XCASE
+ tp->c_lflag &= ~XCASE;
+#endif
+#ifdef IUTF8
+ if (flags & UL_TTY_UTF8)
+ tp->c_iflag |= IUTF8; /* Set UTF-8 input flag */
+ else
+ tp->c_iflag &= ~IUTF8;
+#endif
+ /* VTIME and VMIN can overlap with VEOF and VEOL since they are
+ * only used for non-canonical mode. We just set the at the
+ * beginning, so nothing bad should happen.
+ */
+ tp->c_cc[VTIME] = 0;
+ tp->c_cc[VMIN] = 1;
+ tp->c_cc[VINTR] = CINTR;
+ tp->c_cc[VQUIT] = CQUIT;
+ tp->c_cc[VERASE] = CERASE; /* ASCII DEL (0177) */
+ tp->c_cc[VKILL] = CKILL;
+ tp->c_cc[VEOF] = CEOF;
+#ifdef VSWTC
+ tp->c_cc[VSWTC] = _POSIX_VDISABLE;
+#elif defined(VSWTCH)
+ tp->c_cc[VSWTCH] = _POSIX_VDISABLE;
+#endif
+ tp->c_cc[VSTART] = CSTART;
+ tp->c_cc[VSTOP] = CSTOP;
+ tp->c_cc[VSUSP] = CSUSP;
+ tp->c_cc[VEOL] = _POSIX_VDISABLE;
+ tp->c_cc[VREPRINT] = CREPRINT;
+ tp->c_cc[VDISCARD] = CDISCARD;
+ tp->c_cc[VWERASE] = CWERASE;
+ tp->c_cc[VLNEXT] = CLNEXT;
+ tp->c_cc[VEOL2] = _POSIX_VDISABLE;
+}
+
+#endif /* UTIL_LINUX_TTYUTILS_H */
diff --git a/libblkid/include/widechar.h b/libblkid/include/widechar.h
new file mode 100644
index 000000000..b023b5fb2
--- /dev/null
+++ b/libblkid/include/widechar.h
@@ -0,0 +1,38 @@
+/* Declarations for wide characters */
+/* This file must be included last because the redefinition of wchar_t may
+ cause conflicts when system include files were included after it. */
+
+#ifdef HAVE_WIDECHAR
+
+# include <wchar.h>
+# include <wctype.h>
+
+#else /* !HAVE_WIDECHAR */
+
+# include <ctype.h>
+ /* Fallback for types */
+# define wchar_t char
+# define wint_t int
+# define WEOF EOF
+ /* Fallback for input operations */
+# define fgetwc fgetc
+# define getwc getc
+# define getwchar getchar
+# define fgetws fgets
+ /* Fallback for output operations */
+# define fputwc fputc
+# define putwc putc
+# define putwchar putchar
+# define fputws fputs
+ /* Fallback for character classification */
+# define iswgraph isgraph
+# define iswprint isprint
+# define iswspace isspace
+ /* Fallback for string functions */
+# define wcschr strchr
+# define wcsdup strdup
+# define wcslen strlen
+
+# define wcwidth(c) 1
+
+#endif /* HAVE_WIDECHAR */
diff --git a/libblkid/include/xalloc.h b/libblkid/include/xalloc.h
new file mode 100644
index 000000000..f012fb294
--- /dev/null
+++ b/libblkid/include/xalloc.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * General memory allocation wrappers for malloc, realloc, calloc and strdup
+ */
+
+#ifndef UTIL_LINUX_XALLOC_H
+#define UTIL_LINUX_XALLOC_H
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "c.h"
+
+#ifndef XALLOC_EXIT_CODE
+# define XALLOC_EXIT_CODE EXIT_FAILURE
+#endif
+
+static inline __ul_alloc_size(1)
+void *xmalloc(const size_t size)
+{
+ void *ret = malloc(size);
+
+ if (!ret && size)
+ err(XALLOC_EXIT_CODE, "cannot allocate %zu bytes", size);
+ return ret;
+}
+
+static inline __ul_alloc_size(2)
+void *xrealloc(void *ptr, const size_t size)
+{
+ void *ret = realloc(ptr, size);
+
+ if (!ret && size)
+ err(XALLOC_EXIT_CODE, "cannot allocate %zu bytes", size);
+ return ret;
+}
+
+static inline __ul_calloc_size(1, 2)
+void *xcalloc(const size_t nelems, const size_t size)
+{
+ void *ret = calloc(nelems, size);
+
+ if (!ret && size && nelems)
+ err(XALLOC_EXIT_CODE, "cannot allocate %zu bytes", size);
+ return ret;
+}
+
+static inline char __attribute__((warn_unused_result)) *xstrdup(const char *str)
+{
+ char *ret;
+
+ if (!str)
+ return NULL;
+
+ ret = strdup(str);
+
+ if (!ret)
+ err(XALLOC_EXIT_CODE, "cannot duplicate string");
+ return ret;
+}
+
+static inline char * __attribute__((warn_unused_result)) xstrndup(const char *str, size_t size)
+{
+ char *ret;
+
+ if (!str)
+ return NULL;
+
+ ret = strndup(str, size);
+
+ if (!ret)
+ err(XALLOC_EXIT_CODE, "cannot duplicate string");
+ return ret;
+}
+
+
+static inline int __attribute__ ((__format__(printf, 2, 3)))
+ xasprintf(char **strp, const char *fmt, ...)
+{
+ int ret;
+ va_list args;
+ va_start(args, fmt);
+ ret = vasprintf(&(*strp), fmt, args);
+ va_end(args);
+ if (ret < 0)
+ err(XALLOC_EXIT_CODE, "cannot allocate string");
+ return ret;
+}
+
+static inline int xvasprintf(char **strp, const char *fmt, va_list ap)
+{
+ int ret = vasprintf(&(*strp), fmt, ap);
+ if (ret < 0)
+ err(XALLOC_EXIT_CODE, "cannot allocate string");
+ return ret;
+}
+
+
+static inline char * __attribute__((warn_unused_result)) xgethostname(void)
+{
+ char *name;
+ size_t sz = get_hostname_max() + 1;
+
+ name = xmalloc(sizeof(char) * sz);
+
+ if (gethostname(name, sz) != 0) {
+ free(name);
+ return NULL;
+ }
+ name[sz - 1] = '\0';
+ return name;
+}
+
+#endif
diff --git a/libblkid/lib/Makemodule.am b/libblkid/lib/Makemodule.am
new file mode 100644
index 000000000..565294e5c
--- /dev/null
+++ b/libblkid/lib/Makemodule.am
@@ -0,0 +1,115 @@
+
+noinst_LTLIBRARIES += libcommon.la
+libcommon_la_CFLAGS = $(AM_CFLAGS)
+libcommon_la_SOURCES = \
+ lib/at.c \
+ lib/blkdev.c \
+ lib/canonicalize.c \
+ lib/colors.c \
+ lib/crc32.c \
+ lib/crc64.c \
+ lib/env.c \
+ lib/fileutils.c \
+ lib/ismounted.c \
+ lib/mangle.c \
+ lib/match.c \
+ lib/mbsalign.c \
+ lib/md5.c \
+ lib/pager.c \
+ lib/path.c \
+ lib/procutils.c \
+ lib/randutils.c \
+ lib/setproctitle.c \
+ lib/strutils.c \
+ lib/sysfs.c \
+ lib/timeutils.c \
+ lib/ttyutils.c \
+ lib/exec_shell.c \
+ lib/readutmp.c
+
+if LINUX
+libcommon_la_SOURCES += \
+ lib/linux_version.c \
+ lib/loopdev.c
+endif
+
+if !HAVE_LANGINFO
+libcommon_la_SOURCES += lib/langinfo.c
+endif
+
+if HAVE_CPU_SET_T
+libcommon_la_SOURCES += lib/cpuset.c
+endif
+
+dist_man_MANS += lib/terminal-colors.d.5
+
+check_PROGRAMS += \
+ test_at \
+ test_blkdev \
+ test_canonicalize \
+ test_colors \
+ test_fileutils \
+ test_ismounted \
+ test_mangle \
+ test_procutils \
+ test_randutils \
+ test_strutils \
+ test_ttyutils
+
+if LINUX
+if HAVE_CPU_SET_T
+check_PROGRAMS += test_cpuset
+endif
+check_PROGRAMS += \
+ test_sysfs \
+ test_pager
+endif
+
+test_ttyutils_SOURCES = lib/ttyutils.c
+test_ttyutils_CFLAGS = -DTEST_PROGRAM
+test_ttyutils_LDADD = libcommon.la
+
+test_blkdev_SOURCES = lib/blkdev.c
+test_blkdev_CFLAGS = -DTEST_PROGRAM_BLKDEV
+test_blkdev_LDADD = libcommon.la
+
+test_ismounted_SOURCES = lib/ismounted.c
+test_ismounted_CFLAGS = -DTEST_PROGRAM
+test_ismounted_LDADD = libcommon.la
+
+test_mangle_SOURCES = lib/mangle.c
+test_mangle_CFLAGS = -DTEST_PROGRAM
+
+test_at_SOURCES = lib/at.c
+test_at_CFLAGS = -DTEST_PROGRAM_AT
+
+test_strutils_SOURCES = lib/strutils.c
+test_strutils_CFLAGS = -DTEST_PROGRAM
+
+test_colors_SOURCES = lib/colors.c
+test_colors_CFLAGS = -DTEST_PROGRAM
+
+test_randutils_SOURCES = lib/randutils.c
+test_randutils_CFLAGS = -DTEST_PROGRAM
+
+test_procutils_SOURCES = lib/procutils.c lib/at.c
+test_procutils_CFLAGS = -DTEST_PROGRAM
+
+if LINUX
+test_cpuset_SOURCES = lib/cpuset.c
+test_cpuset_CFLAGS = -DTEST_PROGRAM
+
+test_sysfs_SOURCES = lib/sysfs.c
+test_sysfs_CFLAGS = -DTEST_PROGRAM_SYSFS
+test_sysfs_LDADD = libcommon.la
+
+test_pager_SOURCES = lib/pager.c
+test_pager_CFLAGS = -DTEST_PROGRAM
+endif
+
+test_fileutils_SOURCES = lib/fileutils.c
+test_fileutils_CFLAGS = -DTEST_PROGRAM
+
+test_canonicalize_SOURCES = lib/canonicalize.c
+test_canonicalize_CFLAGS = -DTEST_PROGRAM_CANONICALIZE
+
diff --git a/libblkid/lib/at.c b/libblkid/lib/at.c
new file mode 100644
index 000000000..f8bfe1399
--- /dev/null
+++ b/libblkid/lib/at.c
@@ -0,0 +1,143 @@
+/*
+ * Portable xxxat() functions.
+ *
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "at.h"
+#include "c.h"
+
+#ifdef HAVE_FSTATAT
+int fstat_at(int dir, const char *dirname __attribute__ ((__unused__)),
+ const char *filename, struct stat *st, int nofollow)
+{
+ return fstatat(dir, filename, st,
+ nofollow ? AT_SYMLINK_NOFOLLOW : 0);
+}
+#else
+int fstat_at(int dir, const char *dirname, const char *filename,
+ struct stat *st, int nofollow)
+{
+
+ if (*filename != '/') {
+ char path[PATH_MAX];
+ int len;
+
+ len = snprintf(path, sizeof(path), "%s/%s", dirname, filename);
+ if (len < 0 || len + 1 > sizeof(path))
+ return -1;
+
+ return nofollow ? lstat(path, st) : stat(path, st);
+ }
+
+ return nofollow ? lstat(filename, st) : stat(filename, st);
+}
+#endif
+
+#ifdef HAVE_FSTATAT
+int open_at(int dir, const char *dirname __attribute__ ((__unused__)),
+ const char *filename, int flags)
+{
+ return openat(dir, filename, flags);
+}
+#else
+int open_at(int dir, const char *dirname, const char *filename, int flags)
+{
+ if (*filename != '/') {
+ char path[PATH_MAX];
+ int len;
+
+ len = snprintf(path, sizeof(path), "%s/%s", dirname, filename);
+ if (len < 0 || len + 1 > sizeof(path))
+ return -1;
+
+ return open(path, flags);
+ }
+ return open(filename, flags);
+}
+#endif
+
+FILE *fopen_at(int dir, const char *dirname, const char *filename, int flags,
+ const char *mode)
+{
+ int fd = open_at(dir, dirname, filename, flags);
+
+ if (fd < 0)
+ return NULL;
+
+ return fdopen(fd, mode);
+}
+
+#ifdef HAVE_FSTATAT
+ssize_t readlink_at(int dir, const char *dirname __attribute__ ((__unused__)),
+ const char *pathname, char *buf, size_t bufsiz)
+{
+ return readlinkat(dir, pathname, buf, bufsiz);
+}
+#else
+ssize_t readlink_at(int dir, const char *dirname, const char *pathname,
+ char *buf, size_t bufsiz)
+{
+ if (*pathname != '/') {
+ char path[PATH_MAX];
+ int len;
+
+ len = snprintf(path, sizeof(path), "%s/%s", dirname, pathname);
+ if (len < 0 || len + 1 > sizeof(path))
+ return -1;
+
+ return readlink(path, buf, bufsiz);
+ }
+ return readlink(pathname, buf, bufsiz);
+}
+#endif
+
+#ifdef TEST_PROGRAM_AT
+#include <errno.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ DIR *dir;
+ struct dirent *d;
+ char *dirname;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <directory>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ dirname = argv[1];
+
+ dir = opendir(dirname);
+ if (!dir)
+ err(EXIT_FAILURE, "cannot open %s", dirname);
+
+ while ((d = readdir(dir))) {
+ struct stat st;
+ FILE *f;
+
+ printf("%32s ", d->d_name);
+
+ if (fstat_at(dirfd(dir), dirname, d->d_name, &st, 0) == 0)
+ printf("%16jd bytes ", st.st_size);
+ else
+ printf("%16s bytes ", "???");
+
+ f = fopen_at(dirfd(dir), dirname, d->d_name, O_RDONLY, "r");
+ printf(" %s\n", f ? "OK" : strerror(errno));
+ if (f)
+ fclose(f);
+ }
+ closedir(dir);
+ return EXIT_SUCCESS;
+}
+#endif
diff --git a/libblkid/lib/blkdev.c b/libblkid/lib/blkdev.c
new file mode 100644
index 000000000..a29352963
--- /dev/null
+++ b/libblkid/lib/blkdev.c
@@ -0,0 +1,378 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+
+#ifdef HAVE_SYS_DISKLABEL_H
+#include <sys/disklabel.h>
+#endif
+
+#ifdef HAVE_SYS_DISK_H
+#ifdef HAVE_SYS_QUEUE_H
+#include <sys/queue.h> /* for LIST_HEAD */
+#endif
+#include <sys/disk.h>
+#endif
+
+#ifdef __FreeBSD_kernel__
+#include <sys/disk.h>
+#endif
+
+#include "blkdev.h"
+#include "c.h"
+#include "linux_version.h"
+
+static long
+blkdev_valid_offset (int fd, off_t offset) {
+ char ch;
+
+ if (lseek (fd, offset, 0) < 0)
+ return 0;
+ if (read (fd, &ch, 1) < 1)
+ return 0;
+ return 1;
+}
+
+int is_blkdev(int fd)
+{
+ struct stat st;
+ return (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode));
+}
+
+off_t
+blkdev_find_size (int fd) {
+ uintmax_t high, low = 0;
+
+ for (high = 1024; blkdev_valid_offset (fd, high); ) {
+ if (high == UINTMAX_MAX)
+ return -1;
+
+ low = high;
+
+ if (high >= UINTMAX_MAX/2)
+ high = UINTMAX_MAX;
+ else
+ high *= 2;
+ }
+
+ while (low < high - 1)
+ {
+ uintmax_t mid = (low + high) / 2;
+
+ if (blkdev_valid_offset (fd, mid))
+ low = mid;
+ else
+ high = mid;
+ }
+ blkdev_valid_offset (fd, 0);
+ return (low + 1);
+}
+
+/* get size in bytes */
+int
+blkdev_get_size(int fd, unsigned long long *bytes)
+{
+#ifdef DKIOCGETBLOCKCOUNT
+ /* Apple Darwin */
+ if (ioctl(fd, DKIOCGETBLOCKCOUNT, bytes) >= 0) {
+ *bytes <<= 9;
+ return 0;
+ }
+#endif
+
+#ifdef BLKGETSIZE64
+ {
+#ifdef __linux__
+ int ver = get_linux_version();
+
+ /* kernels 2.4.15-2.4.17, had a broken BLKGETSIZE64 */
+ if (ver >= KERNEL_VERSION (2,6,0) ||
+ (ver >= KERNEL_VERSION (2,4,18) && ver < KERNEL_VERSION (2,5,0)))
+#endif
+ if (ioctl(fd, BLKGETSIZE64, bytes) >= 0)
+ return 0;
+ }
+#endif /* BLKGETSIZE64 */
+
+#ifdef BLKGETSIZE
+ {
+ unsigned long size;
+
+ if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+ *bytes = ((unsigned long long)size << 9);
+ return 0;
+ }
+ }
+
+#endif /* BLKGETSIZE */
+
+#ifdef DIOCGMEDIASIZE
+ /* FreeBSD */
+ if (ioctl(fd, DIOCGMEDIASIZE, bytes) >= 0)
+ return 0;
+#endif
+
+#ifdef FDGETPRM
+ {
+ struct floppy_struct this_floppy;
+
+ if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
+ *bytes = this_floppy.size << 9;
+ return 0;
+ }
+ }
+#endif /* FDGETPRM */
+
+#ifdef HAVE_SYS_DISKLABEL_H
+ {
+ /*
+ * This code works for FreeBSD 4.11 i386, except for the full device
+ * (such as /dev/ad0). It doesn't work properly for newer FreeBSD
+ * though. FreeBSD >= 5.0 should be covered by the DIOCGMEDIASIZE
+ * above however.
+ *
+ * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw,
+ * character) devices, so we need to check for S_ISCHR, too.
+ */
+ int part = -1;
+ struct disklabel lab;
+ struct partition *pp;
+ char ch;
+ struct stat st;
+
+ if ((fstat(fd, &st) >= 0) &&
+ (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)))
+ part = st.st_rdev & 7;
+
+ if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
+ pp = &lab.d_partitions[part];
+ if (pp->p_size) {
+ *bytes = pp->p_size << 9;
+ return 0;
+ }
+ }
+ }
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+ {
+ struct stat st;
+
+ if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
+ *bytes = st.st_size;
+ return 0;
+ }
+ if (!S_ISBLK(st.st_mode))
+ return -1;
+ }
+
+ *bytes = blkdev_find_size(fd);
+ return 0;
+}
+
+/* get 512-byte sector count */
+int
+blkdev_get_sectors(int fd, unsigned long long *sectors)
+{
+ unsigned long long bytes;
+
+ if (blkdev_get_size(fd, &bytes) == 0) {
+ *sectors = (bytes >> 9);
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Get logical sector size.
+ *
+ * This is the smallest unit the storage device can
+ * address. It is typically 512 bytes.
+ */
+int blkdev_get_sector_size(int fd, int *sector_size)
+{
+#ifdef BLKSSZGET
+ if (ioctl(fd, BLKSSZGET, sector_size) >= 0)
+ return 0;
+ return -1;
+#else
+ *sector_size = DEFAULT_SECTOR_SIZE;
+ return 0;
+#endif
+}
+
+/*
+ * Get physical block device size. The BLKPBSZGET is supported since Linux
+ * 2.6.32. For old kernels is probably the best to assume that physical sector
+ * size is the same as logical sector size.
+ *
+ * Example:
+ *
+ * rc = blkdev_get_physector_size(fd, &physec);
+ * if (rc || physec == 0) {
+ * rc = blkdev_get_sector_size(fd, &physec);
+ * if (rc)
+ * physec = DEFAULT_SECTOR_SIZE;
+ * }
+ */
+int blkdev_get_physector_size(int fd, int *sector_size)
+{
+#ifdef BLKPBSZGET
+ if (ioctl(fd, BLKPBSZGET, &sector_size) >= 0)
+ return 0;
+ return -1;
+#else
+ *sector_size = DEFAULT_SECTOR_SIZE;
+ return 0;
+#endif
+}
+
+/*
+ * Return the alignment status of a device
+ */
+int blkdev_is_misaligned(int fd)
+{
+#ifdef BLKALIGNOFF
+ int aligned;
+
+ if (ioctl(fd, BLKALIGNOFF, &aligned) < 0)
+ return 0; /* probably kernel < 2.6.32 */
+ /*
+ * Note that kernel returns -1 as alignement offset if no compatible
+ * sizes and alignments exist for stacked devices
+ */
+ return aligned != 0 ? 1 : 0;
+#else
+ return 0;
+#endif
+}
+
+int blkdev_is_cdrom(int fd)
+{
+#ifdef CDROM_GET_CAPABILITY
+ int ret;
+
+ if ((ret = ioctl(fd, CDROM_GET_CAPABILITY, NULL)) < 0)
+ return 0;
+ else
+ return ret;
+#else
+ return 0;
+#endif
+}
+
+/*
+ * Get kernel's interpretation of the device's geometry.
+ *
+ * Returns the heads and sectors - but not cylinders
+ * as it's truncated for disks with more than 65535 tracks.
+ *
+ * Note that this is deprecated in favor of LBA addressing.
+ */
+int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s)
+{
+#ifdef HDIO_GETGEO
+ struct hd_geometry geometry;
+
+ if (ioctl(fd, HDIO_GETGEO, &geometry) == 0) {
+ *h = geometry.heads;
+ *s = geometry.sectors;
+ return 0;
+ }
+#else
+ *h = 0;
+ *s = 0;
+#endif
+ return -1;
+}
+
+/*
+ * Convert scsi type to human readable string.
+ */
+const char *blkdev_scsi_type_to_name(int type)
+{
+ switch (type) {
+ case SCSI_TYPE_DISK:
+ return "disk";
+ case SCSI_TYPE_TAPE:
+ return "tape";
+ case SCSI_TYPE_PRINTER:
+ return "printer";
+ case SCSI_TYPE_PROCESSOR:
+ return "processor";
+ case SCSI_TYPE_WORM:
+ return "worm";
+ case SCSI_TYPE_ROM:
+ return "rom";
+ case SCSI_TYPE_SCANNER:
+ return "scanner";
+ case SCSI_TYPE_MOD:
+ return "mo-disk";
+ case SCSI_TYPE_MEDIUM_CHANGER:
+ return "changer";
+ case SCSI_TYPE_COMM:
+ return "comm";
+ case SCSI_TYPE_RAID:
+ return "raid";
+ case SCSI_TYPE_ENCLOSURE:
+ return "enclosure";
+ case SCSI_TYPE_RBC:
+ return "rbc";
+ case SCSI_TYPE_OSD:
+ return "osd";
+ case SCSI_TYPE_NO_LUN:
+ return "no-lun";
+ default:
+ break;
+ }
+ return NULL;
+}
+
+#ifdef TEST_PROGRAM_BLKDEV
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+int
+main(int argc, char **argv)
+{
+ unsigned long long bytes;
+ unsigned long long sectors;
+ int sector_size, phy_sector_size;
+ int fd;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s device\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if ((fd = open(argv[1], O_RDONLY|O_CLOEXEC)) < 0)
+ err(EXIT_FAILURE, "open %s failed", argv[1]);
+
+ if (blkdev_get_size(fd, &bytes) < 0)
+ err(EXIT_FAILURE, "blkdev_get_size() failed");
+ if (blkdev_get_sectors(fd, &sectors) < 0)
+ err(EXIT_FAILURE, "blkdev_get_sectors() failed");
+ if (blkdev_get_sector_size(fd, &sector_size) < 0)
+ err(EXIT_FAILURE, "blkdev_get_sector_size() failed");
+ if (blkdev_get_physector_size(fd, &phy_sector_size) < 0)
+ err(EXIT_FAILURE, "blkdev_get_physector_size() failed");
+
+ printf(" bytes: %llu\n", bytes);
+ printf(" sectors: %llu\n", sectors);
+ printf(" sector size: %d\n", sector_size);
+ printf("phy-sector size: %d\n", phy_sector_size);
+
+ return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/lib/canonicalize.c b/libblkid/lib/canonicalize.c
new file mode 100644
index 000000000..303703b7c
--- /dev/null
+++ b/libblkid/lib/canonicalize.c
@@ -0,0 +1,145 @@
+/*
+ * canonicalize.c -- canonicalize pathname by removing symlinks
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Copyright (C) 2009-2013 Karel Zak <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "canonicalize.h"
+
+/*
+ * Converts private "dm-N" names to "/dev/mapper/<name>"
+ *
+ * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs
+ * provides the real DM device names in /sys/block/<ptname>/dm/name
+ */
+char *canonicalize_dm_name(const char *ptname)
+{
+ FILE *f;
+ size_t sz;
+ char path[256], name[256], *res = NULL;
+
+ if (!ptname || !*ptname)
+ return NULL;
+
+ snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname);
+ if (!(f = fopen(path, "r")))
+ return NULL;
+
+ /* read "<name>\n" from sysfs */
+ if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) {
+ name[sz - 1] = '\0';
+ snprintf(path, sizeof(path), "/dev/mapper/%s", name);
+
+ if (access(path, F_OK) == 0)
+ res = strdup(path);
+ }
+ fclose(f);
+ return res;
+}
+
+static int is_dm_devname(char *canonical, char **name)
+{
+ struct stat sb;
+ char *p = strrchr(canonical, '/');
+
+ *name = NULL;
+
+ if (!p
+ || strncmp(p, "/dm-", 4) != 0
+ || !isdigit(*(p + 4))
+ || stat(canonical, &sb) != 0
+ || !S_ISBLK(sb.st_mode))
+ return 0;
+
+ *name = p + 1;
+ return 1;
+}
+
+char *canonicalize_path(const char *path)
+{
+ char *canonical, *dmname;
+
+ if (!path || !*path)
+ return NULL;
+
+ canonical = realpath(path, NULL);
+ if (!canonical)
+ return strdup(path);
+
+ if (is_dm_devname(canonical, &dmname)) {
+ char *dm = canonicalize_dm_name(dmname);
+ if (dm) {
+ free(canonical);
+ return dm;
+ }
+ }
+
+ return canonical;
+}
+
+char *canonicalize_path_restricted(const char *path)
+{
+ char *canonical, *dmname;
+ int errsv;
+ uid_t euid;
+ gid_t egid;
+
+ if (!path || !*path)
+ return NULL;
+
+ euid = geteuid();
+ egid = getegid();
+
+ /* drop permissions */
+ if (setegid(getgid()) < 0 || seteuid(getuid()) < 0)
+ return NULL;
+
+ errsv = errno = 0;
+
+ canonical = realpath(path, NULL);
+ if (!canonical)
+ errsv = errno;
+ else if (is_dm_devname(canonical, &dmname)) {
+ char *dm = canonicalize_dm_name(dmname);
+ if (dm) {
+ free(canonical);
+ canonical = dm;
+ }
+ }
+
+ /* restore */
+ if (setegid(egid) < 0 || seteuid(euid) < 0) {
+ free(canonical);
+ return NULL;
+ }
+
+ errno = errsv;
+ return canonical;
+}
+
+
+#ifdef TEST_PROGRAM_CANONICALIZE
+int main(int argc, char **argv)
+{
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <device>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ fprintf(stdout, "orig: %s\n", argv[1]);
+ fprintf(stdout, "real: %s\n", canonicalize_path(argv[1]));
+
+ exit(EXIT_SUCCESS);
+}
+#endif
diff --git a/libblkid/lib/colors.c b/libblkid/lib/colors.c
new file mode 100644
index 000000000..6f79ac4a8
--- /dev/null
+++ b/libblkid/lib/colors.c
@@ -0,0 +1,877 @@
+/*
+ * Copyright (C) 2012 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2012-2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <assert.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#include "c.h"
+#include "colors.h"
+#include "pathnames.h"
+#include "strutils.h"
+
+#include "debug.h"
+
+/*
+ * terminal-colors.d debug stuff
+ */
+UL_DEBUG_DEFINE_MASK(termcolors);
+UL_DEBUG_DEFINE_MASKNAMES(termcolors) = UL_DEBUG_EMPTY_MASKNAMES;
+
+#define TERMCOLORS_DEBUG_INIT (1 << 1)
+#define TERMCOLORS_DEBUG_CONF (1 << 2)
+#define TERMCOLORS_DEBUG_SCHEME (1 << 3)
+#define TERMCOLORS_DEBUG_ALL 0xFFFF
+
+#define DBG(m, x) __UL_DBG(termcolors, TERMCOLORS_DEBUG_, m, x)
+#define ON_DBG(m, x) __UL_DBG_CALL(termcolors, TERMCOLORS_DEBUG_, m, x)
+
+/*
+ * terminal-colors.d file types
+ */
+enum {
+ UL_COLORFILE_DISABLE, /* .disable */
+ UL_COLORFILE_ENABLE, /* .enable */
+ UL_COLORFILE_SCHEME, /* .scheme */
+
+ __UL_COLORFILE_COUNT
+};
+
+struct ul_color_scheme {
+ char *name;
+ char *seq;
+};
+
+/*
+ * Global colors control struct
+ *
+ * The terminal-colors.d/ evaluation is based on "scores":
+ *
+ * filename score
+ * ---------------------------------------
+ * type 1
+ * @termname.type 10 + 1
+ * utilname.type 20 + 1
+ * utilname@termname.type 20 + 10 + 1
+ *
+ * the match with higher score wins. The score is per type.
+ */
+struct ul_color_ctl {
+ const char *utilname; /* util name */
+ const char *termname; /* terminal name ($TERM) */
+
+ char *sfile; /* path to scheme */
+
+ struct ul_color_scheme *schemes; /* array with color schemes */
+ size_t nschemes; /* number of the items */
+ size_t schemes_sz; /* number of the allocated items */
+
+ int mode; /* UL_COLORMODE_* */
+ unsigned int has_colors : 1, /* based on mode and scores[] */
+ disabled : 1, /* disable colors */
+ cs_configured : 1, /* color schemes read */
+ configured : 1; /* terminal-colors.d parsed */
+
+ int scores[__UL_COLORFILE_COUNT]; /* the best match */
+};
+
+/*
+ * Control struct, globally shared.
+ */
+static struct ul_color_ctl ul_colors;
+
+static void colors_free_schemes(struct ul_color_ctl *cc);
+static int colors_read_schemes(struct ul_color_ctl *cc);
+
+/*
+ * qsort/bsearch buddy
+ */
+static int cmp_scheme_name(const void *a0, const void *b0)
+{
+ struct ul_color_scheme *a = (struct ul_color_scheme *) a0,
+ *b = (struct ul_color_scheme *) b0;
+ return strcmp(a->name, b->name);
+}
+
+/*
+ * Maintains human readable color names
+ */
+const char *color_sequence_from_colorname(const char *str)
+{
+ static const struct ul_color_scheme basic_schemes[] = {
+ { "black", UL_COLOR_BLACK },
+ { "blue", UL_COLOR_BLUE },
+ { "brown", UL_COLOR_BROWN },
+ { "cyan", UL_COLOR_CYAN },
+ { "darkgray", UL_COLOR_DARK_GRAY },
+ { "gray", UL_COLOR_GRAY },
+ { "green", UL_COLOR_GREEN },
+ { "lightblue", UL_COLOR_BOLD_BLUE },
+ { "lightcyan", UL_COLOR_BOLD_CYAN },
+ { "lightgray,", UL_COLOR_GRAY },
+ { "lightgreen", UL_COLOR_BOLD_GREEN },
+ { "lightmagenta", UL_COLOR_BOLD_MAGENTA },
+ { "lightred", UL_COLOR_BOLD_RED },
+ { "magenta", UL_COLOR_MAGENTA },
+ { "red", UL_COLOR_RED },
+ { "yellow", UL_COLOR_BOLD_YELLOW },
+ };
+ struct ul_color_scheme key = { .name = (char *) str }, *res;
+
+ if (!str)
+ return NULL;
+
+ res = bsearch(&key, basic_schemes, ARRAY_SIZE(basic_schemes),
+ sizeof(struct ul_color_scheme),
+ cmp_scheme_name);
+ return res ? res->seq : NULL;
+}
+
+
+/*
+ * Resets control struct (note that we don't allocate the struct)
+ */
+static void colors_reset(struct ul_color_ctl *cc)
+{
+ if (!cc)
+ return;
+
+ colors_free_schemes(cc);
+
+ free(cc->sfile);
+
+ cc->sfile = NULL;
+ cc->utilname = NULL;
+ cc->termname = NULL;
+ cc->mode = UL_COLORMODE_UNDEF;
+
+ memset(cc->scores, 0, sizeof(cc->scores));
+}
+
+static void colors_debug(struct ul_color_ctl *cc)
+{
+ size_t i;
+
+ if (!cc)
+ return;
+
+ printf("Colors:\n");
+ printf("\tutilname = '%s'\n", cc->utilname);
+ printf("\ttermname = '%s'\n", cc->termname);
+ printf("\tscheme file = '%s'\n", cc->sfile);
+ printf("\tmode = %s\n",
+ cc->mode == UL_COLORMODE_UNDEF ? "undefined" :
+ cc->mode == UL_COLORMODE_AUTO ? "auto" :
+ cc->mode == UL_COLORMODE_NEVER ? "never" :
+ cc->mode == UL_COLORMODE_ALWAYS ? "always" : "???");
+ printf("\thas_colors = %d\n", cc->has_colors);
+ printf("\tdisabled = %d\n", cc->disabled);
+ printf("\tconfigured = %d\n", cc->configured);
+ printf("\tcs configured = %d\n", cc->cs_configured);
+
+ fputc('\n', stdout);
+
+ for (i = 0; i < ARRAY_SIZE(cc->scores); i++)
+ printf("\tscore %s = %d\n",
+ i == UL_COLORFILE_DISABLE ? "disable" :
+ i == UL_COLORFILE_ENABLE ? "enable" :
+ i == UL_COLORFILE_SCHEME ? "scheme" : "???",
+ cc->scores[i]);
+
+ fputc('\n', stdout);
+
+ for (i = 0; i < cc->nschemes; i++) {
+ printf("\tscheme #%02zu ", i);
+ color_scheme_enable(cc->schemes[i].name, NULL);
+ fputs(cc->schemes[i].name, stdout);
+ color_disable();
+ fputc('\n', stdout);
+ }
+ fputc('\n', stdout);
+}
+
+/*
+ * Parses [[<utilname>][@<termname>].]<type>
+ */
+static int filename_to_tokens(const char *str,
+ const char **name, size_t *namesz,
+ const char **term, size_t *termsz,
+ int *filetype)
+{
+ const char *type_start, *term_start, *p;
+
+ if (!str || !*str || *str == '.' || strlen(str) > PATH_MAX)
+ return -EINVAL;
+
+ /* parse .type */
+ p = strrchr(str, '.');
+ type_start = p ? p + 1 : str;
+
+ if (strcmp(type_start, "disable") == 0)
+ *filetype = UL_COLORFILE_DISABLE;
+ else if (strcmp(type_start, "enable") == 0)
+ *filetype = UL_COLORFILE_ENABLE;
+ else if (strcmp(type_start, "scheme") == 0)
+ *filetype = UL_COLORFILE_SCHEME;
+ else {
+ DBG(CONF, ul_debug("unknown type '%s'", type_start));
+ return 1; /* unknown type */
+ }
+
+ if (type_start == str)
+ return 0; /* "type" only */
+
+ /* parse @termname */
+ p = strchr(str, '@');
+ term_start = p ? p + 1 : NULL;
+ if (term_start) {
+ *term = term_start;
+ *termsz = type_start - term_start - 1;
+ if (term_start - 1 == str)
+ return 0; /* "@termname.type" */
+ }
+
+ /* parse utilname */
+ p = term_start ? term_start : type_start;
+ *name = str;
+ *namesz = p - str - 1;
+
+ return 0;
+}
+
+/*
+ * Scans @dirname and select the best matches for UL_COLORFILE_* types.
+ * The result is stored to cc->scores. The path to the best "scheme"
+ * file is stored to cc->scheme.
+ */
+static int colors_readdir(struct ul_color_ctl *cc, const char *dirname)
+{
+ DIR *dir;
+ int rc = 0;
+ struct dirent *d;
+ char sfile[PATH_MAX] = { '\0' };
+ size_t namesz, termsz;
+
+ if (!dirname || !cc || !cc->utilname || !*cc->utilname)
+ return -EINVAL;
+
+ DBG(CONF, ul_debug("reading dir: '%s'", dirname));
+
+ dir = opendir(dirname);
+ if (!dir)
+ return -errno;
+
+ namesz = strlen(cc->utilname);
+ termsz = cc->termname ? strlen(cc->termname) : 0;
+
+ while ((d = readdir(dir))) {
+ int type, score = 1;
+ const char *tk_name = NULL, *tk_term = NULL;
+ size_t tk_namesz = 0, tk_termsz = 0;
+
+ if (*d->d_name == '.')
+ continue;
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (d->d_type != DT_UNKNOWN && d->d_type != DT_LNK &&
+ d->d_type != DT_REG)
+ continue;
+#endif
+ if (filename_to_tokens(d->d_name,
+ &tk_name, &tk_namesz,
+ &tk_term, &tk_termsz, &type) != 0)
+ continue;
+
+ /* count teoretical score before we check names to avoid
+ * unnecessary strcmp() */
+ if (tk_name)
+ score += 20;
+ if (tk_term)
+ score += 10;
+
+ DBG(CONF, ul_debug("item '%s': score=%d "
+ "[cur: %d, name(%zu): %s, term(%zu): %s]",
+ d->d_name, score, cc->scores[type],
+ tk_namesz, tk_name,
+ tk_termsz, tk_term));
+
+
+ if (score < cc->scores[type])
+ continue;
+
+ /* filter out by names */
+ if (tk_namesz && (tk_namesz != namesz ||
+ strncmp(tk_name, cc->utilname, namesz) != 0))
+ continue;
+
+ if (tk_termsz && (termsz == 0 || tk_termsz != termsz ||
+ strncmp(tk_term, cc->termname, termsz) != 0))
+ continue;
+
+ DBG(CONF, ul_debug("setting '%s' from %d -to-> %d",
+ type == UL_COLORFILE_SCHEME ? "scheme" :
+ type == UL_COLORFILE_DISABLE ? "disable" :
+ type == UL_COLORFILE_ENABLE ? "enable" : "???",
+ cc->scores[type], score));
+ cc->scores[type] = score;
+ if (type == UL_COLORFILE_SCHEME)
+ strncpy(sfile, d->d_name, sizeof(sfile));
+ }
+
+ if (*sfile) {
+ sfile[sizeof(sfile) - 1] = '\0';
+ if (asprintf(&cc->sfile, "%s/%s", dirname, sfile) <= 0)
+ rc = -ENOMEM;
+ }
+
+ closedir(dir);
+ return rc;
+}
+
+/* atexit() wrapper */
+static void colors_deinit(void)
+{
+ colors_reset(&ul_colors);
+}
+
+/*
+ * Returns path to $XDG_CONFIG_HOME/terminal-colors.d
+ */
+static char *colors_get_homedir(char *buf, size_t bufsz)
+{
+ char *p = getenv("XDG_CONFIG_HOME");
+
+ if (p) {
+ snprintf(buf, bufsz, "%s/" _PATH_TERMCOLORS_DIRNAME, p);
+ return buf;
+ }
+
+ p = getenv("HOME");
+ if (p) {
+ snprintf(buf, bufsz, "%s/.config/" _PATH_TERMCOLORS_DIRNAME, p);
+ return buf;
+ }
+
+ return NULL;
+}
+
+/* canonicalize sequence */
+static int cn_sequence(const char *str, char **seq)
+{
+ char *in, *out;
+
+ if (!str)
+ return -EINVAL;
+
+ *seq = NULL;
+
+ /* convert logical names like "red" to the real sequence */
+ if (*str != '\\' && isalpha(*str)) {
+ const char *s = color_sequence_from_colorname(str);
+ *seq = strdup(s ? s : str);
+
+ return *seq ? 0 : -ENOMEM;
+ }
+
+ /* convert xx;yy sequences to "\033[xx;yy" */
+ if (asprintf(seq, "\033[%sm", str) < 1)
+ return -ENOMEM;
+
+ for (in = *seq, out = *seq; in && *in; in++) {
+ if (*in != '\\') {
+ *out++ = *in;
+ continue;
+ }
+ switch(*(in + 1)) {
+ case 'a':
+ *out++ = '\a'; /* Bell */
+ break;
+ case 'b':
+ *out++ = '\b'; /* Backspace */
+ break;
+ case 'e':
+ *out++ = '\033'; /* Escape */
+ break;
+ case 'f':
+ *out++ = '\f'; /* Form Feed */
+ break;
+ case 'n':
+ *out++ = '\n'; /* Newline */
+ break;
+ case 'r':
+ *out++ = '\r'; /* Carriage Return */
+ break;
+ case 't':
+ *out++ = '\t'; /* Tab */
+ break;
+ case 'v':
+ *out++ = '\v'; /* Vertical Tab */
+ break;
+ case '\\':
+ *out++ = '\\'; /* Backslash */
+ break;
+ case '_':
+ *out++ = ' '; /* Space */
+ break;
+ case '#':
+ *out++ = '#'; /* Hash mark */
+ break;
+ case '?':
+ *out++ = '?'; /* Qestion mark */
+ break;
+ default:
+ *out++ = *in;
+ *out++ = *(in + 1);
+ break;
+ }
+ in++;
+ }
+ *out = '\0';
+
+ return 0;
+}
+
+
+/*
+ * Adds one color sequence to array with color scheme.
+ * When returning success (0) this function takes ownership of
+ * @seq and @name, which have to be allocated strings.
+ */
+static int colors_add_scheme(struct ul_color_ctl *cc,
+ char *name,
+ char *seq0)
+{
+ struct ul_color_scheme *cs = NULL;
+ char *seq = NULL;
+ int rc;
+
+ if (!cc || !name || !*name || !seq0 || !*seq0)
+ return -EINVAL;
+
+ DBG(SCHEME, ul_debug("add '%s'", name));
+
+ rc = cn_sequence(seq0, &seq);
+ if (rc)
+ return rc;
+
+ rc = -ENOMEM;
+
+ /* convert logical name (e.g. "red") to real ESC code */
+ if (isalpha(*seq)) {
+ const char *s = color_sequence_from_colorname(seq);
+ char *p;
+
+ if (!s) {
+ DBG(SCHEME, ul_debug("unknown logical name: %s", seq));
+ rc = -EINVAL;
+ goto err;
+ }
+
+ p = strdup(s);
+ if (!p)
+ goto err;
+ free(seq);
+ seq = p;
+ }
+
+ /* enlarge the array */
+ if (cc->nschemes == cc->schemes_sz) {
+ void *tmp = realloc(cc->schemes, (cc->nschemes + 10)
+ * sizeof(struct ul_color_scheme));
+ if (!tmp)
+ goto err;
+ cc->schemes = tmp;
+ cc->schemes_sz = cc->nschemes + 10;
+ }
+
+ /* add a new item */
+ cs = &cc->schemes[cc->nschemes];
+ cs->seq = seq;
+ cs->name = strdup(name);
+ if (!cs->name)
+ goto err;
+
+ cc->nschemes++;
+ return 0;
+err:
+ if (cs) {
+ free(cs->seq);
+ free(cs->name);
+ cs->seq = cs->name = NULL;
+ } else
+ free(seq);
+ return rc;
+}
+
+/*
+ * Deallocates all regards to color schemes
+ */
+static void colors_free_schemes(struct ul_color_ctl *cc)
+{
+ size_t i;
+
+ DBG(SCHEME, ul_debug("free scheme"));
+
+ for (i = 0; i < cc->nschemes; i++) {
+ free(cc->schemes[i].name);
+ free(cc->schemes[i].seq);
+ }
+
+ free(cc->schemes);
+ cc->schemes = NULL;
+ cc->nschemes = 0;
+ cc->schemes_sz = 0;
+}
+
+/*
+ * The scheme configuration has to be sorted for bsearch
+ */
+static void colors_sort_schemes(struct ul_color_ctl *cc)
+{
+ if (!cc->nschemes)
+ return;
+
+ DBG(SCHEME, ul_debug("sort scheme"));
+
+ qsort(cc->schemes, cc->nschemes,
+ sizeof(struct ul_color_scheme), cmp_scheme_name);
+}
+
+/*
+ * Returns just one color scheme
+ */
+static struct ul_color_scheme *colors_get_scheme(struct ul_color_ctl *cc,
+ const char *name)
+{
+ struct ul_color_scheme key = { .name = (char *) name}, *res;
+
+ if (!cc || !name || !*name)
+ return NULL;
+
+ if (!cc->cs_configured) {
+ int rc = colors_read_schemes(cc);
+ if (rc)
+ return NULL;
+ }
+ if (!cc->nschemes)
+ return NULL;
+
+ DBG(SCHEME, ul_debug("search '%s'", name));
+
+ res = bsearch(&key, cc->schemes, cc->nschemes,
+ sizeof(struct ul_color_scheme),
+ cmp_scheme_name);
+
+ return res && res->seq ? res : NULL;
+}
+
+/*
+ * Parses filenames in terminal-colors.d
+ */
+static int colors_read_configuration(struct ul_color_ctl *cc)
+{
+ int rc = -ENOENT;
+ char *dirname, buf[PATH_MAX];
+
+ cc->termname = getenv("TERM");
+
+ dirname = colors_get_homedir(buf, sizeof(buf));
+ if (dirname)
+ rc = colors_readdir(cc, dirname); /* ~/.config */
+ if (rc == -EPERM || rc == -EACCES || rc == -ENOENT)
+ rc = colors_readdir(cc, _PATH_TERMCOLORS_DIR); /* /etc */
+
+ cc->configured = 1;
+ return rc;
+}
+
+/*
+ * Reads terminal-colors.d/ scheme file into array schemes
+ */
+static int colors_read_schemes(struct ul_color_ctl *cc)
+{
+ int rc = 0;
+ FILE *f = NULL;
+ char buf[BUFSIZ],
+ cn[129], seq[129];
+
+ if (!cc->configured)
+ rc = colors_read_configuration(cc);
+
+ cc->cs_configured = 1;
+
+ if (rc || !cc->sfile)
+ goto done;
+
+ DBG(SCHEME, ul_debug("reading file '%s'", cc->sfile));
+
+ f = fopen(cc->sfile, "r");
+ if (!f) {
+ rc = -errno;
+ goto done;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ char *p = strchr(buf, '\n');
+
+ if (!p) {
+ if (feof(f))
+ p = strchr(buf, '\0');
+ else {
+ rc = -errno;
+ goto done;
+ }
+ }
+ *p = '\0';
+ p = (char *) skip_blank(buf);
+ if (*p == '\0' || *p == '#')
+ continue;
+
+ rc = sscanf(p, "%128[^ ] %128[^\n ]", cn, seq);
+ if (rc == 2 && *cn && *seq) {
+ rc = colors_add_scheme(cc, cn, seq); /* set rc=0 on success */
+ if (rc)
+ goto done;
+ }
+ }
+ rc = 0;
+
+done:
+ if (f)
+ fclose(f);
+ colors_sort_schemes(cc);
+
+ return rc;
+}
+
+
+static void termcolors_init_debug(void)
+{
+ __UL_INIT_DEBUG(termcolors, TERMCOLORS_DEBUG_, 0, TERMINAL_COLORS_DEBUG);
+}
+
+/**
+ * colors_init:
+ * @mode: UL_COLORMODE_*
+ * @name: util argv[0]
+ *
+ * Initialize private color control struct and initialize the colors
+ * status. The color schemes are parsed on demand by colors_get_scheme().
+ *
+ * Returns: >0 on success.
+ */
+int colors_init(int mode, const char *name)
+{
+ int atty = -1;
+ struct ul_color_ctl *cc = &ul_colors;
+
+ cc->utilname = name;
+ cc->mode = mode;
+
+ termcolors_init_debug();
+
+ if (mode == UL_COLORMODE_UNDEF && (atty = isatty(STDOUT_FILENO))) {
+ int rc = colors_read_configuration(cc);
+ if (rc)
+ cc->mode = UL_COLORMODE_AUTO;
+ else {
+
+ /* evaluate scores */
+ if (cc->scores[UL_COLORFILE_DISABLE] >
+ cc->scores[UL_COLORFILE_ENABLE])
+ cc->mode = UL_COLORMODE_NEVER;
+ else
+ cc->mode = UL_COLORMODE_AUTO;
+
+ atexit(colors_deinit);
+ }
+ }
+
+ switch (cc->mode) {
+ case UL_COLORMODE_AUTO:
+ cc->has_colors = atty == -1 ? isatty(STDOUT_FILENO) : atty;
+ break;
+ case UL_COLORMODE_ALWAYS:
+ cc->has_colors = 1;
+ break;
+ case UL_COLORMODE_NEVER:
+ default:
+ cc->has_colors = 0;
+ }
+
+ ON_DBG(CONF, colors_debug(cc));
+
+ return cc->has_colors;
+}
+
+/*
+ * Temporary disable colors (this setting is independent on terminal-colors.d/)
+ */
+void colors_off(void)
+{
+ ul_colors.disabled = 1;
+}
+
+/*
+ * Enable colors
+ */
+void colors_on(void)
+{
+ ul_colors.disabled = 0;
+}
+
+/*
+ * Is terminal-colors.d/ configured to use colors?
+ */
+int colors_wanted(void)
+{
+ return ul_colors.has_colors;
+}
+
+/*
+ * Enable @seq color
+ */
+void color_fenable(const char *seq, FILE *f)
+{
+ if (!ul_colors.disabled && ul_colors.has_colors && seq)
+ fputs(seq, f);
+}
+
+/*
+ * Returns escape sequence by logical @name, if undefined then returns @dflt.
+ */
+const char *color_scheme_get_sequence(const char *name, const char *dflt)
+{
+ struct ul_color_scheme *cs;
+
+ if (ul_colors.disabled || !ul_colors.has_colors)
+ return NULL;
+
+ cs = colors_get_scheme(&ul_colors, name);
+ return cs && cs->seq ? cs->seq : dflt;
+}
+
+/*
+ * Enable color by logical @name, if undefined enable @dflt.
+ */
+void color_scheme_fenable(const char *name, const char *dflt, FILE *f)
+{
+ const char *seq = color_scheme_get_sequence(name, dflt);
+
+ if (!seq)
+ return;
+ color_fenable(seq, f);
+}
+
+
+/*
+ * Disable previously enabled color
+ */
+void color_fdisable(FILE *f)
+{
+ if (!ul_colors.disabled && ul_colors.has_colors)
+ fputs(UL_COLOR_RESET, f);
+}
+
+/*
+ * Parses @str to return UL_COLORMODE_*
+ */
+int colormode_from_string(const char *str)
+{
+ size_t i;
+ static const char *modes[] = {
+ [UL_COLORMODE_AUTO] = "auto",
+ [UL_COLORMODE_NEVER] = "never",
+ [UL_COLORMODE_ALWAYS] = "always",
+ [UL_COLORMODE_UNDEF] = ""
+ };
+
+ if (!str || !*str)
+ return -EINVAL;
+
+ assert(ARRAY_SIZE(modes) == __UL_NCOLORMODES);
+
+ for (i = 0; i < ARRAY_SIZE(modes); i++) {
+ if (strcasecmp(str, modes[i]) == 0)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Parses @str and exit(EXIT_FAILURE) on error
+ */
+int colormode_or_err(const char *str, const char *errmsg)
+{
+ const char *p = str && *str == '=' ? str + 1 : str;
+ int colormode;
+
+ colormode = colormode_from_string(p);
+ if (colormode < 0)
+ errx(EXIT_FAILURE, "%s: '%s'", errmsg, p);
+
+ return colormode;
+}
+
+#ifdef TEST_PROGRAM
+# include <getopt.h>
+int main(int argc, char *argv[])
+{
+ static const struct option longopts[] = {
+ { "mode", required_argument, 0, 'm' },
+ { "color", required_argument, 0, 'c' },
+ { "color-scheme", required_argument, 0, 'C' },
+ { "name", required_argument, 0, 'n' },
+ { NULL, 0, 0, 0 }
+ };
+ int c, mode = UL_COLORMODE_UNDEF; /* default */
+ const char *color = "red", *name = NULL, *color_scheme = NULL;
+ const char *seq = NULL;
+
+ while ((c = getopt_long(argc, argv, "C:c:m:n:", longopts, NULL)) != -1) {
+ switch (c) {
+ case 'c':
+ color = optarg;
+ break;
+ case 'C':
+ color_scheme = optarg;
+ break;
+ case 'm':
+ mode = colormode_or_err(optarg, "unsupported color mode");
+ break;
+ case 'n':
+ name = optarg;
+ break;
+ default:
+ fprintf(stderr, "usage: %s [options]\n"
+ " -m, --mode <auto|never|always> default is undefined\n"
+ " -c, --color <red|blue|...> color for the test message\n"
+ " -C, --color-scheme <name> color for the test message\n"
+ " -n, --name <utilname> util name\n",
+ program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+ }
+
+ colors_init(mode, name ? name : program_invocation_short_name);
+
+ seq = color_sequence_from_colorname(color);
+
+ if (color_scheme)
+ color_scheme_enable(color_scheme, seq);
+ else
+ color_enable(seq);
+ printf("Hello World!");
+ color_disable();
+ fputc('\n', stdout);
+
+ return EXIT_SUCCESS;
+}
+#endif
+
diff --git a/libblkid/lib/cpuset.c b/libblkid/lib/cpuset.c
new file mode 100644
index 000000000..d715720e3
--- /dev/null
+++ b/libblkid/lib/cpuset.c
@@ -0,0 +1,403 @@
+/*
+ * Terminology:
+ *
+ * cpuset - (libc) cpu_set_t data structure represents set of CPUs
+ * cpumask - string with hex mask (e.g. "0x00000001")
+ * cpulist - string with CPU ranges (e.g. "0-3,5,7,8")
+ *
+ * Based on code from taskset.c and Linux kernel.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sched.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/syscall.h>
+
+#include "cpuset.h"
+#include "c.h"
+
+static inline int val_to_char(int v)
+{
+ if (v >= 0 && v < 10)
+ return '0' + v;
+ else if (v >= 10 && v < 16)
+ return ('a' - 10) + v;
+ else
+ return -1;
+}
+
+static inline int char_to_val(int c)
+{
+ int cl;
+
+ cl = tolower(c);
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (cl >= 'a' && cl <= 'f')
+ return cl + (10 - 'a');
+ else
+ return -1;
+}
+
+static const char *nexttoken(const char *q, int sep)
+{
+ if (q)
+ q = strchr(q, sep);
+ if (q)
+ q++;
+ return q;
+}
+
+/*
+ * Number of bits in a CPU bitmask on current system
+ */
+int get_max_number_of_cpus(void)
+{
+#ifdef SYS_sched_getaffinity
+ int n, cpus = 2048;
+ size_t setsize;
+ cpu_set_t *set = cpuset_alloc(cpus, &setsize, NULL);
+
+ if (!set)
+ return -1; /* error */
+
+ for (;;) {
+ CPU_ZERO_S(setsize, set);
+
+ /* the library version does not return size of cpumask_t */
+ n = syscall(SYS_sched_getaffinity, 0, setsize, set);
+
+ if (n < 0 && errno == EINVAL && cpus < 1024 * 1024) {
+ cpuset_free(set);
+ cpus *= 2;
+ set = cpuset_alloc(cpus, &setsize, NULL);
+ if (!set)
+ return -1; /* error */
+ continue;
+ }
+ cpuset_free(set);
+ return n * 8;
+ }
+#endif
+ return -1;
+}
+
+/*
+ * Allocates a new set for ncpus and returns size in bytes and size in bits
+ */
+cpu_set_t *cpuset_alloc(int ncpus, size_t *setsize, size_t *nbits)
+{
+ cpu_set_t *set = CPU_ALLOC(ncpus);
+
+ if (!set)
+ return NULL;
+ if (setsize)
+ *setsize = CPU_ALLOC_SIZE(ncpus);
+ if (nbits)
+ *nbits = cpuset_nbits(CPU_ALLOC_SIZE(ncpus));
+ return set;
+}
+
+void cpuset_free(cpu_set_t *set)
+{
+ CPU_FREE(set);
+}
+
+#if !HAVE_DECL_CPU_ALLOC
+/* Please, use CPU_COUNT_S() macro. This is fallback */
+int __cpuset_count_s(size_t setsize, const cpu_set_t *set)
+{
+ int s = 0;
+ const __cpu_mask *p = set->__bits;
+ const __cpu_mask *end = &set->__bits[setsize / sizeof (__cpu_mask)];
+
+ while (p < end) {
+ __cpu_mask l = *p++;
+
+ if (l == 0)
+ continue;
+# if LONG_BIT > 32
+ l = (l & 0x5555555555555555ul) + ((l >> 1) & 0x5555555555555555ul);
+ l = (l & 0x3333333333333333ul) + ((l >> 2) & 0x3333333333333333ul);
+ l = (l & 0x0f0f0f0f0f0f0f0ful) + ((l >> 4) & 0x0f0f0f0f0f0f0f0ful);
+ l = (l & 0x00ff00ff00ff00fful) + ((l >> 8) & 0x00ff00ff00ff00fful);
+ l = (l & 0x0000ffff0000fffful) + ((l >> 16) & 0x0000ffff0000fffful);
+ l = (l & 0x00000000fffffffful) + ((l >> 32) & 0x00000000fffffffful);
+# else
+ l = (l & 0x55555555ul) + ((l >> 1) & 0x55555555ul);
+ l = (l & 0x33333333ul) + ((l >> 2) & 0x33333333ul);
+ l = (l & 0x0f0f0f0ful) + ((l >> 4) & 0x0f0f0f0ful);
+ l = (l & 0x00ff00fful) + ((l >> 8) & 0x00ff00fful);
+ l = (l & 0x0000fffful) + ((l >> 16) & 0x0000fffful);
+# endif
+ s += l;
+ }
+ return s;
+}
+#endif
+
+/*
+ * Returns human readable representation of the cpuset. The output format is
+ * a list of CPUs with ranges (for example, "0,1,3-9").
+ */
+char *cpulist_create(char *str, size_t len,
+ cpu_set_t *set, size_t setsize)
+{
+ size_t i;
+ char *ptr = str;
+ int entry_made = 0;
+ size_t max = cpuset_nbits(setsize);
+
+ for (i = 0; i < max; i++) {
+ if (CPU_ISSET_S(i, setsize, set)) {
+ int rlen;
+ size_t j, run = 0;
+ entry_made = 1;
+ for (j = i + 1; j < max; j++) {
+ if (CPU_ISSET_S(j, setsize, set))
+ run++;
+ else
+ break;
+ }
+ if (!run)
+ rlen = snprintf(ptr, len, "%zd,", i);
+ else if (run == 1) {
+ rlen = snprintf(ptr, len, "%zd,%zd,", i, i + 1);
+ i++;
+ } else {
+ rlen = snprintf(ptr, len, "%zd-%zd,", i, i + run);
+ i += run;
+ }
+ if (rlen < 0 || (size_t) rlen + 1 > len)
+ return NULL;
+ ptr += rlen;
+ if (rlen > 0 && len > (size_t) rlen)
+ len -= rlen;
+ else
+ len = 0;
+ }
+ }
+ ptr -= entry_made;
+ *ptr = '\0';
+
+ return str;
+}
+
+/*
+ * Returns string with CPU mask.
+ */
+char *cpumask_create(char *str, size_t len,
+ cpu_set_t *set, size_t setsize)
+{
+ char *ptr = str;
+ char *ret = NULL;
+ int cpu;
+
+ for (cpu = cpuset_nbits(setsize) - 4; cpu >= 0; cpu -= 4) {
+ char val = 0;
+
+ if (len == (size_t) (ptr - str))
+ break;
+
+ if (CPU_ISSET_S(cpu, setsize, set))
+ val |= 1;
+ if (CPU_ISSET_S(cpu + 1, setsize, set))
+ val |= 2;
+ if (CPU_ISSET_S(cpu + 2, setsize, set))
+ val |= 4;
+ if (CPU_ISSET_S(cpu + 3, setsize, set))
+ val |= 8;
+
+ if (!ret && val)
+ ret = ptr;
+ *ptr++ = val_to_char(val);
+ }
+ *ptr = '\0';
+ return ret ? ret : ptr - 1;
+}
+
+/*
+ * Parses string with CPUs mask.
+ */
+int cpumask_parse(const char *str, cpu_set_t *set, size_t setsize)
+{
+ int len = strlen(str);
+ const char *ptr = str + len - 1;
+ int cpu = 0;
+
+ /* skip 0x, it's all hex anyway */
+ if (len > 1 && !memcmp(str, "0x", 2L))
+ str += 2;
+
+ CPU_ZERO_S(setsize, set);
+
+ while (ptr >= str) {
+ char val;
+
+ /* cpu masks in /sys uses comma as a separator */
+ if (*ptr == ',')
+ ptr--;
+
+ val = char_to_val(*ptr);
+ if (val == (char) -1)
+ return -1;
+ if (val & 1)
+ CPU_SET_S(cpu, setsize, set);
+ if (val & 2)
+ CPU_SET_S(cpu + 1, setsize, set);
+ if (val & 4)
+ CPU_SET_S(cpu + 2, setsize, set);
+ if (val & 8)
+ CPU_SET_S(cpu + 3, setsize, set);
+ len--;
+ ptr--;
+ cpu += 4;
+ }
+
+ return 0;
+}
+
+/*
+ * Parses string with list of CPU ranges.
+ * Returns 0 on success.
+ * Returns 1 on error.
+ * Returns 2 if fail is set and a cpu number passed in the list doesn't fit
+ * into the cpu_set. If fail is not set cpu numbers that do not fit are
+ * ignored and 0 is returned instead.
+ */
+int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail)
+{
+ size_t max = cpuset_nbits(setsize);
+ const char *p, *q;
+ int r = 0;
+
+ q = str;
+ CPU_ZERO_S(setsize, set);
+
+ while (p = q, q = nexttoken(q, ','), p) {
+ unsigned int a; /* beginning of range */
+ unsigned int b; /* end of range */
+ unsigned int s; /* stride */
+ const char *c1, *c2;
+ char c;
+
+ if ((r = sscanf(p, "%u%c", &a, &c)) < 1)
+ return 1;
+ b = a;
+ s = 1;
+
+ c1 = nexttoken(p, '-');
+ c2 = nexttoken(p, ',');
+ if (c1 != NULL && (c2 == NULL || c1 < c2)) {
+ if ((r = sscanf(c1, "%u%c", &b, &c)) < 1)
+ return 1;
+ c1 = nexttoken(c1, ':');
+ if (c1 != NULL && (c2 == NULL || c1 < c2)) {
+ if ((r = sscanf(c1, "%u%c", &s, &c)) < 1)
+ return 1;
+ if (s == 0)
+ return 1;
+ }
+ }
+
+ if (!(a <= b))
+ return 1;
+ while (a <= b) {
+ if (fail && (a >= max))
+ return 2;
+ CPU_SET_S(a, setsize, set);
+ a += s;
+ }
+ }
+
+ if (r == 2)
+ return 1;
+ return 0;
+}
+
+#ifdef TEST_PROGRAM
+
+#include <getopt.h>
+
+int main(int argc, char *argv[])
+{
+ cpu_set_t *set;
+ size_t setsize, buflen, nbits;
+ char *buf, *mask = NULL, *range = NULL;
+ int ncpus = 2048, rc, c;
+
+ static const struct option longopts[] = {
+ { "ncpus", 1, 0, 'n' },
+ { "mask", 1, 0, 'm' },
+ { "range", 1, 0, 'r' },
+ { NULL, 0, 0, 0 }
+ };
+
+ while ((c = getopt_long(argc, argv, "n:m:r:", longopts, NULL)) != -1) {
+ switch(c) {
+ case 'n':
+ ncpus = atoi(optarg);
+ break;
+ case 'm':
+ mask = strdup(optarg);
+ break;
+ case 'r':
+ range = strdup(optarg);
+ break;
+ default:
+ goto usage_err;
+ }
+ }
+
+ if (!mask && !range)
+ goto usage_err;
+
+ set = cpuset_alloc(ncpus, &setsize, &nbits);
+ if (!set)
+ err(EXIT_FAILURE, "failed to allocate cpu set");
+
+ /*
+ fprintf(stderr, "ncpus: %d, cpuset bits: %zd, cpuset bytes: %zd\n",
+ ncpus, nbits, setsize);
+ */
+
+ buflen = 7 * nbits;
+ buf = malloc(buflen);
+ if (!buf)
+ err(EXIT_FAILURE, "failed to allocate cpu set buffer");
+
+ if (mask)
+ rc = cpumask_parse(mask, set, setsize);
+ else
+ rc = cpulist_parse(range, set, setsize, 0);
+
+ if (rc)
+ errx(EXIT_FAILURE, "failed to parse string: %s", mask ? : range);
+
+ printf("%-15s = %15s ", mask ? : range,
+ cpumask_create(buf, buflen, set, setsize));
+ printf("[%s]\n", cpulist_create(buf, buflen, set, setsize));
+
+ free(buf);
+ free(mask);
+ free(range);
+ cpuset_free(set);
+
+ return EXIT_SUCCESS;
+
+usage_err:
+ fprintf(stderr,
+ "usage: %s [--ncpus <num>] --mask <mask> | --range <list>",
+ program_invocation_short_name);
+ exit(EXIT_FAILURE);
+}
+#endif
diff --git a/libblkid/lib/crc32.c b/libblkid/lib/crc32.c
new file mode 100644
index 000000000..be98f1a8d
--- /dev/null
+++ b/libblkid/lib/crc32.c
@@ -0,0 +1,118 @@
+/*
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ *
+ * First, the polynomial itself and its table of feedback terms. The
+ * polynomial is
+ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ * Note that we take it "backwards" and put the highest-order term in
+ * the lowest-order bit. The X^32 term is "implied"; the LSB is the
+ * X^31 term, etc. The X^0 term (usually shown as "+1") results in
+ * the MSB being 1.
+ *
+ * Note that the usual hardware shift register implementation, which
+ * is what we're using (we're merely optimizing it by doing eight-bit
+ * chunks at a time) shifts bits into the lowest-order term. In our
+ * implementation, that means shifting towards the right. Why do we
+ * do it this way? Because the calculated CRC must be transmitted in
+ * order from highest-order term to lowest-order term. UARTs transmit
+ * characters in order from LSB to MSB. By storing the CRC this way,
+ * we hand it to the UART in the order low-byte to high-byte; the UART
+ * sends each low-bit to hight-bit; and the result is transmission bit
+ * by bit from highest- to lowest-order term without requiring any bit
+ * shuffling on our part. Reception works similarly.
+ *
+ * The feedback terms table consists of 256, 32-bit entries. Notes
+ *
+ * The table can be generated at runtime if desired; code to do so
+ * is shown later. It might not be obvious, but the feedback
+ * terms simply represent the results of eight shift/xor opera-
+ * tions for all combinations of data and CRC register values.
+ *
+ * The values must be right-shifted by eight bits by the "updcrc"
+ * logic; the shift must be unsigned (bring in zeroes). On some
+ * hardware you could probably optimize the shift in assembler by
+ * using byte-swap instructions.
+ * polynomial $edb88320
+ *
+ */
+
+#include <stdio.h>
+
+#include "crc32.h"
+
+
+static const uint32_t crc32_tab[] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+};
+
+/*
+ * This a generic crc32() function, it takes seed as an argument,
+ * and does __not__ xor at the end. Then individual users can do
+ * whatever they need.
+ */
+uint32_t crc32(uint32_t seed, const unsigned char *buf, size_t len)
+{
+ uint32_t crc = seed;
+ const unsigned char *p = buf;
+
+ while (len) {
+ crc = crc32_tab[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ len--;
+ }
+
+ return crc;
+}
+
diff --git a/libblkid/lib/crc64.c b/libblkid/lib/crc64.c
new file mode 100644
index 000000000..0be78e63b
--- /dev/null
+++ b/libblkid/lib/crc64.c
@@ -0,0 +1,109 @@
+#include "crc64.h"
+
+static const uint64_t crc64_tab[256] = {
+ 0x0000000000000000ULL, 0x42F0E1EBA9EA3693ULL, 0x85E1C3D753D46D26ULL,
+ 0xC711223CFA3E5BB5ULL, 0x493366450E42ECDFULL, 0x0BC387AEA7A8DA4CULL,
+ 0xCCD2A5925D9681F9ULL, 0x8E224479F47CB76AULL, 0x9266CC8A1C85D9BEULL,
+ 0xD0962D61B56FEF2DULL, 0x17870F5D4F51B498ULL, 0x5577EEB6E6BB820BULL,
+ 0xDB55AACF12C73561ULL, 0x99A54B24BB2D03F2ULL, 0x5EB4691841135847ULL,
+ 0x1C4488F3E8F96ED4ULL, 0x663D78FF90E185EFULL, 0x24CD9914390BB37CULL,
+ 0xE3DCBB28C335E8C9ULL, 0xA12C5AC36ADFDE5AULL, 0x2F0E1EBA9EA36930ULL,
+ 0x6DFEFF5137495FA3ULL, 0xAAEFDD6DCD770416ULL, 0xE81F3C86649D3285ULL,
+ 0xF45BB4758C645C51ULL, 0xB6AB559E258E6AC2ULL, 0x71BA77A2DFB03177ULL,
+ 0x334A9649765A07E4ULL, 0xBD68D2308226B08EULL, 0xFF9833DB2BCC861DULL,
+ 0x388911E7D1F2DDA8ULL, 0x7A79F00C7818EB3BULL, 0xCC7AF1FF21C30BDEULL,
+ 0x8E8A101488293D4DULL, 0x499B3228721766F8ULL, 0x0B6BD3C3DBFD506BULL,
+ 0x854997BA2F81E701ULL, 0xC7B97651866BD192ULL, 0x00A8546D7C558A27ULL,
+ 0x4258B586D5BFBCB4ULL, 0x5E1C3D753D46D260ULL, 0x1CECDC9E94ACE4F3ULL,
+ 0xDBFDFEA26E92BF46ULL, 0x990D1F49C77889D5ULL, 0x172F5B3033043EBFULL,
+ 0x55DFBADB9AEE082CULL, 0x92CE98E760D05399ULL, 0xD03E790CC93A650AULL,
+ 0xAA478900B1228E31ULL, 0xE8B768EB18C8B8A2ULL, 0x2FA64AD7E2F6E317ULL,
+ 0x6D56AB3C4B1CD584ULL, 0xE374EF45BF6062EEULL, 0xA1840EAE168A547DULL,
+ 0x66952C92ECB40FC8ULL, 0x2465CD79455E395BULL, 0x3821458AADA7578FULL,
+ 0x7AD1A461044D611CULL, 0xBDC0865DFE733AA9ULL, 0xFF3067B657990C3AULL,
+ 0x711223CFA3E5BB50ULL, 0x33E2C2240A0F8DC3ULL, 0xF4F3E018F031D676ULL,
+ 0xB60301F359DBE0E5ULL, 0xDA050215EA6C212FULL, 0x98F5E3FE438617BCULL,
+ 0x5FE4C1C2B9B84C09ULL, 0x1D14202910527A9AULL, 0x93366450E42ECDF0ULL,
+ 0xD1C685BB4DC4FB63ULL, 0x16D7A787B7FAA0D6ULL, 0x5427466C1E109645ULL,
+ 0x4863CE9FF6E9F891ULL, 0x0A932F745F03CE02ULL, 0xCD820D48A53D95B7ULL,
+ 0x8F72ECA30CD7A324ULL, 0x0150A8DAF8AB144EULL, 0x43A04931514122DDULL,
+ 0x84B16B0DAB7F7968ULL, 0xC6418AE602954FFBULL, 0xBC387AEA7A8DA4C0ULL,
+ 0xFEC89B01D3679253ULL, 0x39D9B93D2959C9E6ULL, 0x7B2958D680B3FF75ULL,
+ 0xF50B1CAF74CF481FULL, 0xB7FBFD44DD257E8CULL, 0x70EADF78271B2539ULL,
+ 0x321A3E938EF113AAULL, 0x2E5EB66066087D7EULL, 0x6CAE578BCFE24BEDULL,
+ 0xABBF75B735DC1058ULL, 0xE94F945C9C3626CBULL, 0x676DD025684A91A1ULL,
+ 0x259D31CEC1A0A732ULL, 0xE28C13F23B9EFC87ULL, 0xA07CF2199274CA14ULL,
+ 0x167FF3EACBAF2AF1ULL, 0x548F120162451C62ULL, 0x939E303D987B47D7ULL,
+ 0xD16ED1D631917144ULL, 0x5F4C95AFC5EDC62EULL, 0x1DBC74446C07F0BDULL,
+ 0xDAAD56789639AB08ULL, 0x985DB7933FD39D9BULL, 0x84193F60D72AF34FULL,
+ 0xC6E9DE8B7EC0C5DCULL, 0x01F8FCB784FE9E69ULL, 0x43081D5C2D14A8FAULL,
+ 0xCD2A5925D9681F90ULL, 0x8FDAB8CE70822903ULL, 0x48CB9AF28ABC72B6ULL,
+ 0x0A3B7B1923564425ULL, 0x70428B155B4EAF1EULL, 0x32B26AFEF2A4998DULL,
+ 0xF5A348C2089AC238ULL, 0xB753A929A170F4ABULL, 0x3971ED50550C43C1ULL,
+ 0x7B810CBBFCE67552ULL, 0xBC902E8706D82EE7ULL, 0xFE60CF6CAF321874ULL,
+ 0xE224479F47CB76A0ULL, 0xA0D4A674EE214033ULL, 0x67C58448141F1B86ULL,
+ 0x253565A3BDF52D15ULL, 0xAB1721DA49899A7FULL, 0xE9E7C031E063ACECULL,
+ 0x2EF6E20D1A5DF759ULL, 0x6C0603E6B3B7C1CAULL, 0xF6FAE5C07D3274CDULL,
+ 0xB40A042BD4D8425EULL, 0x731B26172EE619EBULL, 0x31EBC7FC870C2F78ULL,
+ 0xBFC9838573709812ULL, 0xFD39626EDA9AAE81ULL, 0x3A28405220A4F534ULL,
+ 0x78D8A1B9894EC3A7ULL, 0x649C294A61B7AD73ULL, 0x266CC8A1C85D9BE0ULL,
+ 0xE17DEA9D3263C055ULL, 0xA38D0B769B89F6C6ULL, 0x2DAF4F0F6FF541ACULL,
+ 0x6F5FAEE4C61F773FULL, 0xA84E8CD83C212C8AULL, 0xEABE6D3395CB1A19ULL,
+ 0x90C79D3FEDD3F122ULL, 0xD2377CD44439C7B1ULL, 0x15265EE8BE079C04ULL,
+ 0x57D6BF0317EDAA97ULL, 0xD9F4FB7AE3911DFDULL, 0x9B041A914A7B2B6EULL,
+ 0x5C1538ADB04570DBULL, 0x1EE5D94619AF4648ULL, 0x02A151B5F156289CULL,
+ 0x4051B05E58BC1E0FULL, 0x87409262A28245BAULL, 0xC5B073890B687329ULL,
+ 0x4B9237F0FF14C443ULL, 0x0962D61B56FEF2D0ULL, 0xCE73F427ACC0A965ULL,
+ 0x8C8315CC052A9FF6ULL, 0x3A80143F5CF17F13ULL, 0x7870F5D4F51B4980ULL,
+ 0xBF61D7E80F251235ULL, 0xFD913603A6CF24A6ULL, 0x73B3727A52B393CCULL,
+ 0x31439391FB59A55FULL, 0xF652B1AD0167FEEAULL, 0xB4A25046A88DC879ULL,
+ 0xA8E6D8B54074A6ADULL, 0xEA16395EE99E903EULL, 0x2D071B6213A0CB8BULL,
+ 0x6FF7FA89BA4AFD18ULL, 0xE1D5BEF04E364A72ULL, 0xA3255F1BE7DC7CE1ULL,
+ 0x64347D271DE22754ULL, 0x26C49CCCB40811C7ULL, 0x5CBD6CC0CC10FAFCULL,
+ 0x1E4D8D2B65FACC6FULL, 0xD95CAF179FC497DAULL, 0x9BAC4EFC362EA149ULL,
+ 0x158E0A85C2521623ULL, 0x577EEB6E6BB820B0ULL, 0x906FC95291867B05ULL,
+ 0xD29F28B9386C4D96ULL, 0xCEDBA04AD0952342ULL, 0x8C2B41A1797F15D1ULL,
+ 0x4B3A639D83414E64ULL, 0x09CA82762AAB78F7ULL, 0x87E8C60FDED7CF9DULL,
+ 0xC51827E4773DF90EULL, 0x020905D88D03A2BBULL, 0x40F9E43324E99428ULL,
+ 0x2CFFE7D5975E55E2ULL, 0x6E0F063E3EB46371ULL, 0xA91E2402C48A38C4ULL,
+ 0xEBEEC5E96D600E57ULL, 0x65CC8190991CB93DULL, 0x273C607B30F68FAEULL,
+ 0xE02D4247CAC8D41BULL, 0xA2DDA3AC6322E288ULL, 0xBE992B5F8BDB8C5CULL,
+ 0xFC69CAB42231BACFULL, 0x3B78E888D80FE17AULL, 0x7988096371E5D7E9ULL,
+ 0xF7AA4D1A85996083ULL, 0xB55AACF12C735610ULL, 0x724B8ECDD64D0DA5ULL,
+ 0x30BB6F267FA73B36ULL, 0x4AC29F2A07BFD00DULL, 0x08327EC1AE55E69EULL,
+ 0xCF235CFD546BBD2BULL, 0x8DD3BD16FD818BB8ULL, 0x03F1F96F09FD3CD2ULL,
+ 0x41011884A0170A41ULL, 0x86103AB85A2951F4ULL, 0xC4E0DB53F3C36767ULL,
+ 0xD8A453A01B3A09B3ULL, 0x9A54B24BB2D03F20ULL, 0x5D45907748EE6495ULL,
+ 0x1FB5719CE1045206ULL, 0x919735E51578E56CULL, 0xD367D40EBC92D3FFULL,
+ 0x1476F63246AC884AULL, 0x568617D9EF46BED9ULL, 0xE085162AB69D5E3CULL,
+ 0xA275F7C11F7768AFULL, 0x6564D5FDE549331AULL, 0x279434164CA30589ULL,
+ 0xA9B6706FB8DFB2E3ULL, 0xEB46918411358470ULL, 0x2C57B3B8EB0BDFC5ULL,
+ 0x6EA7525342E1E956ULL, 0x72E3DAA0AA188782ULL, 0x30133B4B03F2B111ULL,
+ 0xF7021977F9CCEAA4ULL, 0xB5F2F89C5026DC37ULL, 0x3BD0BCE5A45A6B5DULL,
+ 0x79205D0E0DB05DCEULL, 0xBE317F32F78E067BULL, 0xFCC19ED95E6430E8ULL,
+ 0x86B86ED5267CDBD3ULL, 0xC4488F3E8F96ED40ULL, 0x0359AD0275A8B6F5ULL,
+ 0x41A94CE9DC428066ULL, 0xCF8B0890283E370CULL, 0x8D7BE97B81D4019FULL,
+ 0x4A6ACB477BEA5A2AULL, 0x089A2AACD2006CB9ULL, 0x14DEA25F3AF9026DULL,
+ 0x562E43B4931334FEULL, 0x913F6188692D6F4BULL, 0xD3CF8063C0C759D8ULL,
+ 0x5DEDC41A34BBEEB2ULL, 0x1F1D25F19D51D821ULL, 0xD80C07CD676F8394ULL,
+ 0x9AFCE626CE85B507ULL
+};
+
+/*
+ * This a generic crc64() function, it takes seed as an argument,
+ * and does __not__ xor at the end. Then individual users can do
+ * whatever they need.
+ */
+uint64_t crc64(uint64_t seed, const unsigned char *data, size_t len)
+{
+ uint64_t crc = seed;
+
+ while (len) {
+ int i = ((int) (crc >> 56) ^ *data++) & 0xFF;
+ crc = crc64_tab[i] ^ (crc << 8);
+ len--;
+ }
+
+ return crc;
+}
+
diff --git a/libblkid/lib/env.c b/libblkid/lib/env.c
new file mode 100644
index 000000000..c79e0e0de
--- /dev/null
+++ b/libblkid/lib/env.c
@@ -0,0 +1,110 @@
+/*
+ * Security checks of environment
+ * Added from shadow-utils package
+ * by Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#else
+#define PR_GET_DUMPABLE 3
+#endif
+#if (!defined(HAVE_PRCTL) && defined(linux))
+#include <sys/syscall.h>
+#endif
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "env.h"
+
+#ifndef HAVE_ENVIRON_DECL
+extern char **environ;
+#endif
+
+static char * const forbid[] = {
+ "_RLD_=",
+ "BASH_ENV=", /* GNU creeping featurism strikes again... */
+ "ENV=",
+ "HOME=",
+ "IFS=",
+ "KRB_CONF=",
+ "LD_", /* anything with the LD_ prefix */
+ "LIBPATH=",
+ "MAIL=",
+ "NLSPATH=",
+ "PATH=",
+ "SHELL=",
+ "SHLIB_PATH=",
+ (char *) 0
+};
+
+/* these are allowed, but with no slashes inside
+ (to work around security problems in GNU gettext) */
+static char * const noslash[] = {
+ "LANG=",
+ "LANGUAGE=",
+ "LC_", /* anything with the LC_ prefix */
+ (char *) 0
+};
+
+void
+sanitize_env(void)
+{
+ char **envp = environ;
+ char * const *bad;
+ char **cur;
+ char **move;
+
+ for (cur = envp; *cur; cur++) {
+ for (bad = forbid; *bad; bad++) {
+ if (strncmp(*cur, *bad, strlen(*bad)) == 0) {
+ for (move = cur; *move; move++)
+ *move = *(move + 1);
+ cur--;
+ break;
+ }
+ }
+ }
+
+ for (cur = envp; *cur; cur++) {
+ for (bad = noslash; *bad; bad++) {
+ if (strncmp(*cur, *bad, strlen(*bad)) != 0)
+ continue;
+ if (!strchr(*cur, '/'))
+ continue; /* OK */
+ for (move = cur; *move; move++)
+ *move = *(move + 1);
+ cur--;
+ break;
+ }
+ }
+}
+
+
+char *safe_getenv(const char *arg)
+{
+ uid_t ruid = getuid();
+
+ if (ruid != 0 || (ruid != geteuid()) || (getgid() != getegid()))
+ return NULL;
+#ifdef HAVE_PRCTL
+ if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+ return NULL;
+#else
+#if (defined(linux) && defined(SYS_prctl))
+ if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+ return NULL;
+#endif
+#endif
+#ifdef HAVE_SECURE_GETENV
+return secure_getenv(arg);
+#elif HAVE___SECURE_GETENV
+ return __secure_getenv(arg);
+#else
+ return getenv(arg);
+#endif
+}
diff --git a/libblkid/lib/exec_shell.c b/libblkid/lib/exec_shell.c
new file mode 100644
index 000000000..2b723acb4
--- /dev/null
+++ b/libblkid/lib/exec_shell.c
@@ -0,0 +1,47 @@
+/*
+ * exec_shell() - launch a shell, else exit!
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <libgen.h>
+
+#include "nls.h"
+#include "c.h"
+#include "xalloc.h"
+
+#include "exec_shell.h"
+
+#define DEFAULT_SHELL "/bin/sh"
+
+void exec_shell(void)
+{
+ const char *shell = getenv("SHELL"), *shell_basename;
+ char *arg0;
+ if (!shell)
+ shell = DEFAULT_SHELL;
+
+ shell_basename = basename(shell);
+ arg0 = xmalloc(strlen(shell_basename) + 2);
+ arg0[0] = '-';
+ strcpy(arg0 + 1, shell_basename);
+
+ execl(shell, arg0, NULL);
+ err(EXIT_FAILURE, _("failed to execute %s"), shell);
+}
diff --git a/libblkid/lib/fileutils.c b/libblkid/lib/fileutils.c
new file mode 100644
index 000000000..92a391d7e
--- /dev/null
+++ b/libblkid/lib/fileutils.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2012 Sami Kerola <kerolasa@iki.fi>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <paths.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/limits.h>
+
+#include "c.h"
+#include "fileutils.h"
+#include "pathnames.h"
+
+#ifndef _PATH_TMP
+#define _PATH_TMP "/tmp/"
+#endif
+
+#ifndef OPEN_MAX
+#define OPEN_MAX 256
+#endif
+
+/* Create open temporary file in safe way. Please notice that the
+ * file permissions are -rw------- by default. */
+int xmkstemp(char **tmpname, char *dir)
+{
+ char *localtmp;
+ char *tmpenv;
+ mode_t old_mode;
+ int fd, rc;
+
+ /* Some use cases must be capable of being moved atomically
+ * with rename(2), which is the reason why dir is here. */
+ if (dir != NULL)
+ tmpenv = dir;
+ else
+ tmpenv = getenv("TMPDIR");
+
+ if (tmpenv)
+ rc = asprintf(&localtmp, "%s/%s.XXXXXX", tmpenv,
+ program_invocation_short_name);
+ else
+ rc = asprintf(&localtmp, "%s/%s.XXXXXX", _PATH_TMP,
+ program_invocation_short_name);
+
+ if (rc < 0)
+ return -1;
+
+ old_mode = umask(077);
+ fd = mkstemp(localtmp);
+ umask(old_mode);
+ if (fd == -1) {
+ free(localtmp);
+ localtmp = NULL;
+ }
+ *tmpname = localtmp;
+ return fd;
+}
+
+/*
+ * portable getdtablesize()
+ */
+int get_fd_tabsize(void)
+{
+ int m;
+
+#if defined(HAVE_GETDTABLESIZE)
+ m = getdtablesize();
+#elif defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
+ struct rlimit rl;
+
+ getrlimit(RLIMIT_NOFILE, &rl);
+ m = rl.rlim_cur;
+#elif defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
+ m = sysconf(_SC_OPEN_MAX);
+#else
+ m = OPEN_MAX;
+#endif
+ return m;
+}
+
+#ifdef TEST_PROGRAM
+int main(void)
+{
+ FILE *f;
+ char *tmpname;
+ f = xfmkstemp(&tmpname, NULL);
+ unlink(tmpname);
+ free(tmpname);
+ fclose(f);
+ return EXIT_FAILURE;
+}
+#endif
+
+
+int mkdir_p(const char *path, mode_t mode)
+{
+ char *p, *dir;
+ int rc = 0;
+
+ if (!path || !*path)
+ return -EINVAL;
+
+ dir = p = strdup(path);
+ if (!dir)
+ return -ENOMEM;
+
+ if (*p == '/')
+ p++;
+
+ while (p && *p) {
+ char *e = strchr(p, '/');
+ if (e)
+ *e = '\0';
+ if (*p) {
+ rc = mkdir(dir, mode);
+ if (rc && errno != EEXIST)
+ break;
+ rc = 0;
+ }
+ if (!e)
+ break;
+ *e = '/';
+ p = e + 1;
+ }
+
+ free(dir);
+ return rc;
+}
+
+/* returns basename and keeps dirname in the @path, if @path is "/" (root)
+ * then returns empty string */
+char *stripoff_last_component(char *path)
+{
+ char *p = path ? strrchr(path, '/') : NULL;
+
+ if (!p)
+ return NULL;
+ *p = '\0';
+ return p + 1;
+}
diff --git a/libblkid/lib/ismounted.c b/libblkid/lib/ismounted.c
new file mode 100644
index 000000000..00b575c5e
--- /dev/null
+++ b/libblkid/lib/ismounted.c
@@ -0,0 +1,388 @@
+/*
+ * ismounted.c --- Check to see if the filesystem was mounted
+ *
+ * Copyright (C) 1995,1996,1997,1998,1999,2000,2008 Theodore Ts'o.
+ *
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+#include <string.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <sys/param.h>
+#ifdef __APPLE__
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#endif
+
+#include "pathnames.h"
+#include "ismounted.h"
+#include "c.h"
+#ifdef __linux__
+# include "loopdev.h"
+#endif
+
+
+
+#ifdef HAVE_MNTENT_H
+/*
+ * Helper function which checks a file in /etc/mtab format to see if a
+ * filesystem is mounted. Returns an error if the file doesn't exist
+ * or can't be opened.
+ */
+static int check_mntent_file(const char *mtab_file, const char *file,
+ int *mount_flags, char *mtpt, int mtlen)
+{
+ struct mntent *mnt;
+ struct stat st_buf;
+ int retval = 0;
+ dev_t file_dev=0, file_rdev=0;
+ ino_t file_ino=0;
+ FILE *f;
+ int fd;
+
+ *mount_flags = 0;
+ if ((f = setmntent (mtab_file, "r")) == NULL)
+ return errno;
+
+ if (stat(file, &st_buf) == 0) {
+ if (S_ISBLK(st_buf.st_mode)) {
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+ file_rdev = st_buf.st_rdev;
+#endif /* __GNU__ */
+ } else {
+ file_dev = st_buf.st_dev;
+ file_ino = st_buf.st_ino;
+ }
+ }
+
+ while ((mnt = getmntent (f)) != NULL) {
+ if (mnt->mnt_fsname[0] != '/')
+ continue;
+ if (strcmp(file, mnt->mnt_fsname) == 0)
+ break;
+ if (stat(mnt->mnt_fsname, &st_buf) != 0)
+ continue;
+
+ if (S_ISBLK(st_buf.st_mode)) {
+#ifndef __GNU__
+ if (file_rdev && file_rdev == st_buf.st_rdev)
+ break;
+#ifdef __linux__
+ /* maybe the file is loopdev backing file */
+ if (file_dev
+ && major(st_buf.st_rdev) == LOOPDEV_MAJOR
+ && loopdev_is_used(mnt->mnt_fsname, file, 0, 0))
+ break;
+#endif /* __linux__ */
+#endif /* __GNU__ */
+ } else {
+ if (file_dev && ((file_dev == st_buf.st_dev) &&
+ (file_ino == st_buf.st_ino)))
+ break;
+ }
+ }
+
+ if (mnt == NULL) {
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+ /*
+ * Do an extra check to see if this is the root device. We
+ * can't trust /etc/mtab, and /proc/mounts will only list
+ * /dev/root for the root filesystem. Argh. Instead we
+ * check if the given device has the same major/minor number
+ * as the device that the root directory is on.
+ */
+ if (file_rdev && stat("/", &st_buf) == 0 &&
+ st_buf.st_dev == file_rdev) {
+ *mount_flags = MF_MOUNTED;
+ if (mtpt)
+ strncpy(mtpt, "/", mtlen);
+ goto is_root;
+ }
+#endif /* __GNU__ */
+ goto errout;
+ }
+#ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */
+ /* Validate the entry in case /etc/mtab is out of date */
+ /*
+ * We need to be paranoid, because some broken distributions
+ * (read: Slackware) don't initialize /etc/mtab before checking
+ * all of the non-root filesystems on the disk.
+ */
+ if (stat(mnt->mnt_dir, &st_buf) < 0) {
+ retval = errno;
+ if (retval == ENOENT) {
+#ifdef DEBUG
+ printf("Bogus entry in %s! (%s does not exist)\n",
+ mtab_file, mnt->mnt_dir);
+#endif /* DEBUG */
+ retval = 0;
+ }
+ goto errout;
+ }
+ if (file_rdev && (st_buf.st_dev != file_rdev)) {
+#ifdef DEBUG
+ printf("Bogus entry in %s! (%s not mounted on %s)\n",
+ mtab_file, file, mnt->mnt_dir);
+#endif /* DEBUG */
+ goto errout;
+ }
+#endif /* __GNU__ */
+ *mount_flags = MF_MOUNTED;
+
+#ifdef MNTOPT_RO
+ /* Check to see if the ro option is set */
+ if (hasmntopt(mnt, MNTOPT_RO))
+ *mount_flags |= MF_READONLY;
+#endif
+
+ if (mtpt)
+ strncpy(mtpt, mnt->mnt_dir, mtlen);
+ /*
+ * Check to see if we're referring to the root filesystem.
+ * If so, do a manual check to see if we can open /etc/mtab
+ * read/write, since if the root is mounted read/only, the
+ * contents of /etc/mtab may not be accurate.
+ */
+ if (!strcmp(mnt->mnt_dir, "/")) {
+is_root:
+#define TEST_FILE "/.ismount-test-file"
+ *mount_flags |= MF_ISROOT;
+ fd = open(TEST_FILE, O_RDWR|O_CREAT|O_CLOEXEC, 0600);
+ if (fd < 0) {
+ if (errno == EROFS)
+ *mount_flags |= MF_READONLY;
+ } else
+ close(fd);
+ (void) unlink(TEST_FILE);
+ }
+ retval = 0;
+errout:
+ endmntent (f);
+ return retval;
+}
+
+static int check_mntent(const char *file, int *mount_flags,
+ char *mtpt, int mtlen)
+{
+ int retval;
+
+#ifdef DEBUG
+ retval = check_mntent_file("/tmp/mtab", file, mount_flags,
+ mtpt, mtlen);
+ if (retval == 0)
+ return 0;
+#endif /* DEBUG */
+#ifdef __linux__
+ retval = check_mntent_file("/proc/mounts", file, mount_flags,
+ mtpt, mtlen);
+ if (retval == 0 && (*mount_flags != 0))
+ return 0;
+ if (access("/proc/mounts", R_OK) == 0) {
+ *mount_flags = 0;
+ return retval;
+ }
+#endif /* __linux__ */
+#if defined(MOUNTED) || defined(_PATH_MOUNTED)
+#ifndef MOUNTED
+#define MOUNTED _PATH_MOUNTED
+#endif /* MOUNTED */
+ retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen);
+ return retval;
+#else
+ *mount_flags = 0;
+ return 0;
+#endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */
+}
+
+#else
+#if defined(HAVE_GETMNTINFO)
+
+static int check_getmntinfo(const char *file, int *mount_flags,
+ char *mtpt, int mtlen)
+{
+ struct statfs *mp;
+ int len, n;
+ const char *s1;
+ char *s2;
+
+ n = getmntinfo(&mp, MNT_NOWAIT);
+ if (n == 0)
+ return errno;
+
+ len = sizeof(_PATH_DEV) - 1;
+ s1 = file;
+ if (strncmp(_PATH_DEV, s1, len) == 0)
+ s1 += len;
+
+ *mount_flags = 0;
+ while (--n >= 0) {
+ s2 = mp->f_mntfromname;
+ if (strncmp(_PATH_DEV, s2, len) == 0) {
+ s2 += len - 1;
+ *s2 = 'r';
+ }
+ if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) {
+ *mount_flags = MF_MOUNTED;
+ break;
+ }
+ ++mp;
+ }
+ if (mtpt)
+ strncpy(mtpt, mp->f_mntonname, mtlen);
+ return 0;
+}
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_MNTENT_H */
+
+/*
+ * Check to see if we're dealing with the swap device.
+ */
+static int is_swap_device(const char *file)
+{
+ FILE *f;
+ char buf[1024], *cp;
+ dev_t file_dev;
+ struct stat st_buf;
+ int ret = 0;
+
+ file_dev = 0;
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+ if ((stat(file, &st_buf) == 0) &&
+ S_ISBLK(st_buf.st_mode))
+ file_dev = st_buf.st_rdev;
+#endif /* __GNU__ */
+
+ if (!(f = fopen("/proc/swaps", "r")))
+ return 0;
+ /* Skip the first line */
+ if (!fgets(buf, sizeof(buf), f))
+ goto leave;
+ if (*buf && strncmp(buf, "Filename\t", 9))
+ /* Linux <=2.6.19 contained a bug in the /proc/swaps
+ * code where the header would not be displayed
+ */
+ goto valid_first_line;
+
+ while (fgets(buf, sizeof(buf), f)) {
+valid_first_line:
+ if ((cp = strchr(buf, ' ')) != NULL)
+ *cp = 0;
+ if ((cp = strchr(buf, '\t')) != NULL)
+ *cp = 0;
+ if (strcmp(buf, file) == 0) {
+ ret++;
+ break;
+ }
+#ifndef __GNU__
+ if (file_dev && (stat(buf, &st_buf) == 0) &&
+ S_ISBLK(st_buf.st_mode) &&
+ file_dev == st_buf.st_rdev) {
+ ret++;
+ break;
+ }
+#endif /* __GNU__ */
+ }
+
+leave:
+ fclose(f);
+ return ret;
+}
+
+
+/*
+ * check_mount_point() fills determines if the device is mounted or otherwise
+ * busy, and fills in mount_flags with one or more of the following flags:
+ * MF_MOUNTED, MF_ISROOT, MF_READONLY, MF_SWAP, and MF_BUSY. If mtpt is
+ * non-NULL, the directory where the device is mounted is copied to where mtpt
+ * is pointing, up to mtlen characters.
+ */
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+int check_mount_point(const char *device, int *mount_flags,
+ char *mtpt, int mtlen)
+{
+ struct stat st_buf;
+ int retval = 0;
+ int fd;
+
+ if (is_swap_device(device)) {
+ *mount_flags = MF_MOUNTED | MF_SWAP;
+ if (mtpt && mtlen)
+ strncpy(mtpt, "[SWAP]", mtlen);
+ } else {
+#ifdef HAVE_MNTENT_H
+ retval = check_mntent(device, mount_flags, mtpt, mtlen);
+#else
+#ifdef HAVE_GETMNTINFO
+ retval = check_getmntinfo(device, mount_flags, mtpt, mtlen);
+#else
+#ifdef __GNUC__
+ //#warning "Can't use getmntent or getmntinfo to check for mounted filesystems!"
+#endif
+ *mount_flags = 0;
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_MNTENT_H */
+ }
+ if (retval)
+ return retval;
+
+#ifdef __linux__ /* This only works on Linux 2.6+ systems */
+ if ((stat(device, &st_buf) != 0) ||
+ !S_ISBLK(st_buf.st_mode))
+ return 0;
+ fd = open(device, O_RDONLY|O_EXCL|O_CLOEXEC);
+ if (fd < 0) {
+ if (errno == EBUSY)
+ *mount_flags |= MF_BUSY;
+ } else
+ close(fd);
+#endif
+
+ return 0;
+}
+
+int is_mounted(const char *file)
+{
+ int retval;
+ int mount_flags = 0;
+
+ retval = check_mount_point(file, &mount_flags, NULL, 0);
+ if (retval)
+ return 0;
+ return mount_flags & MF_MOUNTED;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+ int flags = 0;
+ char devname[PATH_MAX];
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s device\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ if (check_mount_point(argv[1], &flags, devname, sizeof(devname)) == 0 &&
+ (flags & MF_MOUNTED)) {
+ if (flags & MF_SWAP)
+ printf("used swap device\n");
+ else
+ printf("mounted on %s\n", devname);
+ return EXIT_SUCCESS;
+ }
+
+ printf("not mounted\n");
+ return EXIT_FAILURE;
+}
+#endif /* DEBUG */
diff --git a/libblkid/lib/langinfo.c b/libblkid/lib/langinfo.c
new file mode 100644
index 000000000..deeab9b11
--- /dev/null
+++ b/libblkid/lib/langinfo.c
@@ -0,0 +1,121 @@
+/*
+ * This is callback solution for systems without nl_langinfo(), this function
+ * returns hardcoded and on locale setting independed value.
+ *
+ * See langinfo.h man page for more details.
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ */
+#include "nls.h"
+
+char *langinfo_fallback(nl_item item)
+{
+ switch (item) {
+ case CODESET:
+ return "ISO-8859-1";
+ case THOUSEP:
+ return ",";
+ case D_T_FMT:
+ case ERA_D_T_FMT:
+ return "%a %b %e %H:%M:%S %Y";
+ case D_FMT:
+ case ERA_D_FMT:
+ return "%m/%d/%y";
+ case T_FMT:
+ case ERA_T_FMT:
+ return "%H:%M:%S";
+ case T_FMT_AMPM:
+ return "%I:%M:%S %p";
+ case AM_STR:
+ return "AM";
+ case PM_STR:
+ return "PM";
+ case DAY_1:
+ return "Sunday";
+ case DAY_2:
+ return "Monday";
+ case DAY_3:
+ return "Tuesday";
+ case DAY_4:
+ return "Wednesday";
+ case DAY_5:
+ return "Thursday";
+ case DAY_6:
+ return "Friday";
+ case DAY_7:
+ return "Saturday";
+ case ABDAY_1:
+ return "Sun";
+ case ABDAY_2:
+ return "Mon";
+ case ABDAY_3:
+ return "Tue";
+ case ABDAY_4:
+ return "Wed";
+ case ABDAY_5:
+ return "Thu";
+ case ABDAY_6:
+ return "Fri";
+ case ABDAY_7:
+ return "Sat";
+ case MON_1:
+ return "January";
+ case MON_2:
+ return "February";
+ case MON_3:
+ return "March";
+ case MON_4:
+ return "April";
+ case MON_5:
+ return "May";
+ case MON_6:
+ return "June";
+ case MON_7:
+ return "July";
+ case MON_8:
+ return "August";
+ case MON_9:
+ return "September";
+ case MON_10:
+ return "October";
+ case MON_11:
+ return "November";
+ case MON_12:
+ return "December";
+ case ABMON_1:
+ return "Jan";
+ case ABMON_2:
+ return "Feb";
+ case ABMON_3:
+ return "Mar";
+ case ABMON_4:
+ return "Apr";
+ case ABMON_5:
+ return "May";
+ case ABMON_6:
+ return "Jun";
+ case ABMON_7:
+ return "Jul";
+ case ABMON_8:
+ return "Aug";
+ case ABMON_9:
+ return "Sep";
+ case ABMON_10:
+ return "Oct";
+ case ABMON_11:
+ return "Nov";
+ case ABMON_12:
+ return "Dec";
+ case ALT_DIGITS:
+ return "\0\0\0\0\0\0\0\0\0\0";
+ case CRNCYSTR:
+ return "-";
+ case YESEXPR:
+ return "^[yY]";
+ case NOEXPR:
+ return "^[nN]";
+ default:
+ return "";
+ }
+}
+
diff --git a/libblkid/lib/linux_version.c b/libblkid/lib/linux_version.c
new file mode 100644
index 000000000..2bcc2cc65
--- /dev/null
+++ b/libblkid/lib/linux_version.c
@@ -0,0 +1,25 @@
+#include <stdio.h>
+#include <sys/utsname.h>
+
+#include "linux_version.h"
+
+int get_linux_version (void)
+{
+ static int kver = -1;
+ struct utsname uts;
+ int major = 0;
+ int minor = 0;
+ int teeny = 0;
+ int n;
+
+ if (kver != -1)
+ return kver;
+ if (uname (&uts))
+ return kver = 0;
+
+ n = sscanf(uts.release, "%d.%d.%d", &major, &minor, &teeny);
+ if (n < 1 || n > 3)
+ return kver = 0;
+
+ return kver = KERNEL_VERSION(major, minor, teeny);
+}
diff --git a/libblkid/lib/loopdev.c b/libblkid/lib/loopdev.c
new file mode 100644
index 000000000..09b9bbf75
--- /dev/null
+++ b/libblkid/lib/loopdev.c
@@ -0,0 +1,1572 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ *
+ * -- based on mount/losetup.c
+ *
+ * Simple library for work with loop devices.
+ *
+ * - requires kernel 2.6.x
+ * - reads info from /sys/block/loop<N>/loop/<attr> (new kernels)
+ * - reads info by ioctl
+ * - supports *unlimited* number of loop devices
+ * - supports /dev/loop<N> as well as /dev/loop/<N>
+ * - minimize overhead (fd, loopinfo, ... are shared for all operations)
+ * - setup (associate device and backing file)
+ * - delete (dis-associate file)
+ * - old LOOP_{SET,GET}_STATUS (32bit) ioctls are unsupported
+ * - extendible
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/sysmacros.h>
+#include <inttypes.h>
+#include <dirent.h>
+#include <linux/posix_types.h>
+
+#include "linux_version.h"
+#include "c.h"
+#include "sysfs.h"
+#include "pathnames.h"
+#include "loopdev.h"
+#include "canonicalize.h"
+#include "at.h"
+#include "blkdev.h"
+#include "debug.h"
+
+/*
+ * Debug stuff (based on include/debug.h)
+ */
+UL_DEBUG_DEFINE_MASK(loopdev);
+UL_DEBUG_DEFINE_MASKNAMES(loopdev) = UL_DEBUG_EMPTY_MASKNAMES;
+
+#define LOOPDEV_DEBUG_INIT (1 << 1)
+#define LOOPDEV_DEBUG_CXT (1 << 2)
+#define LOOPDEV_DEBUG_ITER (1 << 3)
+#define LOOPDEV_DEBUG_SETUP (1 << 4)
+#define SFDISKPROG_DEBUG_ALL 0xFFFF
+
+#define DBG(m, x) __UL_DBG(loopdev, LOOPDEV_DEBUG_, m, x)
+#define ON_DBG(m, x) __UL_DBG_CALL(loopdev, LOOPDEV_DEBUG_, m, x)
+
+static void loopdev_init_debug(void)
+{
+ if (loopdev_debug_mask)
+ return;
+ __UL_INIT_DEBUG(loopdev, LOOPDEV_DEBUG_, 0, LOOPDEV_DEBUG);
+}
+
+/*
+ * see loopcxt_init()
+ */
+#define loopcxt_ioctl_enabled(_lc) (!((_lc)->flags & LOOPDEV_FL_NOIOCTL))
+#define loopcxt_sysfs_available(_lc) (!((_lc)->flags & LOOPDEV_FL_NOSYSFS)) \
+ && !loopcxt_ioctl_enabled(_lc)
+
+/*
+ * @lc: context
+ * @device: device name, absolute device path or NULL to reset the current setting
+ *
+ * Sets device, absolute paths (e.g. "/dev/loop<N>") are unchanged, device
+ * names ("loop<N>") are converted to the path (/dev/loop<N> or to
+ * /dev/loop/<N>)
+ *
+ * This sets the device name, but does not check if the device exists!
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_set_device(struct loopdev_cxt *lc, const char *device)
+{
+ if (!lc)
+ return -EINVAL;
+
+ if (lc->fd >= 0) {
+ close(lc->fd);
+ DBG(CXT, ul_debugobj(lc, "closing old open fd"));
+ }
+ lc->fd = -1;
+ lc->mode = 0;
+ lc->has_info = 0;
+ lc->info_failed = 0;
+ *lc->device = '\0';
+ memset(&lc->info, 0, sizeof(lc->info));
+
+ /* set new */
+ if (device) {
+ if (*device != '/') {
+ const char *dir = _PATH_DEV;
+
+ /* compose device name for /dev/loop<n> or /dev/loop/<n> */
+ if (lc->flags & LOOPDEV_FL_DEVSUBDIR) {
+ if (strlen(device) < 5)
+ return -1;
+ device += 4;
+ dir = _PATH_DEV_LOOP "/"; /* _PATH_DEV uses tailing slash */
+ }
+ snprintf(lc->device, sizeof(lc->device), "%s%s",
+ dir, device);
+ } else {
+ strncpy(lc->device, device, sizeof(lc->device));
+ lc->device[sizeof(lc->device) - 1] = '\0';
+ }
+ DBG(CXT, ul_debugobj(lc, "%s name assigned", device));
+ }
+
+ sysfs_deinit(&lc->sysfs);
+ return 0;
+}
+
+int loopcxt_has_device(struct loopdev_cxt *lc)
+{
+ return lc && *lc->device;
+}
+
+/*
+ * @lc: context
+ * @flags: LOOPDEV_FL_* flags
+ *
+ * Initilize loop handler.
+ *
+ * We have two sets of the flags:
+ *
+ * * LOOPDEV_FL_* flags control loopcxt_* API behavior
+ *
+ * * LO_FLAGS_* are kernel flags used for LOOP_{SET,GET}_STAT64 ioctls
+ *
+ * Note about LOOPDEV_FL_{RDONLY,RDWR} flags. These flags are used for open(2)
+ * syscall to open loop device. By default is the device open read-only.
+ *
+ * The expection is loopcxt_setup_device(), where the device is open read-write
+ * if LO_FLAGS_READ_ONLY flags is not set (see loopcxt_set_flags()).
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int loopcxt_init(struct loopdev_cxt *lc, int flags)
+{
+ int rc;
+ struct stat st;
+ struct loopdev_cxt dummy = UL_LOOPDEVCXT_EMPTY;
+
+ if (!lc)
+ return -EINVAL;
+
+ loopdev_init_debug();
+ DBG(CXT, ul_debugobj(lc, "initialize context"));
+
+ memcpy(lc, &dummy, sizeof(dummy));
+ lc->flags = flags;
+
+ rc = loopcxt_set_device(lc, NULL);
+ if (rc)
+ return rc;
+
+ if (stat(_PATH_SYS_BLOCK, &st) || !S_ISDIR(st.st_mode)) {
+ lc->flags |= LOOPDEV_FL_NOSYSFS;
+ lc->flags &= ~LOOPDEV_FL_NOIOCTL;
+ DBG(CXT, ul_debugobj(lc, "init: disable /sys usage"));
+ }
+
+ if (!(lc->flags & LOOPDEV_FL_NOSYSFS) &&
+ get_linux_version() >= KERNEL_VERSION(2,6,37)) {
+ /*
+ * Use only sysfs for basic information about loop devices
+ */
+ lc->flags |= LOOPDEV_FL_NOIOCTL;
+ DBG(CXT, ul_debugobj(lc, "init: ignore ioctls"));
+ }
+
+ if (!(lc->flags & LOOPDEV_FL_CONTROL) && !stat(_PATH_DEV_LOOPCTL, &st)) {
+ lc->flags |= LOOPDEV_FL_CONTROL;
+ DBG(CXT, ul_debugobj(lc, "init: loop-control detected "));
+ }
+
+ return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Deinitialize loop context
+ */
+void loopcxt_deinit(struct loopdev_cxt *lc)
+{
+ int errsv = errno;
+
+ if (!lc)
+ return;
+
+ DBG(CXT, ul_debugobj(lc, "de-initialize"));
+
+ free(lc->filename);
+ lc->filename = NULL;
+
+ ignore_result( loopcxt_set_device(lc, NULL) );
+ loopcxt_deinit_iterator(lc);
+
+ errno = errsv;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns newly allocated device path.
+ */
+char *loopcxt_strdup_device(struct loopdev_cxt *lc)
+{
+ if (!lc || !*lc->device)
+ return NULL;
+ return strdup(lc->device);
+}
+
+/*
+ * @lc: context
+ *
+ * Returns pointer device name in the @lc struct.
+ */
+const char *loopcxt_get_device(struct loopdev_cxt *lc)
+{
+ return lc && *lc->device ? lc->device : NULL;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns pointer to the sysfs context (see lib/sysfs.c)
+ */
+struct sysfs_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc)
+{
+ if (!lc || !*lc->device || (lc->flags & LOOPDEV_FL_NOSYSFS))
+ return NULL;
+
+ if (!lc->sysfs.devno) {
+ dev_t devno = sysfs_devname_to_devno(lc->device, NULL);
+ if (!devno) {
+ DBG(CXT, ul_debugobj(lc, "sysfs: failed devname to devno"));
+ return NULL;
+ }
+ if (sysfs_init(&lc->sysfs, devno, NULL)) {
+ DBG(CXT, ul_debugobj(lc, "sysfs: init failed"));
+ return NULL;
+ }
+ }
+
+ return &lc->sysfs;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: file descriptor to the open loop device or <0 on error. The mode
+ * depends on LOOPDEV_FL_{RDWR,RDONLY} context flags. Default is
+ * read-only.
+ */
+int loopcxt_get_fd(struct loopdev_cxt *lc)
+{
+ if (!lc || !*lc->device)
+ return -EINVAL;
+
+ if (lc->fd < 0) {
+ lc->mode = lc->flags & LOOPDEV_FL_RDWR ? O_RDWR : O_RDONLY;
+ lc->fd = open(lc->device, lc->mode | O_CLOEXEC);
+ DBG(CXT, ul_debugobj(lc, "open %s [%s]: %m", lc->device,
+ lc->flags & LOOPDEV_FL_RDWR ? "rw" : "ro"));
+ }
+ return lc->fd;
+}
+
+int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, int mode)
+{
+ if (!lc)
+ return -EINVAL;
+
+ lc->fd = fd;
+ lc->mode = mode;
+ return 0;
+}
+
+/*
+ * @lc: context
+ * @flags: LOOPITER_FL_* flags
+ *
+ * Iterator allows to scan list of the free or used loop devices.
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_init_iterator(struct loopdev_cxt *lc, int flags)
+{
+ struct loopdev_iter *iter;
+ struct stat st;
+
+ if (!lc)
+ return -EINVAL;
+
+
+ iter = &lc->iter;
+ DBG(ITER, ul_debugobj(iter, "initialize"));
+
+ /* always zeroize
+ */
+ memset(iter, 0, sizeof(*iter));
+ iter->ncur = -1;
+ iter->flags = flags;
+ iter->default_check = 1;
+
+ if (!lc->extra_check) {
+ /*
+ * Check for /dev/loop/<N> subdirectory
+ */
+ if (!(lc->flags & LOOPDEV_FL_DEVSUBDIR) &&
+ stat(_PATH_DEV_LOOP, &st) == 0 && S_ISDIR(st.st_mode))
+ lc->flags |= LOOPDEV_FL_DEVSUBDIR;
+
+ lc->extra_check = 1;
+ }
+ return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_deinit_iterator(struct loopdev_cxt *lc)
+{
+ struct loopdev_iter *iter;
+
+ if (!lc)
+ return -EINVAL;
+
+ iter = &lc->iter;
+ DBG(ITER, ul_debugobj(iter, "de-initialize"));
+
+ free(iter->minors);
+ if (iter->proc)
+ fclose(iter->proc);
+ if (iter->sysblock)
+ closedir(iter->sysblock);
+ iter->minors = NULL;
+ iter->proc = NULL;
+ iter->sysblock = NULL;
+ iter->done = 1;
+ return 0;
+}
+
+/*
+ * Same as loopcxt_set_device, but also checks if the device is
+ * associeted with any file.
+ *
+ * Returns: <0 on error, 0 on success, 1 device does not match with
+ * LOOPITER_FL_{USED,FREE} flags.
+ */
+static int loopiter_set_device(struct loopdev_cxt *lc, const char *device)
+{
+ int rc = loopcxt_set_device(lc, device);
+ int used;
+
+ if (rc)
+ return rc;
+
+ if (!(lc->iter.flags & LOOPITER_FL_USED) &&
+ !(lc->iter.flags & LOOPITER_FL_FREE))
+ return 0; /* caller does not care about device status */
+
+ if (!is_loopdev(lc->device)) {
+ DBG(ITER, ul_debugobj(&lc->iter, "%s does not exist", lc->device));
+ return -errno;
+ }
+
+ DBG(ITER, ul_debugobj(&lc->iter, "%s exist", lc->device));
+
+ used = loopcxt_get_offset(lc, NULL) == 0;
+
+ if ((lc->iter.flags & LOOPITER_FL_USED) && used)
+ return 0;
+
+ if ((lc->iter.flags & LOOPITER_FL_FREE) && !used)
+ return 0;
+
+ DBG(ITER, ul_debugobj(&lc->iter, "failed to use %s device", lc->device));
+
+ ignore_result( loopcxt_set_device(lc, NULL) );
+ return 1;
+}
+
+static int cmpnum(const void *p1, const void *p2)
+{
+ return (((* (int *) p1) > (* (int *) p2)) -
+ ((* (int *) p1) < (* (int *) p2)));
+}
+
+/*
+ * The classic scandir() is more expensive and less portable.
+ * We needn't full loop device names -- loop numbers (loop<N>)
+ * are enough.
+ */
+static int loop_scandir(const char *dirname, int **ary, int hasprefix)
+{
+ DIR *dir;
+ struct dirent *d;
+ unsigned int n, count = 0, arylen = 0;
+
+ if (!dirname || !ary)
+ return 0;
+
+ DBG(ITER, ul_debug("scan dir: %s", dirname));
+
+ dir = opendir(dirname);
+ if (!dir)
+ return 0;
+ free(*ary);
+ *ary = NULL;
+
+ while((d = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (d->d_type != DT_BLK && d->d_type != DT_UNKNOWN &&
+ d->d_type != DT_LNK)
+ continue;
+#endif
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+
+ if (hasprefix) {
+ /* /dev/loop<N> */
+ if (sscanf(d->d_name, "loop%u", &n) != 1)
+ continue;
+ } else {
+ /* /dev/loop/<N> */
+ char *end = NULL;
+
+ errno = 0;
+ n = strtol(d->d_name, &end, 10);
+ if (d->d_name == end || (end && *end) || errno)
+ continue;
+ }
+ if (n < LOOPDEV_DEFAULT_NNODES)
+ continue; /* ignore loop<0..7> */
+
+ if (count + 1 > arylen) {
+ int *tmp;
+
+ arylen += 1;
+
+ tmp = realloc(*ary, arylen * sizeof(int));
+ if (!tmp) {
+ free(*ary);
+ closedir(dir);
+ return -1;
+ }
+ *ary = tmp;
+ }
+ if (*ary)
+ (*ary)[count++] = n;
+ }
+ if (count && *ary)
+ qsort(*ary, count, sizeof(int), cmpnum);
+
+ closedir(dir);
+ return count;
+}
+
+/*
+ * Set the next *used* loop device according to /proc/partitions.
+ *
+ * Loop devices smaller than 512 bytes are invisible for this function.
+ */
+static int loopcxt_next_from_proc(struct loopdev_cxt *lc)
+{
+ struct loopdev_iter *iter = &lc->iter;
+ char buf[BUFSIZ];
+
+ DBG(ITER, ul_debugobj(iter, "scan /proc/partitions"));
+
+ if (!iter->proc)
+ iter->proc = fopen(_PATH_PROC_PARTITIONS, "r");
+ if (!iter->proc)
+ return 1;
+
+ while (fgets(buf, sizeof(buf), iter->proc)) {
+ unsigned int m;
+ char name[128 + 1];
+
+
+ if (sscanf(buf, " %u %*s %*s %128[^\n ]",
+ &m, name) != 2 || m != LOOPDEV_MAJOR)
+ continue;
+
+ DBG(ITER, ul_debugobj(iter, "checking %s", name));
+
+ if (loopiter_set_device(lc, name) == 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Set the next *used* loop device according to
+ * /sys/block/loopN/loop/backing_file (kernel >= 2.6.37 is required).
+ *
+ * This is preferred method.
+ */
+static int loopcxt_next_from_sysfs(struct loopdev_cxt *lc)
+{
+ struct loopdev_iter *iter = &lc->iter;
+ struct dirent *d;
+ int fd;
+
+ DBG(ITER, ul_debugobj(iter, "scanning /sys/block"));
+
+ if (!iter->sysblock)
+ iter->sysblock = opendir(_PATH_SYS_BLOCK);
+
+ if (!iter->sysblock)
+ return 1;
+
+ fd = dirfd(iter->sysblock);
+
+ while ((d = readdir(iter->sysblock))) {
+ char name[256];
+ struct stat st;
+
+ DBG(ITER, ul_debugobj(iter, "check %s", d->d_name));
+
+ if (strcmp(d->d_name, ".") == 0
+ || strcmp(d->d_name, "..") == 0
+ || strncmp(d->d_name, "loop", 4) != 0)
+ continue;
+
+ snprintf(name, sizeof(name), "%s/loop/backing_file", d->d_name);
+ if (fstat_at(fd, _PATH_SYS_BLOCK, name, &st, 0) != 0)
+ continue;
+
+ if (loopiter_set_device(lc, d->d_name) == 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * @lc: context, has to initialized by loopcxt_init_iterator()
+ *
+ * Returns: 0 on success, -1 on error, 1 at the end of scanning. The details
+ * about the current loop device are available by
+ * loopcxt_get_{fd,backing_file,device,offset, ...} functions.
+ */
+int loopcxt_next(struct loopdev_cxt *lc)
+{
+ struct loopdev_iter *iter;
+
+ if (!lc)
+ return -EINVAL;
+
+
+ iter = &lc->iter;
+ if (iter->done)
+ return 1;
+
+ DBG(ITER, ul_debugobj(iter, "next"));
+
+ /* A) Look for used loop devices in /proc/partitions ("losetup -a" only)
+ */
+ if (iter->flags & LOOPITER_FL_USED) {
+ int rc;
+
+ if (loopcxt_sysfs_available(lc))
+ rc = loopcxt_next_from_sysfs(lc);
+ else
+ rc = loopcxt_next_from_proc(lc);
+ if (rc == 0)
+ return 0;
+ goto done;
+ }
+
+ /* B) Classic way, try first eight loop devices (default number
+ * of loop devices). This is enough for 99% of all cases.
+ */
+ if (iter->default_check) {
+ DBG(ITER, ul_debugobj(iter, "next: default check"));
+ for (++iter->ncur; iter->ncur < LOOPDEV_DEFAULT_NNODES;
+ iter->ncur++) {
+ char name[16];
+ snprintf(name, sizeof(name), "loop%d", iter->ncur);
+
+ if (loopiter_set_device(lc, name) == 0)
+ return 0;
+ }
+ iter->default_check = 0;
+ }
+
+ /* C) the worst possibility, scan whole /dev or /dev/loop/<N>
+ */
+ if (!iter->minors) {
+ DBG(ITER, ul_debugobj(iter, "next: scanning /dev"));
+ iter->nminors = (lc->flags & LOOPDEV_FL_DEVSUBDIR) ?
+ loop_scandir(_PATH_DEV_LOOP, &iter->minors, 0) :
+ loop_scandir(_PATH_DEV, &iter->minors, 1);
+ iter->ncur = -1;
+ }
+ for (++iter->ncur; iter->ncur < iter->nminors; iter->ncur++) {
+ char name[16];
+ snprintf(name, sizeof(name), "loop%d", iter->minors[iter->ncur]);
+
+ if (loopiter_set_device(lc, name) == 0)
+ return 0;
+ }
+done:
+ loopcxt_deinit_iterator(lc);
+ return 1;
+}
+
+/*
+ * @device: path to device
+ */
+int is_loopdev(const char *device)
+{
+ struct stat st;
+
+ if (!device)
+ return 0;
+
+ return (stat(device, &st) == 0 &&
+ S_ISBLK(st.st_mode) &&
+ major(st.st_rdev) == LOOPDEV_MAJOR);
+}
+
+/*
+ * @lc: context
+ *
+ * Returns result from LOOP_GET_STAT64 ioctl or NULL on error.
+ */
+struct loop_info64 *loopcxt_get_info(struct loopdev_cxt *lc)
+{
+ int fd;
+
+ if (!lc || lc->info_failed) {
+ errno = EINVAL;
+ return NULL;
+ }
+ errno = 0;
+ if (lc->has_info)
+ return &lc->info;
+
+ fd = loopcxt_get_fd(lc);
+ if (fd < 0)
+ return NULL;
+
+ if (ioctl(fd, LOOP_GET_STATUS64, &lc->info) == 0) {
+ lc->has_info = 1;
+ lc->info_failed = 0;
+ DBG(CXT, ul_debugobj(lc, "reading loop_info64 OK"));
+ return &lc->info;
+ }
+
+ lc->info_failed = 1;
+ DBG(CXT, ul_debugobj(lc, "reading loop_info64 FAILED"));
+
+ return NULL;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns (allocated) string with path to the file assicieted
+ * with the current loop device.
+ */
+char *loopcxt_get_backing_file(struct loopdev_cxt *lc)
+{
+ struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+ char *res = NULL;
+
+ if (sysfs)
+ /*
+ * This is always preffered, the loop_info64
+ * has too small buffer for the filename.
+ */
+ res = sysfs_strdup(sysfs, "loop/backing_file");
+
+ if (!res && loopcxt_ioctl_enabled(lc)) {
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+
+ if (lo) {
+ lo->lo_file_name[LO_NAME_SIZE - 2] = '*';
+ lo->lo_file_name[LO_NAME_SIZE - 1] = '\0';
+ res = strdup((char *) lo->lo_file_name);
+ }
+ }
+
+ DBG(CXT, ul_debugobj(lc, "get_backing_file [%s]", res));
+ return res;
+}
+
+/*
+ * @lc: context
+ * @offset: returns offset number for the given device
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset)
+{
+ struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+ int rc = -EINVAL;
+
+ if (sysfs)
+ rc = sysfs_read_u64(sysfs, "loop/offset", offset);
+
+ if (rc && loopcxt_ioctl_enabled(lc)) {
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ if (lo) {
+ if (offset)
+ *offset = lo->lo_offset;
+ rc = 0;
+ } else
+ rc = -errno;
+ }
+
+ DBG(CXT, ul_debugobj(lc, "get_offset [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * @lc: context
+ * @sizelimit: returns size limit for the given device
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size)
+{
+ struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+ int rc = -EINVAL;
+
+ if (sysfs)
+ rc = sysfs_read_u64(sysfs, "loop/sizelimit", size);
+
+ if (rc && loopcxt_ioctl_enabled(lc)) {
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ if (lo) {
+ if (size)
+ *size = lo->lo_sizelimit;
+ rc = 0;
+ } else
+ rc = -errno;
+ }
+
+ DBG(CXT, ul_debugobj(lc, "get_sizelimit [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * @lc: context
+ * @devno: returns encryption type
+ *
+ * Cryptoloop is DEPRECATED!
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type)
+{
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ int rc;
+
+ /* not provided by sysfs */
+ if (lo) {
+ if (type)
+ *type = lo->lo_encrypt_type;
+ rc = 0;
+ } else
+ rc = -errno;
+
+ DBG(CXT, ul_debugobj(lc, "get_encrypt_type [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * @lc: context
+ * @devno: returns crypt name
+ *
+ * Cryptoloop is DEPRECATED!
+ *
+ * Returns: <0 on error, 0 on success
+ */
+const char *loopcxt_get_crypt_name(struct loopdev_cxt *lc)
+{
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+
+ if (lo)
+ return (char *) lo->lo_crypt_name;
+
+ DBG(CXT, ul_debugobj(lc, "get_crypt_name failed"));
+ return NULL;
+}
+
+/*
+ * @lc: context
+ * @devno: returns backing file devno
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_backing_devno(struct loopdev_cxt *lc, dev_t *devno)
+{
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ int rc;
+
+ if (lo) {
+ if (devno)
+ *devno = lo->lo_device;
+ rc = 0;
+ } else
+ rc = -errno;
+
+ DBG(CXT, ul_debugobj(lc, "get_backing_devno [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * @lc: context
+ * @ino: returns backing file inode
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_backing_inode(struct loopdev_cxt *lc, ino_t *ino)
+{
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ int rc;
+
+ if (lo) {
+ if (ino)
+ *ino = lo->lo_inode;
+ rc = 0;
+ } else
+ rc = -errno;
+
+ DBG(CXT, ul_debugobj(lc, "get_backing_inode [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * Check if the kernel supports partitioned loop devices.
+ *
+ * Notes:
+ * - kernels < 3.2 support partitioned loop devices and PT scanning
+ * only if max_part= module paremeter is non-zero
+ *
+ * - kernels >= 3.2 always support partitioned loop devices
+ *
+ * - kernels >= 3.2 always support BLKPG_{ADD,DEL}_PARTITION ioctls
+ *
+ * - kernels >= 3.2 enable PT scanner only if max_part= is non-zero or if the
+ * LO_FLAGS_PARTSCAN flag is set for the device. The PT scanner is disabled
+ * by default.
+ *
+ * See kernel commit e03c8dd14915fabc101aa495828d58598dc5af98.
+ */
+int loopmod_supports_partscan(void)
+{
+ int rc, ret = 0;
+ FILE *f;
+
+ if (get_linux_version() >= KERNEL_VERSION(3,2,0))
+ return 1;
+
+ f = fopen("/sys/module/loop/parameters/max_part", "r");
+ if (!f)
+ return 0;
+ rc = fscanf(f, "%d", &ret);
+ fclose(f);
+ return rc == 1 ? ret : 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: 1 if the partscan flags is set *or* (for old kernels) partitions
+ * scannig is enabled for all loop devices.
+ */
+int loopcxt_is_partscan(struct loopdev_cxt *lc)
+{
+ struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+
+ if (sysfs) {
+ /* kernel >= 3.2 */
+ int fl;
+ if (sysfs_read_int(sysfs, "loop/partscan", &fl) == 0)
+ return fl;
+ }
+
+ /* old kernels (including kernels without loopN/loop/<flags> directory */
+ return loopmod_supports_partscan();
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: 1 if the autoclear flags is set.
+ */
+int loopcxt_is_autoclear(struct loopdev_cxt *lc)
+{
+ struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+
+ if (sysfs) {
+ int fl;
+ if (sysfs_read_int(sysfs, "loop/autoclear", &fl) == 0)
+ return fl;
+ }
+
+ if (loopcxt_ioctl_enabled(lc)) {
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ if (lo)
+ return lo->lo_flags & LO_FLAGS_AUTOCLEAR;
+ }
+ return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: 1 if the readonly flags is set.
+ */
+int loopcxt_is_readonly(struct loopdev_cxt *lc)
+{
+ struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+
+ if (sysfs) {
+ int fl;
+ if (sysfs_read_int(sysfs, "ro", &fl) == 0)
+ return fl;
+ }
+
+ if (loopcxt_ioctl_enabled(lc)) {
+ struct loop_info64 *lo = loopcxt_get_info(lc);
+ if (lo)
+ return lo->lo_flags & LO_FLAGS_READ_ONLY;
+ }
+ return 0;
+}
+
+/*
+ * @lc: context
+ * @st: backing file stat or NULL
+ * @backing_file: filename
+ * @offset: offset
+ * @flags: LOOPDEV_FL_OFFSET if @offset should not be ignored
+ *
+ * Returns 1 if the current @lc loopdev is associated with the given backing
+ * file. Note that the preferred way is to use devno and inode number rather
+ * than filename. The @backing_file filename is poor solution usable in case
+ * that you don't have rights to call stat().
+ *
+ * Don't forget that old kernels provide very restricted (in size) backing
+ * filename by LOOP_GET_STAT64 ioctl only.
+ */
+int loopcxt_is_used(struct loopdev_cxt *lc,
+ struct stat *st,
+ const char *backing_file,
+ uint64_t offset,
+ int flags)
+{
+ ino_t ino;
+ dev_t dev;
+
+ if (!lc)
+ return 0;
+
+ DBG(CXT, ul_debugobj(lc, "checking %s vs. %s",
+ loopcxt_get_device(lc),
+ backing_file));
+
+ if (st && loopcxt_get_backing_inode(lc, &ino) == 0 &&
+ loopcxt_get_backing_devno(lc, &dev) == 0) {
+
+ if (ino == st->st_ino && dev == st->st_dev)
+ goto found;
+
+ /* don't use filename if we have devno and inode */
+ return 0;
+ }
+
+ /* poor man's solution */
+ if (backing_file) {
+ char *name = loopcxt_get_backing_file(lc);
+ int rc = name && strcmp(name, backing_file) == 0;
+
+ free(name);
+ if (rc)
+ goto found;
+ }
+
+ return 0;
+found:
+ if (flags & LOOPDEV_FL_OFFSET) {
+ uint64_t off;
+
+ return loopcxt_get_offset(lc, &off) == 0 && off == offset;
+ }
+ return 1;
+}
+
+/*
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ */
+int loopcxt_set_offset(struct loopdev_cxt *lc, uint64_t offset)
+{
+ if (!lc)
+ return -EINVAL;
+ lc->info.lo_offset = offset;
+
+ DBG(CXT, ul_debugobj(lc, "set offset=%jd", offset));
+ return 0;
+}
+
+/*
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ */
+int loopcxt_set_sizelimit(struct loopdev_cxt *lc, uint64_t sizelimit)
+{
+ if (!lc)
+ return -EINVAL;
+ lc->info.lo_sizelimit = sizelimit;
+
+ DBG(CXT, ul_debugobj(lc, "set sizelimit=%jd", sizelimit));
+ return 0;
+}
+
+/*
+ * @lc: context
+ * @flags: kernel LO_FLAGS_{READ_ONLY,USE_AOPS,AUTOCLEAR} flags
+ *
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags)
+{
+ if (!lc)
+ return -EINVAL;
+ lc->info.lo_flags = flags;
+
+ DBG(CXT, ul_debugobj(lc, "set flags=%u", (unsigned) flags));
+ return 0;
+}
+
+/*
+ * @lc: context
+ * @filename: backing file path (the path will be canonicalized)
+ *
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename)
+{
+ if (!lc)
+ return -EINVAL;
+
+ lc->filename = canonicalize_path(filename);
+ if (!lc->filename)
+ return -errno;
+
+ strncpy((char *)lc->info.lo_file_name, lc->filename, LO_NAME_SIZE);
+ lc->info.lo_file_name[LO_NAME_SIZE- 1] = '\0';
+
+ DBG(CXT, ul_debugobj(lc, "set backing file=%s", lc->info.lo_file_name));
+ return 0;
+}
+
+/*
+ * In kernels prior to v3.9, if the offset or sizelimit options
+ * are used, the block device's size won't be synced automatically.
+ * blockdev --getsize64 and filesystems will use the backing
+ * file size until the block device has been re-opened or the
+ * LOOP_SET_CAPACITY ioctl is called to sync the sizes.
+ *
+ * Since mount -oloop uses the LO_FLAGS_AUTOCLEAR option and passes
+ * the open file descriptor to the mount system call, we need to use
+ * the ioctl. Calling losetup directly doesn't have this problem since
+ * it closes the device when it exits and whatever consumes the device
+ * next will re-open it, causing the resync.
+ */
+static int loopcxt_check_size(struct loopdev_cxt *lc, int file_fd)
+{
+ uint64_t size, expected_size;
+ int dev_fd;
+ struct stat st;
+
+ if (!lc->info.lo_offset && !lc->info.lo_sizelimit)
+ return 0;
+
+ if (fstat(file_fd, &st)) {
+ DBG(CXT, ul_debugobj(lc, "failed to fstat backing file"));
+ return -errno;
+ }
+ if (S_ISBLK(st.st_mode)) {
+ if (blkdev_get_size(file_fd,
+ (unsigned long long *) &expected_size)) {
+ DBG(CXT, ul_debugobj(lc, "failed to determine device size"));
+ return -errno;
+ }
+ } else
+ expected_size = st.st_size;
+
+ if (expected_size == 0 || expected_size <= lc->info.lo_offset) {
+ DBG(CXT, ul_debugobj(lc, "failed to determine expected size"));
+ return 0; /* ignore this error */
+ }
+
+ if (lc->info.lo_offset > 0)
+ expected_size -= lc->info.lo_offset;
+
+ if (lc->info.lo_sizelimit > 0 && lc->info.lo_sizelimit < expected_size)
+ expected_size = lc->info.lo_sizelimit;
+
+ dev_fd = loopcxt_get_fd(lc);
+ if (dev_fd < 0) {
+ DBG(CXT, ul_debugobj(lc, "failed to get loop FD"));
+ return -errno;
+ }
+
+ if (blkdev_get_size(dev_fd, (unsigned long long *) &size)) {
+ DBG(CXT, ul_debugobj(lc, "failed to determine loopdev size"));
+ return -errno;
+ }
+
+ /* It's block device, so, align to 512-byte sectors */
+ if (expected_size % 512) {
+ DBG(CXT, ul_debugobj(lc, "expected size misaligned to 512-byte sectors"));
+ expected_size = (expected_size >> 9) << 9;
+ }
+
+ if (expected_size != size) {
+ DBG(CXT, ul_debugobj(lc, "warning: loopdev and expected "
+ "size dismatch (%ju/%ju)",
+ size, expected_size));
+
+ if (loopcxt_set_capacity(lc)) {
+ /* ioctl not available */
+ if (errno == ENOTTY || errno == EINVAL)
+ errno = ERANGE;
+ return -errno;
+ }
+
+ if (blkdev_get_size(dev_fd, (unsigned long long *) &size))
+ return -errno;
+
+ if (expected_size != size) {
+ errno = ERANGE;
+ DBG(CXT, ul_debugobj(lc, "failed to set loopdev size, "
+ "size: %ju, expected: %ju",
+ size, expected_size));
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * @cl: context
+ *
+ * Associate the current device (see loopcxt_{set,get}_device()) with
+ * a file (see loopcxt_set_backing_file()).
+ *
+ * The device is initialized read-write by default. If you want read-only
+ * device then set LO_FLAGS_READ_ONLY by loopcxt_set_flags(). The LOOPDEV_FL_*
+ * flags are ignored and modified according to LO_FLAGS_*.
+ *
+ * If the device is already open by loopcxt_get_fd() then this setup device
+ * function will re-open the device to fix read/write mode.
+ *
+ * The device is also initialized read-only if the backing file is not
+ * possible to open read-write (e.g. read-only FS).
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int loopcxt_setup_device(struct loopdev_cxt *lc)
+{
+ int file_fd, dev_fd, mode = O_RDWR, rc = -1, cnt = 0;
+
+ if (!lc || !*lc->device || !lc->filename)
+ return -EINVAL;
+
+ DBG(SETUP, ul_debugobj(lc, "device setup requested"));
+
+ /*
+ * Open backing file and device
+ */
+ if (lc->info.lo_flags & LO_FLAGS_READ_ONLY)
+ mode = O_RDONLY;
+
+ if ((file_fd = open(lc->filename, mode | O_CLOEXEC)) < 0) {
+ if (mode != O_RDONLY && (errno == EROFS || errno == EACCES))
+ file_fd = open(lc->filename, mode = O_RDONLY);
+
+ if (file_fd < 0) {
+ DBG(SETUP, ul_debugobj(lc, "open backing file failed: %m"));
+ return -errno;
+ }
+ }
+ DBG(SETUP, ul_debugobj(lc, "backing file open: OK"));
+
+ if (lc->fd != -1 && lc->mode != mode) {
+ DBG(SETUP, ul_debugobj(lc, "closing already open device (mode mismatch)"));
+ close(lc->fd);
+ lc->fd = -1;
+ lc->mode = 0;
+ }
+
+ if (mode == O_RDONLY) {
+ lc->flags |= LOOPDEV_FL_RDONLY; /* open() mode */
+ lc->info.lo_flags |= LO_FLAGS_READ_ONLY; /* kernel loopdev mode */
+ } else {
+ lc->flags |= LOOPDEV_FL_RDWR; /* open() mode */
+ lc->info.lo_flags &= ~LO_FLAGS_READ_ONLY;
+ lc->flags &= ~LOOPDEV_FL_RDONLY;
+ }
+
+ do {
+ errno = 0;
+ dev_fd = loopcxt_get_fd(lc);
+ if (dev_fd >= 0 || lc->control_ok == 0)
+ break;
+ if (errno != EACCES && errno != ENOENT)
+ break;
+ /* We have permissions to open /dev/loop-control, but open
+ * /dev/loopN failed with EACCES, it's probably because udevd
+ * does not applied chown yet. Let's wait a moment. */
+ usleep(25000);
+ } while (cnt++ < 16);
+
+ if (dev_fd < 0) {
+ rc = -errno;
+ goto err;
+ }
+
+ DBG(SETUP, ul_debugobj(lc, "device open: OK"));
+
+ /*
+ * Set FD
+ */
+ if (ioctl(dev_fd, LOOP_SET_FD, file_fd) < 0) {
+ rc = -errno;
+ DBG(SETUP, ul_debugobj(lc, "LOOP_SET_FD failed: %m"));
+ goto err;
+ }
+
+ DBG(SETUP, ul_debugobj(lc, "LOOP_SET_FD: OK"));
+
+ if (ioctl(dev_fd, LOOP_SET_STATUS64, &lc->info)) {
+ DBG(SETUP, ul_debugobj(lc, "LOOP_SET_STATUS64 failed: %m"));
+ goto err;
+ }
+
+ DBG(SETUP, ul_debugobj(lc, "LOOP_SET_STATUS64: OK"));
+
+ if ((rc = loopcxt_check_size(lc, file_fd)))
+ goto err;
+
+ close(file_fd);
+
+ memset(&lc->info, 0, sizeof(lc->info));
+ lc->has_info = 0;
+ lc->info_failed = 0;
+
+ DBG(SETUP, ul_debugobj(lc, "success [rc=0]"));
+ return 0;
+err:
+ if (file_fd >= 0)
+ close(file_fd);
+ if (dev_fd >= 0 && rc != -EBUSY)
+ ioctl(dev_fd, LOOP_CLR_FD, 0);
+
+ DBG(SETUP, ul_debugobj(lc, "failed [rc=%d]", rc));
+ return rc;
+}
+
+int loopcxt_set_capacity(struct loopdev_cxt *lc)
+{
+ int fd = loopcxt_get_fd(lc);
+
+ if (fd < 0)
+ return -EINVAL;
+
+ /* Kernels prior to v2.6.30 don't support this ioctl */
+ if (ioctl(fd, LOOP_SET_CAPACITY, 0) < 0) {
+ int rc = -errno;
+ DBG(CXT, ul_debugobj(lc, "LOOP_SET_CAPACITY failed: %m"));
+ return rc;
+ }
+
+ DBG(CXT, ul_debugobj(lc, "capacity set"));
+ return 0;
+}
+
+int loopcxt_delete_device(struct loopdev_cxt *lc)
+{
+ int fd = loopcxt_get_fd(lc);
+
+ if (fd < 0)
+ return -EINVAL;
+
+ if (ioctl(fd, LOOP_CLR_FD, 0) < 0) {
+ DBG(CXT, ul_debugobj(lc, "LOOP_CLR_FD failed: %m"));
+ return -errno;
+ }
+
+ DBG(CXT, ul_debugobj(lc, "device removed"));
+ return 0;
+}
+
+int loopcxt_add_device(struct loopdev_cxt *lc)
+{
+ int rc = -EINVAL;
+ int ctl, nr = -1;
+ const char *p, *dev = loopcxt_get_device(lc);
+
+ if (!dev)
+ goto done;
+
+ if (!(lc->flags & LOOPDEV_FL_CONTROL)) {
+ rc = -ENOSYS;
+ goto done;
+ }
+
+ p = strrchr(dev, '/');
+ if (!p || (sscanf(p, "/loop%d", &nr) != 1 && sscanf(p, "/%d", &nr) != 1)
+ || nr < 0)
+ goto done;
+
+ ctl = open(_PATH_DEV_LOOPCTL, O_RDWR|O_CLOEXEC);
+ if (ctl >= 0) {
+ DBG(CXT, ul_debugobj(lc, "add_device %d", nr));
+ rc = ioctl(ctl, LOOP_CTL_ADD, nr);
+ close(ctl);
+ }
+ lc->control_ok = rc >= 0 ? 1 : 0;
+done:
+ DBG(CXT, ul_debugobj(lc, "add_device done [rc=%d]", rc));
+ return rc;
+}
+
+/*
+ * Note that LOOP_CTL_GET_FREE ioctl is supported since kernel 3.1. In older
+ * kernels we have to check all loop devices to found unused one.
+ *
+ * See kernel commit 770fe30a46a12b6fb6b63fbe1737654d28e8484.
+ */
+int loopcxt_find_unused(struct loopdev_cxt *lc)
+{
+ int rc = -1;
+
+ DBG(CXT, ul_debugobj(lc, "find_unused requested"));
+
+ if (lc->flags & LOOPDEV_FL_CONTROL) {
+ int ctl = open(_PATH_DEV_LOOPCTL, O_RDWR|O_CLOEXEC);
+
+ if (ctl >= 0)
+ rc = ioctl(ctl, LOOP_CTL_GET_FREE);
+ if (rc >= 0) {
+ char name[16];
+ snprintf(name, sizeof(name), "loop%d", rc);
+
+ rc = loopiter_set_device(lc, name);
+ }
+ lc->control_ok = ctl >= 0 && rc == 0 ? 1 : 0;
+ if (ctl >= 0)
+ close(ctl);
+ DBG(CXT, ul_debugobj(lc, "find_unused by loop-control [rc=%d]", rc));
+ }
+
+ if (rc < 0) {
+ rc = loopcxt_init_iterator(lc, LOOPITER_FL_FREE);
+ if (rc)
+ return rc;
+
+ rc = loopcxt_next(lc);
+ loopcxt_deinit_iterator(lc);
+ DBG(CXT, ul_debugobj(lc, "find_unused by scan [rc=%d]", rc));
+ }
+ return rc;
+}
+
+
+
+/*
+ * Return: TRUE/FALSE
+ */
+int loopdev_is_autoclear(const char *device)
+{
+ struct loopdev_cxt lc;
+ int rc;
+
+ if (!device)
+ return 0;
+
+ rc = loopcxt_init(&lc, 0);
+ if (!rc)
+ rc = loopcxt_set_device(&lc, device);
+ if (!rc)
+ rc = loopcxt_is_autoclear(&lc);
+
+ loopcxt_deinit(&lc);
+ return rc;
+}
+
+char *loopdev_get_backing_file(const char *device)
+{
+ struct loopdev_cxt lc;
+ char *res = NULL;
+
+ if (!device)
+ return NULL;
+ if (loopcxt_init(&lc, 0))
+ return NULL;
+ if (loopcxt_set_device(&lc, device) == 0)
+ res = loopcxt_get_backing_file(&lc);
+
+ loopcxt_deinit(&lc);
+ return res;
+}
+
+/*
+ * Returns: TRUE/FALSE
+ */
+int loopdev_is_used(const char *device, const char *filename,
+ uint64_t offset, int flags)
+{
+ struct loopdev_cxt lc;
+ struct stat st;
+ int rc = 0;
+
+ if (!device || !filename)
+ return 0;
+
+ rc = loopcxt_init(&lc, 0);
+ if (!rc)
+ rc = loopcxt_set_device(&lc, device);
+ if (rc)
+ return rc;
+
+ rc = !stat(filename, &st);
+ rc = loopcxt_is_used(&lc, rc ? &st : NULL, filename, offset, flags);
+
+ loopcxt_deinit(&lc);
+ return rc;
+}
+
+int loopdev_delete(const char *device)
+{
+ struct loopdev_cxt lc;
+ int rc;
+
+ if (!device)
+ return -EINVAL;
+
+ rc = loopcxt_init(&lc, 0);
+ if (!rc)
+ rc = loopcxt_set_device(&lc, device);
+ if (!rc)
+ rc = loopcxt_delete_device(&lc);
+ loopcxt_deinit(&lc);
+ return rc;
+}
+
+/*
+ * Returns: 0 = success, < 0 error, 1 not found
+ */
+int loopcxt_find_by_backing_file(struct loopdev_cxt *lc, const char *filename,
+ uint64_t offset, int flags)
+{
+ int rc, hasst;
+ struct stat st;
+
+ if (!filename)
+ return -EINVAL;
+
+ hasst = !stat(filename, &st);
+
+ rc = loopcxt_init_iterator(lc, LOOPITER_FL_USED);
+ if (rc)
+ return rc;
+
+ while ((rc = loopcxt_next(lc)) == 0) {
+
+ if (loopcxt_is_used(lc, hasst ? &st : NULL,
+ filename, offset, flags))
+ break;
+ }
+
+ loopcxt_deinit_iterator(lc);
+ return rc;
+}
+
+/*
+ * Returns allocated string with device name
+ */
+char *loopdev_find_by_backing_file(const char *filename, uint64_t offset, int flags)
+{
+ struct loopdev_cxt lc;
+ char *res = NULL;
+
+ if (!filename)
+ return NULL;
+
+ if (loopcxt_init(&lc, 0))
+ return NULL;
+ if (loopcxt_find_by_backing_file(&lc, filename, offset, flags) == 0)
+ res = loopcxt_strdup_device(&lc);
+ loopcxt_deinit(&lc);
+
+ return res;
+}
+
+/*
+ * Returns number of loop devices associated with @file, if only one loop
+ * device is associeted with the given @filename and @loopdev is not NULL then
+ * @loopdev returns name of the device.
+ */
+int loopdev_count_by_backing_file(const char *filename, char **loopdev)
+{
+ struct loopdev_cxt lc;
+ int count = 0, rc;
+
+ if (!filename)
+ return -1;
+
+ rc = loopcxt_init(&lc, 0);
+ if (rc)
+ return rc;
+ if (loopcxt_init_iterator(&lc, LOOPITER_FL_USED))
+ return -1;
+
+ while(loopcxt_next(&lc) == 0) {
+ char *backing = loopcxt_get_backing_file(&lc);
+
+ if (!backing || strcmp(backing, filename)) {
+ free(backing);
+ continue;
+ }
+
+ free(backing);
+ if (loopdev && count == 0)
+ *loopdev = loopcxt_strdup_device(&lc);
+ count++;
+ }
+
+ loopcxt_deinit(&lc);
+
+ if (loopdev && count > 1) {
+ free(*loopdev);
+ *loopdev = NULL;
+ }
+ return count;
+}
+
diff --git a/libblkid/lib/mangle.c b/libblkid/lib/mangle.c
new file mode 100644
index 000000000..5236e97bf
--- /dev/null
+++ b/libblkid/lib/mangle.c
@@ -0,0 +1,166 @@
+/*
+ * Functions for \oct encoding used in mtab/fstab/swaps/etc.
+ *
+ * Based on code from mount(8).
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "mangle.h"
+#include "c.h"
+
+#define isoctal(a) (((a) & ~7) == '0')
+
+#define from_hex(c) (isdigit(c) ? c - '0' : tolower(c) - 'a' + 10)
+
+#define is_unwanted_char(x) (strchr(" \t\n\\", (unsigned int) x) != NULL)
+
+
+char *mangle(const char *s)
+{
+ char *ss, *sp;
+
+ if (!s)
+ return NULL;
+
+ ss = sp = malloc(4 * strlen(s) + 1);
+ if (!sp)
+ return NULL;
+ while(1) {
+ if (!*s) {
+ *sp = '\0';
+ break;
+ }
+ if (is_unwanted_char(*s)) {
+ *sp++ = '\\';
+ *sp++ = '0' + ((*s & 0300) >> 6);
+ *sp++ = '0' + ((*s & 070) >> 3);
+ *sp++ = '0' + (*s & 07);
+ } else
+ *sp++ = *s;
+ s++;
+ }
+ return ss;
+}
+
+
+void unmangle_to_buffer(const char *s, char *buf, size_t len)
+{
+ size_t sz = 0;
+
+ if (!s)
+ return;
+
+ while(*s && sz < len - 1) {
+ if (*s == '\\' && sz + 3 < len - 1 && isoctal(s[1]) &&
+ isoctal(s[2]) && isoctal(s[3])) {
+
+ *buf++ = 64*(s[1] & 7) + 8*(s[2] & 7) + (s[3] & 7);
+ s += 4;
+ sz += 4;
+ } else {
+ *buf++ = *s++;
+ sz++;
+ }
+ }
+ *buf = '\0';
+}
+
+void unhexmangle_to_buffer(const char *s, char *buf, size_t len)
+{
+ size_t sz = 0;
+
+ if (!s)
+ return;
+
+ while(*s && sz < len - 1) {
+ if (*s == '\\' && sz + 3 < len - 1 && s[1] == 'x' &&
+ isxdigit(s[2]) && isxdigit(s[3])) {
+
+ *buf++ = from_hex(s[2]) << 4 | from_hex(s[3]);
+ s += 4;
+ sz += 4;
+ } else {
+ *buf++ = *s++;
+ sz++;
+ }
+ }
+ *buf = '\0';
+}
+
+static inline char *skip_nonspaces(const char *s)
+{
+ while (*s && !(*s == ' ' || *s == '\t'))
+ s++;
+ return (char *) s;
+}
+
+/*
+ * Returns mallocated buffer or NULL in case of error.
+ */
+char *unmangle(const char *s, char **end)
+{
+ char *buf;
+ char *e;
+ size_t sz;
+
+ if (!s)
+ return NULL;
+
+ e = skip_nonspaces(s);
+ sz = e - s + 1;
+
+ if (end)
+ *end = e;
+ if (e == s)
+ return NULL; /* empty string */
+
+ buf = malloc(sz);
+ if (!buf)
+ return NULL;
+
+ unmangle_to_buffer(s, buf, sz);
+ return buf;
+}
+
+#ifdef TEST_PROGRAM
+#include <errno.h>
+int main(int argc, char *argv[])
+{
+ char *p = NULL;
+ if (argc < 3) {
+ fprintf(stderr, "usage: %s --mangle|unmangle <string>\n",
+ program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+
+ if (!strcmp(argv[1], "--mangle")) {
+ p = mangle(argv[2]);
+ printf("mangled: '%s'\n", p);
+ free(p);
+ }
+
+ else if (!strcmp(argv[1], "--unmangle")) {
+ char *x = unmangle(argv[2], NULL);
+
+ if (x) {
+ printf("unmangled: '%s'\n", x);
+ free(x);
+ }
+
+ x = strdup(argv[2]);
+ unmangle_to_buffer(x, x, strlen(x) + 1);
+
+ if (x) {
+ printf("self-unmangled: '%s'\n", x);
+ free(x);
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/lib/match.c b/libblkid/lib/match.c
new file mode 100644
index 000000000..9be82b0cc
--- /dev/null
+++ b/libblkid/lib/match.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <string.h>
+
+#include "match.h"
+
+/*
+ * match_fstype:
+ * @type: filesystem type
+ * @pattern: filesystem name or comma delimited list of names
+ *
+ * The @pattern list of filesystem can be prefixed with a global
+ * "no" prefix to invert matching of the whole list. The "no" could
+ * also be used for individual items in the @pattern list. So,
+ * "nofoo,bar" has the same meaning as "nofoo,nobar".
+ */
+int match_fstype(const char *type, const char *pattern)
+{
+ int no = 0; /* negated types list */
+ int len;
+ const char *p;
+
+ if (!pattern && !type)
+ return 1;
+ if (!pattern)
+ return 0;
+
+ if (!strncmp(pattern, "no", 2)) {
+ no = 1;
+ pattern += 2;
+ }
+
+ /* Does type occur in types, separated by commas? */
+ len = strlen(type);
+ p = pattern;
+ while(1) {
+ if (!strncmp(p, "no", 2) && !strncmp(p+2, type, len) &&
+ (p[len+2] == 0 || p[len+2] == ','))
+ return 0;
+ if (strncmp(p, type, len) == 0 && (p[len] == 0 || p[len] == ','))
+ return !no;
+ p = strchr(p,',');
+ if (!p)
+ break;
+ p++;
+ }
+ return no;
+}
diff --git a/libblkid/lib/mbsalign.c b/libblkid/lib/mbsalign.c
new file mode 100644
index 000000000..5e52e8f44
--- /dev/null
+++ b/libblkid/lib/mbsalign.c
@@ -0,0 +1,466 @@
+/* Align/Truncate a string in a given screen width
+ Copyright (C) 2009-2010 Free Software Foundation, Inc.
+
+ This program 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 program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Written by Pádraig Brady. */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <ctype.h>
+
+#include "c.h"
+#include "mbsalign.h"
+#include "widechar.h"
+
+#ifdef HAVE_WIDECHAR
+/* Replace non printable chars.
+ Note \t and \n etc. are non printable.
+ Return 1 if replacement made, 0 otherwise. */
+
+/*
+ * Counts number of cells in multibyte string. For all control and
+ * non-printable chars is the result width enlarged to store \x?? hex
+ * sequence. See mbs_safe_encode().
+ *
+ * Returns: number of cells, @sz returns number of bytes.
+ */
+size_t mbs_safe_nwidth(const char *buf, size_t bufsz, size_t *sz)
+{
+ mbstate_t st;
+ const char *p = buf, *last = buf;
+ size_t width = 0, bytes = 0;
+
+ memset(&st, 0, sizeof(st));
+
+ if (p && *p && bufsz)
+ last = p + (bufsz - 1);
+
+ while (p && *p && p <= last) {
+ if (iscntrl((unsigned char) *p)) {
+ width += 4, bytes += 4; /* *p encoded to \x?? */
+ p++;
+ }
+#ifdef HAVE_WIDECHAR
+ else {
+ wchar_t wc;
+ size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st);
+
+ if (len == 0)
+ break;
+
+ if (len == (size_t) -1 || len == (size_t) -2) {
+ len = 1;
+ if (isprint((unsigned char) *p))
+ width += 1, bytes += 1;
+ else
+ width += 4, bytes += 4;
+
+ } else if (!iswprint(wc)) {
+ width += len * 4; /* hex encode whole sequence */
+ bytes += len * 4;
+ } else {
+ width += wcwidth(wc); /* number of cells */
+ bytes += len; /* number of bytes */
+ }
+ p += len;
+ }
+#else
+ else if (!isprint((unsigned char) *p)) {
+ width += 4, bytes += 4; /* *p encoded to \x?? */
+ p++;
+ } else {
+ width++, bytes++;
+ p++;
+ }
+#endif
+ }
+
+ if (sz)
+ *sz = bytes;
+ return width;
+}
+
+size_t mbs_safe_width(const char *s)
+{
+ if (!s || !*s)
+ return 0;
+ return mbs_safe_nwidth(s, strlen(s), NULL);
+}
+
+/*
+ * Copy @s to @buf and replace control and non-printable chars with
+ * \x?? hex sequence. The @width returns number of cells.
+ *
+ * The @buf has to be big enough to store mbs_safe_encode_size(strlen(s)))
+ * bytes.
+ */
+char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf)
+{
+ mbstate_t st;
+ const char *p = s;
+ char *r;
+ size_t sz = s ? strlen(s) : 0;
+
+ if (!sz || !buf)
+ return NULL;
+
+ memset(&st, 0, sizeof(st));
+
+ r = buf;
+ *width = 0;
+
+ while (p && *p) {
+ if (iscntrl((unsigned char) *p)) {
+ sprintf(r, "\\x%02x", (unsigned char) *p);
+ r += 4;
+ *width += 4;
+ p++;
+ }
+#ifdef HAVE_WIDECHAR
+ else {
+ wchar_t wc;
+ size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st);
+
+ if (len == 0)
+ break; /* end of string */
+
+ if (len == (size_t) -1 || len == (size_t) -2) {
+ len = 1;
+ /*
+ * Not valid multibyte sequence -- maybe it's
+ * printable char according to the current locales.
+ */
+ if (!isprint((unsigned char) *p)) {
+ sprintf(r, "\\x%02x", (unsigned char) *p);
+ r += 4;
+ *width += 4;
+ } else {
+ width++;
+ *r++ = *p;
+ }
+ } else if (!iswprint(wc)) {
+ size_t i;
+ for (i = 0; i < len; i++) {
+ sprintf(r, "\\x%02x", (unsigned char) *p);
+ r += 4;
+ *width += 4;
+ }
+ } else {
+ memcpy(r, p, len);
+ r += len;
+ *width += wcwidth(wc);
+ }
+ p += len;
+ }
+#else
+ else if (!isprint((unsigned char) *p)) {
+ sprintf(r, "\\x%02x", (unsigned char) *p);
+ p++;
+ r += 4;
+ *width += 4;
+ } else {
+ *r++ = *p++;
+ *width++;
+ }
+#endif
+ }
+
+ *r = '\0';
+
+ return buf;
+}
+
+size_t mbs_safe_encode_size(size_t bytes)
+{
+ return (bytes * 4) + 1;
+}
+
+/*
+ * Returns allocated string where all control and non-printable chars are
+ * replaced with \x?? hex sequence.
+ */
+char *mbs_safe_encode(const char *s, size_t *width)
+{
+ size_t sz = s ? strlen(s) : 0;
+ char *buf;
+
+ if (!sz)
+ return NULL;
+ buf = malloc(mbs_safe_encode_size(sz));
+ if (!buf)
+ return NULL;
+
+ return mbs_safe_encode_to_buffer(s, width, buf);
+}
+
+static bool
+wc_ensure_printable (wchar_t *wchars)
+{
+ bool replaced = false;
+ wchar_t *wc = wchars;
+ while (*wc)
+ {
+ if (!iswprint ((wint_t) *wc))
+ {
+ *wc = 0xFFFD; /* L'\uFFFD' (replacement char) */
+ replaced = true;
+ }
+ wc++;
+ }
+ return replaced;
+}
+
+/* Truncate wchar string to width cells.
+ * Returns number of cells used. */
+
+static size_t
+wc_truncate (wchar_t *wc, size_t width)
+{
+ size_t cells = 0;
+ int next_cells = 0;
+
+ while (*wc)
+ {
+ next_cells = wcwidth (*wc);
+ if (next_cells == -1) /* non printable */
+ {
+ *wc = 0xFFFD; /* L'\uFFFD' (replacement char) */
+ next_cells = 1;
+ }
+ if (cells + next_cells > width)
+ break;
+ cells += next_cells;
+ wc++;
+ }
+ *wc = L'\0';
+ return cells;
+}
+
+/* FIXME: move this function to gnulib as it's missing on:
+ OpenBSD 3.8, IRIX 5.3, Solaris 2.5.1, mingw, BeOS */
+
+static int
+rpl_wcswidth (const wchar_t *s, size_t n)
+{
+ int ret = 0;
+
+ while (n-- > 0 && *s != L'\0')
+ {
+ int nwidth = wcwidth (*s++);
+ if (nwidth == -1) /* non printable */
+ return -1;
+ if (ret > (INT_MAX - nwidth)) /* overflow */
+ return -1;
+ ret += nwidth;
+ }
+
+ return ret;
+}
+#endif
+
+/* Truncate multi-byte string to @width and returns number of
+ * bytes of the new string @str, and in @width returns number
+ * of cells.
+ */
+size_t
+mbs_truncate(char *str, size_t *width)
+{
+ ssize_t bytes = strlen(str);
+#ifdef HAVE_WIDECHAR
+ ssize_t sz = mbstowcs(NULL, str, 0);
+ wchar_t *wcs = NULL;
+
+ if (sz == (ssize_t) -1)
+ goto done;
+
+ wcs = malloc((sz + 1) * sizeof(wchar_t));
+ if (!wcs)
+ goto done;
+
+ if (!mbstowcs(wcs, str, sz))
+ goto done;
+ *width = wc_truncate(wcs, *width);
+ bytes = wcstombs(str, wcs, bytes);
+done:
+ free(wcs);
+#else
+ if (*width < bytes)
+ bytes = *width;
+#endif
+ if (bytes >= 0)
+ str[bytes] = '\0';
+ return bytes;
+}
+
+/* Write N_SPACES space characters to DEST while ensuring
+ nothing is written beyond DEST_END. A terminating NUL
+ is always added to DEST.
+ A pointer to the terminating NUL is returned. */
+
+static char*
+mbs_align_pad (char *dest, const char* dest_end, size_t n_spaces)
+{
+ /* FIXME: Should we pad with "figure space" (\u2007)
+ if non ascii data present? */
+ for (/* nothing */; n_spaces && (dest < dest_end); n_spaces--)
+ *dest++ = ' ';
+ *dest = '\0';
+ return dest;
+}
+
+/* Align a string, SRC, in a field of *WIDTH columns, handling multi-byte
+ characters; write the result into the DEST_SIZE-byte buffer, DEST.
+ ALIGNMENT specifies whether to left- or right-justify or to center.
+ If SRC requires more than *WIDTH columns, truncate it to fit.
+ When centering, the number of trailing spaces may be one less than the
+ number of leading spaces. The FLAGS parameter is unused at present.
+ Return the length in bytes required for the final result, not counting
+ the trailing NUL. A return value of DEST_SIZE or larger means there
+ wasn't enough space. DEST will be NUL terminated in any case.
+ Return (size_t) -1 upon error (invalid multi-byte sequence in SRC,
+ or malloc failure), unless MBA_UNIBYTE_FALLBACK is specified.
+ Update *WIDTH to indicate how many columns were used before padding. */
+
+size_t
+mbsalign (const char *src, char *dest, size_t dest_size,
+ size_t *width, mbs_align_t align, int flags)
+{
+ size_t ret = -1;
+ size_t src_size = strlen (src) + 1;
+ char *newstr = NULL;
+ wchar_t *str_wc = NULL;
+ const char *str_to_print = src;
+ size_t n_cols = src_size - 1;
+ size_t n_used_bytes = n_cols; /* Not including NUL */
+ size_t n_spaces = 0, space_left;
+ bool conversion = false;
+ bool wc_enabled = false;
+
+#ifdef HAVE_WIDECHAR
+ /* In multi-byte locales convert to wide characters
+ to allow easy truncation. Also determine number
+ of screen columns used. */
+ if (MB_CUR_MAX > 1)
+ {
+ size_t src_chars = mbstowcs (NULL, src, 0);
+ if (src_chars == (size_t) -1)
+ {
+ if (flags & MBA_UNIBYTE_FALLBACK)
+ goto mbsalign_unibyte;
+ else
+ goto mbsalign_cleanup;
+ }
+ src_chars += 1; /* make space for NUL */
+ str_wc = malloc (src_chars * sizeof (wchar_t));
+ if (str_wc == NULL)
+ {
+ if (flags & MBA_UNIBYTE_FALLBACK)
+ goto mbsalign_unibyte;
+ else
+ goto mbsalign_cleanup;
+ }
+ if (mbstowcs (str_wc, src, src_chars) != 0)
+ {
+ str_wc[src_chars - 1] = L'\0';
+ wc_enabled = true;
+ conversion = wc_ensure_printable (str_wc);
+ n_cols = rpl_wcswidth (str_wc, src_chars);
+ }
+ }
+
+ /* If we transformed or need to truncate the source string
+ then create a modified copy of it. */
+ if (wc_enabled && (conversion || (n_cols > *width)))
+ {
+ if (conversion)
+ {
+ /* May have increased the size by converting
+ \t to \uFFFD for example. */
+ src_size = wcstombs(NULL, str_wc, 0) + 1;
+ }
+ newstr = malloc (src_size);
+ if (newstr == NULL)
+ {
+ if (flags & MBA_UNIBYTE_FALLBACK)
+ goto mbsalign_unibyte;
+ else
+ goto mbsalign_cleanup;
+ }
+ str_to_print = newstr;
+ n_cols = wc_truncate (str_wc, *width);
+ n_used_bytes = wcstombs (newstr, str_wc, src_size);
+ }
+#endif
+
+mbsalign_unibyte:
+
+ if (n_cols > *width) /* Unibyte truncation required. */
+ {
+ n_cols = *width;
+ n_used_bytes = n_cols;
+ }
+
+ if (*width > n_cols) /* Padding required. */
+ n_spaces = *width - n_cols;
+
+ /* indicate to caller how many cells needed (not including padding). */
+ *width = n_cols;
+
+ /* indicate to caller how many bytes needed (not including NUL). */
+ ret = n_used_bytes + (n_spaces * 1);
+
+ /* Write as much NUL terminated output to DEST as possible. */
+ if (dest_size != 0)
+ {
+ char *dest_end = dest + dest_size - 1;
+ size_t start_spaces;
+ size_t end_spaces;
+
+ switch (align)
+ {
+ case MBS_ALIGN_CENTER:
+ start_spaces = n_spaces / 2 + n_spaces % 2;
+ end_spaces = n_spaces / 2;
+ break;
+ case MBS_ALIGN_LEFT:
+ start_spaces = 0;
+ end_spaces = n_spaces;
+ break;
+ case MBS_ALIGN_RIGHT:
+ start_spaces = n_spaces;
+ end_spaces = 0;
+ break;
+ default:
+ abort();
+ }
+
+ dest = mbs_align_pad (dest, dest_end, start_spaces);
+ space_left = dest_end - dest;
+ dest = memcpy (dest, str_to_print, min (n_used_bytes, space_left));
+ mbs_align_pad (dest, dest_end, end_spaces);
+ }
+
+mbsalign_cleanup:
+
+ free (str_wc);
+ free (newstr);
+
+ return ret;
+}
diff --git a/libblkid/lib/md5.c b/libblkid/lib/md5.c
new file mode 100644
index 000000000..488d16ef6
--- /dev/null
+++ b/libblkid/lib/md5.c
@@ -0,0 +1,257 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+#include <string.h> /* for memcpy() */
+
+#include "md5.h"
+
+#if !defined(WORDS_BIGENDIAN)
+#define byteReverse(buf, len) /* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32_t t;
+ do {
+ t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32_t *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+ uint32_t t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[MD5LENGTH], struct MD5Context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform.
+ * Use memcpy to avoid aliasing problems. On most systems,
+ * this will be optimized away to the same code.
+ */
+ memcpy(&ctx->in[14 * sizeof(uint32_t)], &ctx->bits[0], 4);
+ memcpy(&ctx->in[15 * sizeof(uint32_t)], &ctx->bits[1], 4);
+
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, MD5LENGTH);
+ memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+ register uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif
+
diff --git a/libblkid/lib/monotonic.c b/libblkid/lib/monotonic.c
new file mode 100644
index 000000000..3d4a4438e
--- /dev/null
+++ b/libblkid/lib/monotonic.c
@@ -0,0 +1,68 @@
+/*
+ * Please, don't add this file to libcommon because clock_gettime() requires
+ * -lrt on systems with old libc.
+ */
+#include <time.h>
+#include <sys/sysinfo.h>
+#include <sys/time.h>
+
+#include "c.h"
+#include "nls.h"
+#include "monotonic.h"
+
+int get_boot_time(struct timeval *boot_time)
+{
+#ifdef CLOCK_BOOTTIME
+ struct timespec hires_uptime;
+ struct timeval lores_uptime;
+#endif
+ struct timeval now;
+#ifdef HAVE_SYSINFO
+ struct sysinfo info;
+#endif
+
+ if (gettimeofday(&now, NULL) != 0) {
+ warn(_("gettimeofday failed"));
+ return -errno;
+ }
+#ifdef CLOCK_BOOTTIME
+ if (clock_gettime(CLOCK_BOOTTIME, &hires_uptime) == 0) {
+ TIMESPEC_TO_TIMEVAL(&lores_uptime, &hires_uptime);
+ timersub(&now, &lores_uptime, boot_time);
+ return 0;
+ }
+#endif
+#ifdef HAVE_SYSINFO
+ /* fallback */
+ if (sysinfo(&info) != 0)
+ warn(_("sysinfo failed"));
+
+ boot_time->tv_sec = now.tv_sec - info.uptime;
+ boot_time->tv_usec = 0;
+ return 0;
+#else
+ return -ENOSYS;
+#endif
+}
+
+int gettime_monotonic(struct timeval *tv)
+{
+#ifdef CLOCK_MONOTONIC
+ /* Can slew only by ntp and adjtime */
+ int ret;
+ struct timespec ts;
+
+# ifdef CLOCK_MONOTONIC_RAW
+ /* Linux specific, cant slew */
+ if (!(ret = clock_gettime(CLOCK_MONOTONIC_RAW, &ts))) {
+# else
+ if (!(ret = clock_gettime(CLOCK_MONOTONIC, &ts))) {
+# endif
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / 1000;
+ }
+ return ret;
+#else
+ return gettimeofday(tv, NULL);
+#endif
+}
diff --git a/libblkid/lib/pager.c b/libblkid/lib/pager.c
new file mode 100644
index 000000000..9e09cd52b
--- /dev/null
+++ b/libblkid/lib/pager.c
@@ -0,0 +1,210 @@
+/*
+ * Based on linux-perf/git scm
+ *
+ * Some modifications and simplifications for util-linux
+ * by Davidlohr Bueso <dave@xxxxxxx> - March 2012.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "c.h"
+#include "xalloc.h"
+#include "nls.h"
+
+#define NULL_DEVICE "/dev/null"
+
+void setup_pager(void);
+
+static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
+
+struct child_process {
+ const char **argv;
+ pid_t pid;
+ int in;
+ int out;
+ int err;
+ unsigned no_stdin:1;
+ void (*preexec_cb)(void);
+};
+static struct child_process pager_process;
+
+static inline void close_pair(int fd[2])
+{
+ close(fd[0]);
+ close(fd[1]);
+}
+
+static int start_command(struct child_process *cmd)
+{
+ int need_in;
+ int fdin[2];
+
+ /*
+ * In case of errors we must keep the promise to close FDs
+ * that have been passed in via ->in and ->out.
+ */
+ need_in = !cmd->no_stdin && cmd->in < 0;
+ if (need_in) {
+ if (pipe(fdin) < 0) {
+ if (cmd->out > 0)
+ close(cmd->out);
+ return -1;
+ }
+ cmd->in = fdin[1];
+ }
+
+ fflush(NULL);
+ cmd->pid = fork();
+ if (!cmd->pid) {
+ if (need_in) {
+ dup2(fdin[0], STDIN_FILENO);
+ close_pair(fdin);
+ } else if (cmd->in > 0) {
+ dup2(cmd->in, STDIN_FILENO);
+ close(cmd->in);
+ }
+
+ cmd->preexec_cb();
+ execvp(cmd->argv[0], (char *const*) cmd->argv);
+ exit(127); /* cmd not found */
+ }
+
+ if (cmd->pid < 0) {
+ if (need_in)
+ close_pair(fdin);
+ else if (cmd->in)
+ close(cmd->in);
+ return -1;
+ }
+
+ if (need_in)
+ close(fdin[0]);
+ else if (cmd->in)
+ close(cmd->in);
+ return 0;
+}
+
+static int wait_or_whine(pid_t pid)
+{
+ for (;;) {
+ int status, code;
+ pid_t waiting = waitpid(pid, &status, 0);
+
+ if (waiting < 0) {
+ if (errno == EINTR)
+ continue;
+ err(EXIT_FAILURE, _("waitpid failed (%s)"), strerror(errno));
+ }
+ if (waiting != pid)
+ return -1;
+ if (WIFSIGNALED(status))
+ return -1;
+
+ if (!WIFEXITED(status))
+ return -1;
+ code = WEXITSTATUS(status);
+ switch (code) {
+ case 127:
+ return -1;
+ case 0:
+ return 0;
+ default:
+ return -1;
+ }
+ }
+}
+
+static int finish_command(struct child_process *cmd)
+{
+ return wait_or_whine(cmd->pid);
+}
+
+static void pager_preexec(void)
+{
+ /*
+ * Work around bug in "less" by not starting it until we
+ * have real input
+ */
+ fd_set in;
+
+ FD_ZERO(&in);
+ FD_SET(STDIN_FILENO, &in);
+ select(1, &in, NULL, &in, NULL);
+
+ setenv("LESS", "FRSX", 0);
+}
+
+static void wait_for_pager(void)
+{
+ fflush(stdout);
+ fflush(stderr);
+ /* signal EOF to pager */
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ finish_command(&pager_process);
+}
+
+static void wait_for_pager_signal(int signo)
+{
+ wait_for_pager();
+ raise(signo);
+}
+
+void setup_pager(void)
+{
+ const char *pager = getenv("PAGER");
+
+ if (!isatty(STDOUT_FILENO))
+ return;
+
+ if (!pager)
+ pager = "less";
+ else if (!*pager || !strcmp(pager, "cat"))
+ return;
+
+ /* spawn the pager */
+ pager_argv[2] = pager;
+ pager_process.argv = pager_argv;
+ pager_process.in = -1;
+ pager_process.preexec_cb = pager_preexec;
+
+ if (start_command(&pager_process))
+ return;
+
+ /* original process continues, but writes to the pipe */
+ dup2(pager_process.in, STDOUT_FILENO);
+ if (isatty(STDERR_FILENO))
+ dup2(pager_process.in, STDERR_FILENO);
+ close(pager_process.in);
+
+ /* this makes sure that the parent terminates after the pager */
+ signal(SIGINT, wait_for_pager_signal);
+ signal(SIGHUP, wait_for_pager_signal);
+ signal(SIGTERM, wait_for_pager_signal);
+ signal(SIGQUIT, wait_for_pager_signal);
+ signal(SIGPIPE, wait_for_pager_signal);
+
+ atexit(wait_for_pager);
+}
+
+#ifdef TEST_PROGRAM
+
+#define MAX 255
+
+int main(int argc __attribute__ ((__unused__)),
+ char *argv[] __attribute__ ((__unused__)))
+{
+ int i;
+
+ setup_pager();
+ for (i = 0; i < MAX; i++)
+ printf("%d\n", i);
+ return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/lib/path.c b/libblkid/lib/path.c
new file mode 100644
index 000000000..fc90c0a16
--- /dev/null
+++ b/libblkid/lib/path.c
@@ -0,0 +1,258 @@
+/*
+ * Simple functions to access files, paths maybe be globally prefixed by a
+ * global prefix to read data from alternative destination (e.g. /proc dump for
+ * regression tests).
+ *
+ * Taken from lscpu.c
+ *
+ * Copyright (C) 2008 Cai Qian <qcai@redhat.com>
+ * Copyright (C) 2008-2012 Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include "all-io.h"
+#include "path.h"
+#include "nls.h"
+#include "c.h"
+
+static size_t prefixlen;
+static char pathbuf[PATH_MAX];
+
+static const char *
+path_vcreate(const char *path, va_list ap)
+{
+ if (prefixlen)
+ vsnprintf(pathbuf + prefixlen,
+ sizeof(pathbuf) - prefixlen, path, ap);
+ else
+ vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
+ return pathbuf;
+}
+
+char *
+path_strdup(const char *path, ...)
+{
+ const char *p;
+ va_list ap;
+
+ va_start(ap, path);
+ p = path_vcreate(path, ap);
+ va_end(ap);
+
+ return p ? strdup(p) : NULL;
+}
+
+static FILE *
+path_vfopen(const char *mode, int exit_on_error, const char *path, va_list ap)
+{
+ FILE *f;
+ const char *p = path_vcreate(path, ap);
+
+ f = fopen(p, mode);
+ if (!f && exit_on_error)
+ err(EXIT_FAILURE, _("cannot open %s"), p);
+ return f;
+}
+
+static int
+path_vopen(int flags, const char *path, va_list ap)
+{
+ int fd;
+ const char *p = path_vcreate(path, ap);
+
+ fd = open(p, flags);
+ if (fd == -1)
+ err(EXIT_FAILURE, _("cannot open %s"), p);
+ return fd;
+}
+
+FILE *
+path_fopen(const char *mode, int exit_on_error, const char *path, ...)
+{
+ FILE *fd;
+ va_list ap;
+
+ va_start(ap, path);
+ fd = path_vfopen(mode, exit_on_error, path, ap);
+ va_end(ap);
+
+ return fd;
+}
+
+void
+path_read_str(char *result, size_t len, const char *path, ...)
+{
+ FILE *fd;
+ va_list ap;
+
+ va_start(ap, path);
+ fd = path_vfopen("r", 1, path, ap);
+ va_end(ap);
+
+ if (!fgets(result, len, fd))
+ err(EXIT_FAILURE, _("cannot read %s"), pathbuf);
+ fclose(fd);
+
+ len = strlen(result);
+ if (result[len - 1] == '\n')
+ result[len - 1] = '\0';
+}
+
+int
+path_read_s32(const char *path, ...)
+{
+ FILE *fd;
+ va_list ap;
+ int result;
+
+ va_start(ap, path);
+ fd = path_vfopen("r", 1, path, ap);
+ va_end(ap);
+
+ if (fscanf(fd, "%d", &result) != 1) {
+ if (ferror(fd))
+ err(EXIT_FAILURE, _("cannot read %s"), pathbuf);
+ else
+ errx(EXIT_FAILURE, _("parse error: %s"), pathbuf);
+ }
+ fclose(fd);
+ return result;
+}
+
+uint64_t
+path_read_u64(const char *path, ...)
+{
+ FILE *fd;
+ va_list ap;
+ uint64_t result;
+
+ va_start(ap, path);
+ fd = path_vfopen("r", 1, path, ap);
+ va_end(ap);
+
+ if (fscanf(fd, "%"SCNu64, &result) != 1) {
+ if (ferror(fd))
+ err(EXIT_FAILURE, _("cannot read %s"), pathbuf);
+ else
+ errx(EXIT_FAILURE, _("parse error: %s"), pathbuf);
+ }
+ fclose(fd);
+ return result;
+}
+
+int
+path_write_str(const char *str, const char *path, ...)
+{
+ int fd, result;
+ va_list ap;
+
+ va_start(ap, path);
+ fd = path_vopen(O_WRONLY|O_CLOEXEC, path, ap);
+ va_end(ap);
+ result = write_all(fd, str, strlen(str));
+ close(fd);
+ return result;
+}
+
+int
+path_exist(const char *path, ...)
+{
+ va_list ap;
+ const char *p;
+
+ va_start(ap, path);
+ p = path_vcreate(path, ap);
+ va_end(ap);
+
+ return access(p, F_OK) == 0;
+}
+
+#ifdef HAVE_CPU_SET_T
+
+static cpu_set_t *
+path_cpuparse(int maxcpus, int islist, const char *path, va_list ap)
+{
+ FILE *fd;
+ cpu_set_t *set;
+ size_t setsize, len = maxcpus * 7;
+ char buf[len];
+
+ fd = path_vfopen("r", 1, path, ap);
+
+ if (!fgets(buf, len, fd))
+ err(EXIT_FAILURE, _("cannot read %s"), pathbuf);
+ fclose(fd);
+
+ len = strlen(buf);
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+
+ set = cpuset_alloc(maxcpus, &setsize, NULL);
+ if (!set)
+ err(EXIT_FAILURE, _("failed to callocate cpu set"));
+
+ if (islist) {
+ if (cpulist_parse(buf, set, setsize, 0))
+ errx(EXIT_FAILURE, _("failed to parse CPU list %s"), buf);
+ } else {
+ if (cpumask_parse(buf, set, setsize))
+ errx(EXIT_FAILURE, _("failed to parse CPU mask %s"), buf);
+ }
+ return set;
+}
+
+cpu_set_t *
+path_read_cpuset(int maxcpus, const char *path, ...)
+{
+ va_list ap;
+ cpu_set_t *set;
+
+ va_start(ap, path);
+ set = path_cpuparse(maxcpus, 0, path, ap);
+ va_end(ap);
+
+ return set;
+}
+
+cpu_set_t *
+path_read_cpulist(int maxcpus, const char *path, ...)
+{
+ va_list ap;
+ cpu_set_t *set;
+
+ va_start(ap, path);
+ set = path_cpuparse(maxcpus, 1, path, ap);
+ va_end(ap);
+
+ return set;
+}
+
+#endif /* HAVE_CPU_SET_T */
+
+void
+path_set_prefix(const char *prefix)
+{
+ prefixlen = strlen(prefix);
+ strncpy(pathbuf, prefix, sizeof(pathbuf));
+ pathbuf[sizeof(pathbuf) - 1] = '\0';
+}
diff --git a/libblkid/lib/procutils.c b/libblkid/lib/procutils.c
new file mode 100644
index 000000000..ef969417d
--- /dev/null
+++ b/libblkid/lib/procutils.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2011 Davidlohr Bueso <dave@gnu.org>
+ *
+ * procutils.c: General purpose procfs parsing utilities
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 Library Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#include "procutils.h"
+#include "at.h"
+#include "c.h"
+
+/*
+ * @pid: process ID for which we want to obtain the threads group
+ *
+ * Returns: newly allocated tasks structure
+ */
+struct proc_tasks *proc_open_tasks(pid_t pid)
+{
+ struct proc_tasks *tasks;
+ char path[PATH_MAX];
+
+ sprintf(path, "/proc/%d/task/", pid);
+
+ tasks = malloc(sizeof(struct proc_tasks));
+ if (tasks) {
+ tasks->dir = opendir(path);
+ if (tasks->dir)
+ return tasks;
+ }
+
+ free(tasks);
+ return NULL;
+}
+
+/*
+ * @tasks: allocated tasks structure
+ *
+ * Returns: nothing
+ */
+void proc_close_tasks(struct proc_tasks *tasks)
+{
+ if (tasks && tasks->dir)
+ closedir(tasks->dir);
+ free(tasks);
+}
+
+/*
+ * @tasks: allocated task structure
+ * @tid: [output] one of the thread IDs belonging to the thread group
+ * If when an error occurs, it is set to 0.
+ *
+ * Returns: 0 on success, 1 on end, -1 on failure or no more threads
+ */
+int proc_next_tid(struct proc_tasks *tasks, pid_t *tid)
+{
+ struct dirent *d;
+ char *end;
+
+ if (!tasks || !tid)
+ return -EINVAL;
+
+ *tid = 0;
+ errno = 0;
+
+ do {
+ d = readdir(tasks->dir);
+ if (!d)
+ return errno ? -1 : 1; /* error or end-of-dir */
+
+ if (!isdigit((unsigned char) *d->d_name))
+ continue;
+ errno = 0;
+ *tid = (pid_t) strtol(d->d_name, &end, 10);
+ if (errno || d->d_name == end || (end && *end))
+ return -1;
+
+ } while (!*tid);
+
+ return 0;
+}
+
+struct proc_processes *proc_open_processes(void)
+{
+ struct proc_processes *ps;
+
+ ps = calloc(1, sizeof(struct proc_processes));
+ if (ps) {
+ ps->dir = opendir("/proc");
+ if (ps->dir)
+ return ps;
+ }
+
+ free(ps);
+ return NULL;
+}
+
+void proc_close_processes(struct proc_processes *ps)
+{
+ if (ps && ps->dir)
+ closedir(ps->dir);
+ free(ps);
+}
+
+void proc_processes_filter_by_name(struct proc_processes *ps, const char *name)
+{
+ ps->fltr_name = name;
+ ps->has_fltr_name = name ? 1 : 0;
+}
+
+void proc_processes_filter_by_uid(struct proc_processes *ps, uid_t uid)
+{
+ ps->fltr_uid = uid;
+ ps->has_fltr_uid = 1;
+}
+
+int proc_next_pid(struct proc_processes *ps, pid_t *pid)
+{
+ struct dirent *d;
+
+ if (!ps || !pid)
+ return -EINVAL;
+
+ *pid = 0;
+ errno = 0;
+
+ do {
+ char buf[BUFSIZ], *p;
+
+ d = readdir(ps->dir);
+ if (!d)
+ return errno ? -1 : 1; /* error or end-of-dir */
+
+
+ if (!isdigit((unsigned char) *d->d_name))
+ continue;
+
+ /* filter out by UID */
+ if (ps->has_fltr_uid) {
+ struct stat st;
+
+ if (fstat_at(dirfd(ps->dir), "/proc", d->d_name, &st, 0))
+ continue;
+ if (ps->fltr_uid != st.st_uid)
+ continue;
+ }
+
+ /* filter out by NAME */
+ if (ps->has_fltr_name) {
+ char procname[256];
+ FILE *f;
+
+ snprintf(buf, sizeof(buf), "%s/stat", d->d_name);
+ f = fopen_at(dirfd(ps->dir), "/proc", buf,
+ O_CLOEXEC|O_RDONLY, "r");
+ if (!f)
+ continue;
+
+ p = fgets(buf, sizeof(buf), f);
+ fclose(f);
+ if (!p)
+ continue;
+
+ if (sscanf(buf, "%*d (%255[^)])", procname) != 1)
+ continue;
+
+ /* ok, we got the process name. */
+ if (strcmp(procname, ps->fltr_name) != 0)
+ continue;
+ }
+
+ p = NULL;
+ errno = 0;
+ *pid = (pid_t) strtol(d->d_name, &p, 10);
+ if (errno || d->d_name == p || (p && *p))
+ return errno ? -errno : -1;
+
+ return 0;
+ } while (1);
+
+ return 0;
+}
+
+#ifdef TEST_PROGRAM
+
+static int test_tasks(int argc, char *argv[])
+{
+ pid_t tid, pid;
+ struct proc_tasks *ts;
+
+ if (argc != 2)
+ return EXIT_FAILURE;
+
+ pid = strtol(argv[1], (char **) NULL, 10);
+ printf("PID=%d, TIDs:", pid);
+
+ ts = proc_open_tasks(pid);
+ if (!ts)
+ err(EXIT_FAILURE, "open list of tasks failed");
+
+ while (proc_next_tid(ts, &tid) == 0)
+ printf(" %d", tid);
+
+ printf("\n");
+ proc_close_tasks(ts);
+ return EXIT_SUCCESS;
+}
+
+static int test_processes(int argc, char *argv[])
+{
+ pid_t pid;
+ struct proc_processes *ps;
+
+ ps = proc_open_processes();
+ if (!ps)
+ err(EXIT_FAILURE, "open list of processes failed");
+
+ if (argc >= 3 && strcmp(argv[1], "--name") == 0)
+ proc_processes_filter_by_name(ps, argv[2]);
+
+ if (argc >= 3 && strcmp(argv[1], "--uid") == 0)
+ proc_processes_filter_by_uid(ps, (uid_t) atol(argv[2]));
+
+ while (proc_next_pid(ps, &pid) == 0)
+ printf(" %d", pid);
+
+ printf("\n");
+ proc_close_processes(ps);
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc < 2) {
+ fprintf(stderr, "usage: %1$s --tasks <pid>\n"
+ " %1$s --processes [---name <name>] [--uid <uid>]\n",
+ program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+
+ if (strcmp(argv[1], "--tasks") == 0)
+ return test_tasks(argc - 1, argv + 1);
+ if (strcmp(argv[1], "--processes") == 0)
+ return test_processes(argc - 1, argv + 1);
+
+ return EXIT_FAILURE;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/lib/randutils.c b/libblkid/lib/randutils.c
new file mode 100644
index 000000000..684ac0ac1
--- /dev/null
+++ b/libblkid/lib/randutils.c
@@ -0,0 +1,147 @@
+/*
+ * General purpose random utilities
+ *
+ * Based on libuuid code.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <sys/syscall.h>
+
+#include "c.h"
+#include "randutils.h"
+#include "nls.h"
+
+#ifdef HAVE_TLS
+#define THREAD_LOCAL static __thread
+#else
+#define THREAD_LOCAL static
+#endif
+
+#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
+#define DO_JRAND_MIX
+THREAD_LOCAL unsigned short ul_jrand_seed[3];
+#endif
+
+int random_get_fd(void)
+{
+ int i, fd;
+ struct timeval tv;
+
+ gettimeofday(&tv, 0);
+ fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
+ if (fd == -1)
+ fd = open("/dev/random", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+ if (fd >= 0) {
+ i = fcntl(fd, F_GETFD);
+ if (i >= 0)
+ fcntl(fd, F_SETFD, i | FD_CLOEXEC);
+ }
+ srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
+
+#ifdef DO_JRAND_MIX
+ ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
+ ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
+ ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
+#endif
+ /* Crank the random number generator a few times */
+ gettimeofday(&tv, 0);
+ for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
+ rand();
+ return fd;
+}
+
+
+/*
+ * Generate a stream of random nbytes into buf.
+ * Use /dev/urandom if possible, and if not,
+ * use glibc pseudo-random functions.
+ */
+void random_get_bytes(void *buf, size_t nbytes)
+{
+ size_t i, n = nbytes;
+ int fd = random_get_fd();
+ int lose_counter = 0;
+ unsigned char *cp = (unsigned char *) buf;
+
+ if (fd >= 0) {
+ while (n > 0) {
+ ssize_t x = read(fd, cp, n);
+ if (x <= 0) {
+ if (lose_counter++ > 16)
+ break;
+ continue;
+ }
+ n -= x;
+ cp += x;
+ lose_counter = 0;
+ }
+
+ close(fd);
+ }
+
+ /*
+ * We do this all the time, but this is the only source of
+ * randomness if /dev/random/urandom is out to lunch.
+ */
+ for (cp = buf, i = 0; i < nbytes; i++)
+ *cp++ ^= (rand() >> 7) & 0xFF;
+
+#ifdef DO_JRAND_MIX
+ {
+ unsigned short tmp_seed[3];
+
+ memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed));
+ ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid);
+ for (cp = buf, i = 0; i < nbytes; i++)
+ *cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
+ memcpy(ul_jrand_seed, tmp_seed,
+ sizeof(ul_jrand_seed)-sizeof(unsigned short));
+ }
+#endif
+
+ return;
+}
+
+
+/*
+ * Tell source of randomness.
+ */
+const char *random_tell_source(void)
+{
+ size_t i;
+ static const char *random_sources[] = {
+ "/dev/urandom",
+ "/dev/random"
+ };
+
+ for (i = 0; i < ARRAY_SIZE(random_sources); i++) {
+ if (!access(random_sources[i], R_OK))
+ return random_sources[i];
+ }
+
+ return _("libc pseudo-random functions");
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc __attribute__ ((__unused__)),
+ char *argv[] __attribute__ ((__unused__)))
+{
+ unsigned int v, i;
+
+ /* generate and print 10 random numbers */
+ for (i = 0; i < 10; i++) {
+ random_get_bytes(&v, sizeof(v));
+ printf("%d\n", v);
+ }
+
+ return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/lib/readutmp.c b/libblkid/lib/readutmp.c
new file mode 100644
index 000000000..b11e9a4d2
--- /dev/null
+++ b/libblkid/lib/readutmp.c
@@ -0,0 +1,78 @@
+/* GNU's read utmp module.
+
+ Copyright (C) 1992-2001, 2003-2006, 2009-2014 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Written by jla; revised by djm */
+/* extracted for util-linux by ooprala */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "xalloc.h"
+#include "readutmp.h"
+
+/* Read the utmp entries corresponding to file FILE into freshly-
+ malloc'd storage, set *UTMP_BUF to that pointer, set *N_ENTRIES to
+ the number of entries, and return zero. If there is any error,
+ return -1, setting errno, and don't modify the parameters.
+ If OPTIONS & READ_UTMP_CHECK_PIDS is nonzero, omit entries whose
+ process-IDs do not currently exist. */
+int
+read_utmp (char const *file, size_t *n_entries, struct utmp **utmp_buf)
+{
+ size_t n_read = 0;
+ size_t n_alloc = 0;
+ struct utmp *utmp = NULL;
+ struct utmp *u;
+
+ /* Ignore the return value for now.
+ Solaris' utmpname returns 1 upon success -- which is contrary
+ to what the GNU libc version does. In addition, older GNU libc
+ versions are actually void. */
+ utmpname(file);
+
+ setutent();
+
+ errno = 0;
+ while ((u = getutent()) != NULL) {
+ if (n_read == n_alloc) {
+ n_alloc += 32;
+ utmp = xrealloc(utmp, n_alloc * sizeof (struct utmp));
+ if (!utmp)
+ return -1;
+ }
+ utmp[n_read++] = *u;
+ }
+ if (!u && errno) {
+ free(utmp);
+ return -1;
+ }
+
+ endutent();
+
+ *n_entries = n_read;
+ *utmp_buf = utmp;
+
+ return 0;
+}
diff --git a/libblkid/lib/setproctitle.c b/libblkid/lib/setproctitle.c
new file mode 100644
index 000000000..4bcf8c8a9
--- /dev/null
+++ b/libblkid/lib/setproctitle.c
@@ -0,0 +1,74 @@
+/*
+ * set process title for ps (from sendmail)
+ *
+ * Clobbers argv of our main procedure so ps(1) will display the title.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "setproctitle.h"
+
+#ifndef SPT_BUFSIZE
+# define SPT_BUFSIZE 2048
+#endif
+
+extern char **environ;
+
+static char **argv0;
+static int argv_lth;
+
+void initproctitle (int argc, char **argv)
+{
+ int i;
+ char **envp = environ;
+
+ /*
+ * Move the environment so we can reuse the memory.
+ * (Code borrowed from sendmail.)
+ * WARNING: ugly assumptions on memory layout here;
+ * if this ever causes problems, #undef DO_PS_FIDDLING
+ */
+ for (i = 0; envp[i] != NULL; i++)
+ continue;
+
+ environ = (char **) malloc(sizeof(char *) * (i + 1));
+ if (environ == NULL)
+ return;
+
+ for (i = 0; envp[i] != NULL; i++)
+ if ((environ[i] = strdup(envp[i])) == NULL)
+ return;
+ environ[i] = NULL;
+
+ argv0 = argv;
+ if (i > 0)
+ argv_lth = envp[i-1] + strlen(envp[i-1]) - argv0[0];
+ else
+ argv_lth = argv0[argc-1] + strlen(argv0[argc-1]) - argv0[0];
+}
+
+void setproctitle (const char *prog, const char *txt)
+{
+ int i;
+ char buf[SPT_BUFSIZE];
+
+ if (!argv0)
+ return;
+
+ if (strlen(prog) + strlen(txt) + 5 > SPT_BUFSIZE)
+ return;
+
+ sprintf(buf, "%s -- %s", prog, txt);
+
+ i = strlen(buf);
+ if (i > argv_lth - 2) {
+ i = argv_lth - 2;
+ buf[i] = '\0';
+ }
+ memset(argv0[0], '\0', argv_lth); /* clear the memory area */
+ strcpy(argv0[0], buf);
+
+ argv0[1] = NULL;
+}
diff --git a/libblkid/lib/strutils.c b/libblkid/lib/strutils.c
new file mode 100644
index 000000000..9fe9481bc
--- /dev/null
+++ b/libblkid/lib/strutils.c
@@ -0,0 +1,763 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <assert.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "bitops.h"
+
+static int do_scale_by_power (uintmax_t *x, int base, int power)
+{
+ while (power--) {
+ if (UINTMAX_MAX / base < *x)
+ return -ERANGE;
+ *x *= base;
+ }
+ return 0;
+}
+
+/*
+ * strtosize() - convert string to size (uintmax_t).
+ *
+ * Supported suffixes:
+ *
+ * XiB or X for 2^N
+ * where X = {K,M,G,T,P,E,Z,Y}
+ * or X = {k,m,g,t,p,e} (undocumented for backward compatibility only)
+ * for example:
+ * 10KiB = 10240
+ * 10K = 10240
+ *
+ * XB for 10^N
+ * where X = {K,M,G,T,P,E,Z,Y}
+ * for example:
+ * 10KB = 10000
+ *
+ * The optinal 'power' variable returns number associated with used suffix
+ * {K,M,G,T,P,E,Z,Y} = {1,2,3,4,5,6,7,8}.
+ *
+ * The function also supports decimal point, for example:
+ * 0.5MB = 500000
+ * 0.5MiB = 512000
+ *
+ * Note that the function does not accept numbers with '-' (negative sign)
+ * prefix.
+ */
+int parse_size(const char *str, uintmax_t *res, int *power)
+{
+ char *p;
+ uintmax_t x, frac = 0;
+ int base = 1024, rc = 0, pwr = 0, frac_zeros = 0;
+
+ static const char *suf = "KMGTPEYZ";
+ static const char *suf2 = "kmgtpeyz";
+ const char *sp;
+
+ *res = 0;
+
+ if (!str || !*str) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ /* Only positive numbers are acceptable
+ *
+ * Note that this check is not perfect, it would be better to
+ * use lconv->negative_sign. But coreutils use the same solution,
+ * so it's probably good enough...
+ */
+ p = (char *) str;
+ while (isspace((unsigned char) *p))
+ p++;
+ if (*p == '-') {
+ rc = -EINVAL;
+ goto err;
+ }
+ p = NULL;
+
+ errno = 0;
+ x = strtoumax(str, &p, 0);
+
+ if (p == str ||
+ (errno != 0 && (x == UINTMAX_MAX || x == 0))) {
+ rc = errno ? -errno : -1;
+ goto err;
+ }
+ if (!p || !*p)
+ goto done; /* without suffix */
+
+ /*
+ * Check size suffixes
+ */
+check_suffix:
+ if (*(p + 1) == 'i' && *(p + 2) == 'B' && !*(p + 3))
+ base = 1024; /* XiB, 2^N */
+ else if (*(p + 1) == 'B' && !*(p + 2))
+ base = 1000; /* XB, 10^N */
+ else if (*(p + 1)) {
+ struct lconv const *l = localeconv();
+ char *dp = l ? l->decimal_point : NULL;
+ size_t dpsz = dp ? strlen(dp) : 0;
+
+ if (frac == 0 && *p && dp && strncmp(dp, p, dpsz) == 0) {
+ char *fstr = p + dpsz;
+
+ for (p = fstr; *p && *p == '0'; p++)
+ frac_zeros++;
+ errno = 0, p = NULL;
+ frac = strtoumax(fstr, &p, 0);
+ if (p == fstr ||
+ (errno != 0 && (frac == UINTMAX_MAX || frac == 0))) {
+ rc = errno ? -errno : -1;
+ goto err;
+ }
+ if (frac && (!p || !*p)) {
+ rc = -EINVAL;
+ goto err; /* without suffix, but with frac */
+ }
+ goto check_suffix;
+ }
+ rc = -EINVAL;
+ goto err; /* unexpected suffix */
+ }
+
+ sp = strchr(suf, *p);
+ if (sp)
+ pwr = (sp - suf) + 1;
+ else {
+ sp = strchr(suf2, *p);
+ if (sp)
+ pwr = (sp - suf2) + 1;
+ else {
+ rc = -EINVAL;
+ goto err;
+ }
+ }
+
+ rc = do_scale_by_power(&x, base, pwr);
+ if (power)
+ *power = pwr;
+ if (frac && pwr) {
+ int zeros_in_pwr = frac_zeros % 3;
+ int frac_pwr = pwr - (frac_zeros / 3) - 1;
+ uintmax_t y = frac * (zeros_in_pwr == 0 ? 100 :
+ zeros_in_pwr == 1 ? 10 : 1);
+
+ if (frac_pwr < 0) {
+ rc = -EINVAL;
+ goto err;
+ }
+ do_scale_by_power(&y, base, frac_pwr);
+ x += y;
+ }
+done:
+ *res = x;
+err:
+ return rc;
+}
+
+int strtosize(const char *str, uintmax_t *res)
+{
+ return parse_size(str, res, NULL);
+}
+
+int isdigit_string(const char *str)
+{
+ const char *p;
+
+ for (p = str; p && *p && isdigit((unsigned char) *p); p++);
+
+ return p && p > str && !*p;
+}
+
+
+#ifndef HAVE_MEMPCPY
+void *mempcpy(void *restrict dest, const void *restrict src, size_t n)
+{
+ return ((char *)memcpy(dest, src, n)) + n;
+}
+#endif
+
+#ifndef HAVE_STRNLEN
+size_t strnlen(const char *s, size_t maxlen)
+{
+ int i;
+
+ for (i = 0; i < maxlen; i++) {
+ if (s[i] == '\0')
+ return i + 1;
+ }
+ return maxlen;
+}
+#endif
+
+#ifndef HAVE_STRNCHR
+char *strnchr(const char *s, size_t maxlen, int c)
+{
+ for (; maxlen-- && *s != '\0'; ++s)
+ if (*s == (char)c)
+ return (char *)s;
+ return NULL;
+}
+#endif
+
+#ifndef HAVE_STRNDUP
+char *strndup(const char *s, size_t n)
+{
+ size_t len = strnlen(s, n);
+ char *new = (char *) malloc((len + 1) * sizeof(char));
+ if (!new)
+ return NULL;
+ new[len] = '\0';
+ return (char *) memcpy(new, s, len);
+}
+#endif
+
+int16_t strtos16_or_err(const char *str, const char *errmesg)
+{
+ int32_t num = strtos32_or_err(str, errmesg);
+
+ if (num < INT16_MIN || num > INT16_MAX)
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ return num;
+}
+
+uint16_t strtou16_or_err(const char *str, const char *errmesg)
+{
+ uint32_t num = strtou32_or_err(str, errmesg);
+
+ if (num > UINT16_MAX)
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ return num;
+}
+
+int32_t strtos32_or_err(const char *str, const char *errmesg)
+{
+ int64_t num = strtos64_or_err(str, errmesg);
+
+ if (num < INT32_MIN || num > INT32_MAX)
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ return num;
+}
+
+uint32_t strtou32_or_err(const char *str, const char *errmesg)
+{
+ uint64_t num = strtou64_or_err(str, errmesg);
+
+ if (num > UINT32_MAX)
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ return num;
+}
+
+int64_t strtos64_or_err(const char *str, const char *errmesg)
+{
+ int64_t num;
+ char *end = NULL;
+
+ if (str == NULL || *str == '\0')
+ goto err;
+ errno = 0;
+ num = strtoimax(str, &end, 10);
+
+ if (errno || str == end || (end && *end))
+ goto err;
+
+ return num;
+err:
+ if (errno)
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+uint64_t strtou64_or_err(const char *str, const char *errmesg)
+{
+ uintmax_t num;
+ char *end = NULL;
+
+ if (str == NULL || *str == '\0')
+ goto err;
+ errno = 0;
+ num = strtoumax(str, &end, 10);
+
+ if (errno || str == end || (end && *end))
+ goto err;
+
+ return num;
+err:
+ if (errno)
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+
+double strtod_or_err(const char *str, const char *errmesg)
+{
+ double num;
+ char *end = NULL;
+
+ if (str == NULL || *str == '\0')
+ goto err;
+ errno = 0;
+ num = strtod(str, &end);
+
+ if (errno || str == end || (end && *end))
+ goto err;
+
+ return num;
+err:
+ if (errno)
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+long strtol_or_err(const char *str, const char *errmesg)
+{
+ long num;
+ char *end = NULL;
+
+ if (str == NULL || *str == '\0')
+ goto err;
+ errno = 0;
+ num = strtol(str, &end, 10);
+
+ if (errno || str == end || (end && *end))
+ goto err;
+
+ return num;
+err:
+ if (errno)
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+unsigned long strtoul_or_err(const char *str, const char *errmesg)
+{
+ unsigned long num;
+ char *end = NULL;
+
+ if (str == NULL || *str == '\0')
+ goto err;
+ errno = 0;
+ num = strtoul(str, &end, 10);
+
+ if (errno || str == end || (end && *end))
+ goto err;
+
+ return num;
+err:
+ if (errno)
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+uintmax_t strtosize_or_err(const char *str, const char *errmesg)
+{
+ uintmax_t num;
+
+ if (strtosize(str, &num) == 0)
+ return num;
+
+ if (errno)
+ err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+ errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+
+void strtotimeval_or_err(const char *str, struct timeval *tv, const char *errmesg)
+{
+ double user_input;
+
+ user_input = strtod_or_err(str, errmesg);
+ tv->tv_sec = (time_t) user_input;
+ tv->tv_usec = (long)((user_input - tv->tv_sec) * 1000000);
+}
+
+/*
+ * Converts stat->st_mode to ls(1)-like mode string. The size of "str" must
+ * be 11 bytes.
+ */
+void strmode(mode_t mode, char *str)
+{
+ if (S_ISDIR(mode))
+ str[0] = 'd';
+ else if (S_ISLNK(mode))
+ str[0] = 'l';
+ else if (S_ISCHR(mode))
+ str[0] = 'c';
+ else if (S_ISBLK(mode))
+ str[0] = 'b';
+ else if (S_ISSOCK(mode))
+ str[0] = 's';
+ else if (S_ISFIFO(mode))
+ str[0] = 'p';
+ else if (S_ISREG(mode))
+ str[0] = '-';
+
+ str[1] = mode & S_IRUSR ? 'r' : '-';
+ str[2] = mode & S_IWUSR ? 'w' : '-';
+ str[3] = (mode & S_ISUID
+ ? (mode & S_IXUSR ? 's' : 'S')
+ : (mode & S_IXUSR ? 'x' : '-'));
+ str[4] = mode & S_IRGRP ? 'r' : '-';
+ str[5] = mode & S_IWGRP ? 'w' : '-';
+ str[6] = (mode & S_ISGID
+ ? (mode & S_IXGRP ? 's' : 'S')
+ : (mode & S_IXGRP ? 'x' : '-'));
+ str[7] = mode & S_IROTH ? 'r' : '-';
+ str[8] = mode & S_IWOTH ? 'w' : '-';
+ str[9] = (mode & S_ISVTX
+ ? (mode & S_IXOTH ? 't' : 'T')
+ : (mode & S_IXOTH ? 'x' : '-'));
+ str[10] = '\0';
+}
+
+/*
+ * returns exponent (2^x=n) in range KiB..PiB
+ */
+static int get_exp(uint64_t n)
+{
+ int shft;
+
+ for (shft = 10; shft <= 60; shft += 10) {
+ if (n < (1ULL << shft))
+ break;
+ }
+ return shft - 10;
+}
+
+char *size_to_human_string(int options, uint64_t bytes)
+{
+ char buf[32];
+ int dec, exp;
+ uint64_t frac;
+ const char *letters = "BKMGTPE";
+ char suffix[sizeof(" KiB")], *psuf = suffix;
+ char c;
+
+ if (options & SIZE_SUFFIX_SPACE)
+ *psuf++ = ' ';
+
+ exp = get_exp(bytes);
+ c = *(letters + (exp ? exp / 10 : 0));
+ dec = exp ? bytes / (1ULL << exp) : bytes;
+ frac = exp ? bytes % (1ULL << exp) : 0;
+
+ *psuf++ = c;
+
+ if ((options & SIZE_SUFFIX_3LETTER) && (c != 'B')) {
+ *psuf++ = 'i';
+ *psuf++ = 'B';
+ }
+
+ *psuf = '\0';
+
+ /* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n",
+ * exp, suffix[0], dec, frac);
+ */
+
+ if (frac) {
+ /* round */
+ frac = (frac / (1ULL << (exp - 10)) + 50) / 100;
+ if (frac == 10)
+ dec++, frac = 0;
+ }
+
+ if (frac) {
+ struct lconv const *l = localeconv();
+ char *dp = l ? l->decimal_point : NULL;
+
+ if (!dp || !*dp)
+ dp = ".";
+ snprintf(buf, sizeof(buf), "%d%s%jd%s", dec, dp, frac, suffix);
+ } else
+ snprintf(buf, sizeof(buf), "%d%s", dec, suffix);
+
+ return strdup(buf);
+}
+
+/*
+ * Parses comma delimited list to array with IDs, for example:
+ *
+ * "aaa,bbb,ccc" --> ary[0] = FOO_AAA;
+ * ary[1] = FOO_BBB;
+ * ary[3] = FOO_CCC;
+ *
+ * The function name2id() provides conversion from string to ID.
+ *
+ * Returns: >= 0 : number of items added to ary[]
+ * -1 : parse error or unknown item
+ * -2 : arysz reached
+ */
+int string_to_idarray(const char *list, int ary[], size_t arysz,
+ int (name2id)(const char *, size_t))
+{
+ const char *begin = NULL, *p;
+ size_t n = 0;
+
+ if (!list || !*list || !ary || !arysz || !name2id)
+ return -1;
+
+ for (p = list; p && *p; p++) {
+ const char *end = NULL;
+ int id;
+
+ if (n >= arysz)
+ return -2;
+ if (!begin)
+ begin = p; /* begin of the column name */
+ if (*p == ',')
+ end = p; /* terminate the name */
+ if (*(p + 1) == '\0')
+ end = p + 1; /* end of string */
+ if (!begin || !end)
+ continue;
+ if (end <= begin)
+ return -1;
+
+ id = name2id(begin, end - begin);
+ if (id == -1)
+ return -1;
+ ary[ n++ ] = id;
+ begin = NULL;
+ if (end && !*end)
+ break;
+ }
+ return n;
+}
+
+/*
+ * Parses the array like string_to_idarray but if format is "+aaa,bbb"
+ * it adds fields to array instead of replacing them.
+ */
+int string_add_to_idarray(const char *list, int ary[], size_t arysz,
+ int *ary_pos, int (name2id)(const char *, size_t))
+{
+ const char *list_add;
+ int r;
+
+ if (!list || !*list || !ary_pos ||
+ *ary_pos < 0 || (size_t) *ary_pos > arysz)
+ return -1;
+
+ if (list[0] == '+')
+ list_add = &list[1];
+ else {
+ list_add = list;
+ *ary_pos = 0;
+ }
+
+ r = string_to_idarray(list_add, &ary[*ary_pos], arysz - *ary_pos, name2id);
+ if (r > 0)
+ *ary_pos += r;
+ return r;
+}
+
+/*
+ * LIST ::= <item> [, <item>]
+ *
+ * The <item> is translated to 'id' by name2id() function and the 'id' is used
+ * as a position in the 'ary' bit array. It means that the 'id' has to be in
+ * range <0..N> where N < sizeof(ary) * NBBY.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int string_to_bitarray(const char *list,
+ char *ary,
+ int (*name2bit)(const char *, size_t))
+{
+ const char *begin = NULL, *p;
+
+ if (!list || !name2bit || !ary)
+ return -EINVAL;
+
+ for (p = list; p && *p; p++) {
+ const char *end = NULL;
+ int bit;
+
+ if (!begin)
+ begin = p; /* begin of the level name */
+ if (*p == ',')
+ end = p; /* terminate the name */
+ if (*(p + 1) == '\0')
+ end = p + 1; /* end of string */
+ if (!begin || !end)
+ continue;
+ if (end <= begin)
+ return -1;
+
+ bit = name2bit(begin, end - begin);
+ if (bit < 0)
+ return bit;
+ setbit(ary, bit);
+ begin = NULL;
+ if (end && !*end)
+ break;
+ }
+ return 0;
+}
+
+/*
+ * LIST ::= <item> [, <item>]
+ *
+ * The <item> is translated to 'id' by name2flag() function and the flags is
+ * set to the 'mask'
+*
+ * Returns: 0 on success, <0 on error.
+ */
+int string_to_bitmask(const char *list,
+ unsigned long *mask,
+ long (*name2flag)(const char *, size_t))
+{
+ const char *begin = NULL, *p;
+
+ if (!list || !name2flag || !mask)
+ return -EINVAL;
+
+ for (p = list; p && *p; p++) {
+ const char *end = NULL;
+ long flag;
+
+ if (!begin)
+ begin = p; /* begin of the level name */
+ if (*p == ',')
+ end = p; /* terminate the name */
+ if (*(p + 1) == '\0')
+ end = p + 1; /* end of string */
+ if (!begin || !end)
+ continue;
+ if (end <= begin)
+ return -1;
+
+ flag = name2flag(begin, end - begin);
+ if (flag < 0)
+ return flag; /* error */
+ *mask |= flag;
+ begin = NULL;
+ if (end && !*end)
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Parse the lower and higher values in a string containing
+ * "lower:higher" or "lower-higher" format. Note that either
+ * the lower or the higher values may be missing, and the def
+ * value will be assigned to it by default.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int parse_range(const char *str, int *lower, int *upper, int def)
+{
+ char *end = NULL;
+
+ if (!str)
+ return 0;
+
+ *upper = *lower = def;
+ errno = 0;
+
+ if (*str == ':') { /* <:N> */
+ str++;
+ *upper = strtol(str, &end, 10);
+ if (errno || !end || *end || end == str)
+ return -1;
+ } else {
+ *upper = *lower = strtol(str, &end, 10);
+ if (errno || !end || end == str)
+ return -1;
+
+ if (*end == ':' && !*(end + 1)) /* <M:> */
+ *upper = 0;
+ else if (*end == '-' || *end == ':') { /* <M:N> <M-N> */
+ str = end + 1;
+ end = NULL;
+ errno = 0;
+ *upper = strtol(str, &end, 10);
+
+ if (errno || !end || *end || end == str)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Compare two strings for equality, ignoring at most one trailing
+ * slash.
+ */
+int streq_except_trailing_slash(const char *s1, const char *s2)
+{
+ int equal;
+
+ if (!s1 && !s2)
+ return 1;
+ if (!s1 || !s2)
+ return 0;
+
+ equal = !strcmp(s1, s2);
+
+ if (!equal) {
+ size_t len1 = strlen(s1);
+ size_t len2 = strlen(s2);
+
+ if (len1 && *(s1 + len1 - 1) == '/')
+ len1--;
+ if (len2 && *(s2 + len2 - 1) == '/')
+ len2--;
+ if (len1 != len2)
+ return 0;
+
+ equal = !strncmp(s1, s2, len1);
+ }
+
+ return equal;
+}
+
+
+#ifdef TEST_PROGRAM
+
+int main(int argc, char *argv[])
+{
+ uintmax_t size = 0;
+ char *hum, *hum2;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <number>[suffix]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (strtosize(argv[1], &size))
+ errx(EXIT_FAILURE, "invalid size '%s' value", argv[1]);
+
+ hum = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
+ hum2 = size_to_human_string(SIZE_SUFFIX_3LETTER |
+ SIZE_SUFFIX_SPACE, size);
+
+ printf("%25s : %20ju : %8s : %12s\n", argv[1], size, hum, hum2);
+ free(hum);
+ free(hum2);
+
+ return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/lib/swapprober.c b/libblkid/lib/swapprober.c
new file mode 100644
index 000000000..5a4b112e1
--- /dev/null
+++ b/libblkid/lib/swapprober.c
@@ -0,0 +1,49 @@
+
+#include "c.h"
+#include "nls.h"
+
+#include "swapheader.h"
+#include "swapprober.h"
+
+blkid_probe get_swap_prober(const char *devname)
+{
+ blkid_probe pr;
+ int rc;
+ const char *version = NULL;
+ char *swap_filter[] = { "swap", NULL };
+
+ pr = blkid_new_probe_from_filename(devname);
+ if (!pr) {
+ warn(_("%s: unable to probe device"), devname);
+ return NULL;
+ }
+
+ blkid_probe_enable_superblocks(pr, TRUE);
+ blkid_probe_set_superblocks_flags(pr,
+ BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
+ BLKID_SUBLKS_VERSION);
+
+ blkid_probe_filter_superblocks_type(pr, BLKID_FLTR_ONLYIN, swap_filter);
+
+ rc = blkid_do_safeprobe(pr);
+ if (rc == -1)
+ warn(_("%s: unable to probe device"), devname);
+ else if (rc == -2)
+ warnx(_("%s: ambiguous probing result; use wipefs(8)"), devname);
+ else if (rc == 1)
+ warnx(_("%s: not a valid swap partition"), devname);
+
+ if (rc == 0) {
+ /* Only the SWAPSPACE2 is supported. */
+ if (blkid_probe_lookup_value(pr, "VERSION", &version, NULL) == 0
+ && version
+ && strcmp(version, stringify_value(SWAP_VERSION)))
+ warnx(_("%s: unsupported swap version '%s'"),
+ devname, version);
+ else
+ return pr;
+ }
+
+ blkid_free_probe(pr);
+ return NULL;
+}
diff --git a/libblkid/lib/sysfs.c b/libblkid/lib/sysfs.c
new file mode 100644
index 000000000..71cf1b410
--- /dev/null
+++ b/libblkid/lib/sysfs.c
@@ -0,0 +1,1075 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <ctype.h>
+#include <string.h>
+#include <libgen.h>
+#include <sys/sysmacros.h>
+
+#include "c.h"
+#include "at.h"
+#include "pathnames.h"
+#include "sysfs.h"
+#include "fileutils.h"
+#include "all-io.h"
+
+#define STRINGIFY(x) #x
+#define EXPAND(x) STRINGIFY(x)
+
+char *sysfs_devno_attribute_path(dev_t devno, char *buf,
+ size_t bufsiz, const char *attr)
+{
+ int len;
+
+ if (attr)
+ len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d/%s",
+ major(devno), minor(devno), attr);
+ else
+ len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d",
+ major(devno), minor(devno));
+
+ return (len < 0 || (size_t) len + 1 > bufsiz) ? NULL : buf;
+}
+
+int sysfs_devno_has_attribute(dev_t devno, const char *attr)
+{
+ char path[PATH_MAX];
+ struct stat info;
+
+ if (!sysfs_devno_attribute_path(devno, path, sizeof(path), attr))
+ return 0;
+ if (stat(path, &info) == 0)
+ return 1;
+ return 0;
+}
+
+char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz)
+{
+ return sysfs_devno_attribute_path(devno, buf, bufsiz, NULL);
+}
+
+dev_t sysfs_devname_to_devno(const char *name, const char *parent)
+{
+ char buf[PATH_MAX], *path = NULL;
+ dev_t dev = 0;
+
+ if (strncmp("/dev/", name, 5) == 0) {
+ /*
+ * Read from /dev
+ */
+ struct stat st;
+
+ if (stat(name, &st) == 0)
+ dev = st.st_rdev;
+ else
+ name += 5; /* unaccesible, or not node in /dev */
+ }
+
+ if (!dev && parent && strncmp("dm-", name, 3)) {
+ /*
+ * Create path to /sys/block/<parent>/<name>/dev
+ */
+ int len = snprintf(buf, sizeof(buf),
+ _PATH_SYS_BLOCK "/%s/%s/dev", parent, name);
+ if (len < 0 || (size_t) len + 1 > sizeof(buf))
+ return 0;
+ path = buf;
+
+ } else if (!dev) {
+ /*
+ * Create path to /sys/block/<name>/dev
+ */
+ int len = snprintf(buf, sizeof(buf),
+ _PATH_SYS_BLOCK "/%s/dev", name);
+ if (len < 0 || (size_t) len + 1 > sizeof(buf))
+ return 0;
+ path = buf;
+ }
+
+ if (path) {
+ /*
+ * read devno from sysfs
+ */
+ FILE *f;
+ int maj = 0, min = 0;
+
+ f = fopen(path, "r" UL_CLOEXECSTR);
+ if (!f)
+ return 0;
+
+ if (fscanf(f, "%d:%d", &maj, &min) == 2)
+ dev = makedev(maj, min);
+ fclose(f);
+ }
+ return dev;
+}
+
+/*
+ * Returns devname (e.g. "/dev/sda1") for the given devno.
+ *
+ * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
+ * symlinks.
+ *
+ * Please, use more robust blkid_devno_to_devname() in your applications.
+ */
+char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz)
+{
+ struct sysfs_cxt cxt;
+ char *name;
+ size_t sz;
+ struct stat st;
+
+ if (sysfs_init(&cxt, devno, NULL))
+ return NULL;
+
+ name = sysfs_get_devname(&cxt, buf, bufsiz);
+ sysfs_deinit(&cxt);
+
+ if (!name)
+ return NULL;
+
+ sz = strlen(name);
+
+ if (sz + sizeof("/dev/") > bufsiz)
+ return NULL;
+
+ /* create the final "/dev/<name>" string */
+ memmove(buf + 5, name, sz + 1);
+ memcpy(buf, "/dev/", 5);
+
+ if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == devno)
+ return buf;
+
+ return NULL;
+}
+
+int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent)
+{
+ char path[PATH_MAX];
+ int fd, rc;
+
+ memset(cxt, 0, sizeof(*cxt));
+ cxt->dir_fd = -1;
+
+ if (!sysfs_devno_path(devno, path, sizeof(path)))
+ goto err;
+
+ fd = open(path, O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ goto err;
+ cxt->dir_fd = fd;
+
+ cxt->dir_path = strdup(path);
+ if (!cxt->dir_path)
+ goto err;
+ cxt->devno = devno;
+ cxt->parent = parent;
+ return 0;
+err:
+ rc = errno > 0 ? -errno : -1;
+ sysfs_deinit(cxt);
+ return rc;
+}
+
+void sysfs_deinit(struct sysfs_cxt *cxt)
+{
+ if (!cxt)
+ return;
+
+ if (cxt->dir_fd >= 0)
+ close(cxt->dir_fd);
+ free(cxt->dir_path);
+
+ memset(cxt, 0, sizeof(*cxt));
+
+ cxt->dir_fd = -1;
+}
+
+int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st)
+{
+ int rc = fstat_at(cxt->dir_fd, cxt->dir_path, attr, st, 0);
+
+ if (rc != 0 && errno == ENOENT &&
+ strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
+
+ /* Exception for "queue/<attr>". These attributes are available
+ * for parental devices only
+ */
+ return fstat_at(cxt->parent->dir_fd,
+ cxt->parent->dir_path, attr, st, 0);
+ }
+ return rc;
+}
+
+int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr)
+{
+ struct stat st;
+
+ return sysfs_stat(cxt, attr, &st) == 0;
+}
+
+static int sysfs_open(struct sysfs_cxt *cxt, const char *attr, int flags)
+{
+ int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, flags);
+
+ if (fd == -1 && errno == ENOENT &&
+ strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
+
+ /* Exception for "queue/<attr>". These attributes are available
+ * for parental devices only
+ */
+ fd = open_at(cxt->parent->dir_fd, cxt->dir_path, attr, flags);
+ }
+ return fd;
+}
+
+ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr,
+ char *buf, size_t bufsiz)
+{
+ if (!cxt->dir_path)
+ return -1;
+
+ if (attr)
+ return readlink_at(cxt->dir_fd, cxt->dir_path, attr, buf, bufsiz);
+
+ /* read /sys/dev/block/<maj:min> link */
+ return readlink(cxt->dir_path, buf, bufsiz);
+}
+
+DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr)
+{
+ DIR *dir;
+ int fd = -1;
+
+ if (attr)
+ fd = sysfs_open(cxt, attr, O_RDONLY|O_CLOEXEC);
+
+ else if (cxt->dir_fd >= 0)
+ /* request to open root of device in sysfs (/sys/block/<dev>)
+ * -- we cannot use cxt->sysfs_fd directly, because closedir()
+ * will close this our persistent file descriptor.
+ */
+ fd = dup(cxt->dir_fd);
+
+ if (fd < 0)
+ return NULL;
+
+ dir = fdopendir(fd);
+ if (!dir) {
+ close(fd);
+ return NULL;
+ }
+ if (!attr)
+ rewinddir(dir);
+ return dir;
+}
+
+
+static FILE *sysfs_fopen(struct sysfs_cxt *cxt, const char *attr)
+{
+ int fd = sysfs_open(cxt, attr, O_RDONLY|O_CLOEXEC);
+
+ return fd < 0 ? NULL : fdopen(fd, "r" UL_CLOEXECSTR);
+}
+
+
+static struct dirent *xreaddir(DIR *dp)
+{
+ struct dirent *d;
+
+ while ((d = readdir(dp))) {
+ if (!strcmp(d->d_name, ".") ||
+ !strcmp(d->d_name, ".."))
+ continue;
+
+ /* blacklist here? */
+ break;
+ }
+ return d;
+}
+
+int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name)
+{
+ char path[256];
+
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (d->d_type != DT_DIR &&
+ d->d_type != DT_LNK &&
+ d->d_type != DT_UNKNOWN)
+ return 0;
+#endif
+ if (parent_name) {
+ const char *p = parent_name;
+ size_t len;
+
+ /* /dev/sda --> "sda" */
+ if (*parent_name == '/') {
+ p = strrchr(parent_name, '/');
+ if (!p)
+ return 0;
+ p++;
+ }
+
+ len = strlen(p);
+ if (strlen(d->d_name) <= len)
+ return 0;
+
+ /* partitions subdir name is
+ * "<parent>[:digit:]" or "<parent>p[:digit:]"
+ */
+ return strncmp(p, d->d_name, len) == 0 &&
+ ((*(d->d_name + len) == 'p' && isdigit(*(d->d_name + len + 1)))
+ || isdigit(*(d->d_name + len)));
+ }
+
+ /* Cannot use /partition file, not supported on old sysfs */
+ snprintf(path, sizeof(path), "%s/start", d->d_name);
+
+ return faccessat(dirfd(dir), path, R_OK, 0) == 0;
+}
+
+/*
+ * Converts @partno (partition number) to devno of the partition.
+ * The @cxt handles wholedisk device.
+ *
+ * Note that this code does not expect any special format of the
+ * partitions devnames.
+ */
+dev_t sysfs_partno_to_devno(struct sysfs_cxt *cxt, int partno)
+{
+ DIR *dir;
+ struct dirent *d;
+ char path[256];
+ dev_t devno = 0;
+
+ dir = sysfs_opendir(cxt, NULL);
+ if (!dir)
+ return 0;
+
+ while ((d = xreaddir(dir))) {
+ int n, maj, min;
+
+ if (!sysfs_is_partition_dirent(dir, d, NULL))
+ continue;
+
+ snprintf(path, sizeof(path), "%s/partition", d->d_name);
+ if (sysfs_read_int(cxt, path, &n))
+ continue;
+
+ if (n == partno) {
+ snprintf(path, sizeof(path), "%s/dev", d->d_name);
+ if (sysfs_scanf(cxt, path, "%d:%d", &maj, &min) == 2)
+ devno = makedev(maj, min);
+ break;
+ }
+ }
+
+ closedir(dir);
+ return devno;
+}
+
+
+int sysfs_scanf(struct sysfs_cxt *cxt, const char *attr, const char *fmt, ...)
+{
+ FILE *f = sysfs_fopen(cxt, attr);
+ va_list ap;
+ int rc;
+
+ if (!f)
+ return -EINVAL;
+ va_start(ap, fmt);
+ rc = vfscanf(f, fmt, ap);
+ va_end(ap);
+
+ fclose(f);
+ return rc;
+}
+
+
+int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res)
+{
+ int64_t x = 0;
+
+ if (sysfs_scanf(cxt, attr, "%"SCNd64, &x) == 1) {
+ if (res)
+ *res = x;
+ return 0;
+ }
+ return -1;
+}
+
+int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res)
+{
+ uint64_t x = 0;
+
+ if (sysfs_scanf(cxt, attr, "%"SCNu64, &x) == 1) {
+ if (res)
+ *res = x;
+ return 0;
+ }
+ return -1;
+}
+
+int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res)
+{
+ int x = 0;
+
+ if (sysfs_scanf(cxt, attr, "%d", &x) == 1) {
+ if (res)
+ *res = x;
+ return 0;
+ }
+ return -1;
+}
+
+int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str)
+{
+ int fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC);
+ int rc, errsv;
+
+ if (fd < 0)
+ return -errno;
+ rc = write_all(fd, str, strlen(str));
+
+ errsv = errno;
+ close(fd);
+ errno = errsv;
+ return rc;
+}
+
+int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num)
+{
+ char buf[sizeof(STRINGIFY(ULLONG_MAX))];
+ int fd, rc = 0, len, errsv;
+
+ fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ len = snprintf(buf, sizeof(buf), "%ju", num);
+ if (len < 0 || (size_t) len + 1 > sizeof(buf))
+ rc = -errno;
+ else
+ rc = write_all(fd, buf, len);
+
+ errsv = errno;
+ close(fd);
+ errno = errsv;
+ return rc;
+}
+
+char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr)
+{
+ char buf[1024];
+ return sysfs_scanf(cxt, attr, "%1023[^\n]", buf) == 1 ?
+ strdup(buf) : NULL;
+}
+
+int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr)
+{
+ DIR *dir;
+ int r = 0;
+
+ if (!(dir = sysfs_opendir(cxt, attr)))
+ return 0;
+
+ while (xreaddir(dir)) r++;
+
+ closedir(dir);
+ return r;
+}
+
+int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname)
+{
+ DIR *dir;
+ struct dirent *d;
+ int r = 0;
+
+ if (!(dir = sysfs_opendir(cxt, NULL)))
+ return 0;
+
+ while ((d = xreaddir(dir))) {
+ if (sysfs_is_partition_dirent(dir, d, devname))
+ r++;
+ }
+
+ closedir(dir);
+ return r;
+}
+
+/*
+ * Returns slave name if there is only one slave, otherwise returns NULL.
+ * The result should be deallocated by free().
+ */
+char *sysfs_get_slave(struct sysfs_cxt *cxt)
+{
+ DIR *dir;
+ struct dirent *d;
+ char *name = NULL;
+
+ if (!(dir = sysfs_opendir(cxt, "slaves")))
+ return NULL;
+
+ while ((d = xreaddir(dir))) {
+ if (name)
+ goto err; /* more slaves */
+
+ name = strdup(d->d_name);
+ }
+
+ closedir(dir);
+ return name;
+err:
+ free(name);
+ closedir(dir);
+ return NULL;
+}
+
+/*
+ * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
+ * symlinks.
+ */
+char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz)
+{
+ char *name = NULL;
+ ssize_t sz;
+
+ sz = sysfs_readlink(cxt, NULL, buf, bufsiz - 1);
+ if (sz < 0)
+ return NULL;
+
+ buf[sz] = '\0';
+ name = strrchr(buf, '/');
+ if (!name)
+ return NULL;
+
+ name++;
+ sz = strlen(name);
+
+ memmove(buf, name, sz + 1);
+ return buf;
+}
+
+#define SUBSYSTEM_LINKNAME "/subsystem"
+
+/*
+ * For example:
+ *
+ * chain: /sys/dev/block/../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/ \
+ * 1-1.2:1.0/host65/target65:0:0/65:0:0:0/block/sdb
+ *
+ * The function check if <chain>/subsystem symlink exists, if yes then returns
+ * basename of the readlink result, and remove the last subdirectory from the
+ * <chain> path.
+ */
+static char *get_subsystem(char *chain, char *buf, size_t bufsz)
+{
+ size_t len;
+ char *p;
+
+ if (!chain || !*chain)
+ return NULL;
+
+ len = strlen(chain);
+ if (len + sizeof(SUBSYSTEM_LINKNAME) > PATH_MAX)
+ return NULL;
+
+ do {
+ ssize_t sz;
+
+ /* append "/subsystem" to the path */
+ memcpy(chain + len, SUBSYSTEM_LINKNAME, sizeof(SUBSYSTEM_LINKNAME));
+
+ /* try if subsystem symlink exists */
+ sz = readlink(chain, buf, bufsz - 1);
+
+ /* remove last subsystem from chain */
+ chain[len] = '\0';
+ p = strrchr(chain, '/');
+ if (p) {
+ *p = '\0';
+ len = p - chain;
+ }
+
+ if (sz > 0) {
+ /* we found symlink to subsystem, return basename */
+ buf[sz] = '\0';
+ return basename(buf);
+ }
+
+ } while (p);
+
+ return NULL;
+}
+
+/*
+ * Returns complete path to the device, the patch contains all all sybsystems
+ * used for the device.
+ */
+char *sysfs_get_devchain(struct sysfs_cxt *cxt, char *buf, size_t bufsz)
+{
+ /* read /sys/dev/block/<maj>:<min> symlink */
+ size_t sz = sysfs_readlink(cxt, NULL, buf, bufsz);
+ if (sz <= 0 || sz + sizeof(_PATH_SYS_DEVBLOCK "/") > bufsz)
+ return NULL;
+
+ buf[sz++] = '\0';
+
+ /* create absolute patch from the link */
+ memmove(buf + sizeof(_PATH_SYS_DEVBLOCK "/") - 1, buf, sz);
+ memcpy(buf, _PATH_SYS_DEVBLOCK "/", sizeof(_PATH_SYS_DEVBLOCK "/") - 1);
+
+ return buf;
+}
+
+/*
+ * The @subsys returns the next subsystem in the chain. Function modifies
+ * @devchain string.
+ *
+ * Returns: 0 in success, <0 on error, 1 on end of chain
+ */
+int sysfs_next_subsystem(struct sysfs_cxt *cxt __attribute__((unused)),
+ char *devchain, char **subsys)
+{
+ char subbuf[PATH_MAX];
+ char *sub;
+
+ if (!subsys || !devchain)
+ return -EINVAL;
+
+ while ((sub = get_subsystem(devchain, subbuf, sizeof(subbuf)))) {
+ *subsys = strdup(sub);
+ if (!*subsys)
+ return -ENOMEM;
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int is_hotpluggable_subsystem(const char *name)
+{
+ static const char * const hotplug_subsystems[] = {
+ "usb",
+ "ieee1394",
+ "pcmcia",
+ "mmc",
+ "ccw"
+ };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(hotplug_subsystems); i++)
+ if (strcmp(name, hotplug_subsystems[i]) == 0)
+ return 1;
+
+ return 0;
+}
+
+int sysfs_is_hotpluggable(struct sysfs_cxt *cxt)
+{
+ char buf[PATH_MAX], *chain, *sub;
+ int rc = 0;
+
+
+ /* check /sys/dev/block/<maj>:<min>/removable attribute */
+ if (sysfs_read_int(cxt, "removable", &rc) == 0 && rc == 1)
+ return 1;
+
+ chain = sysfs_get_devchain(cxt, buf, sizeof(buf));
+
+ while (chain && sysfs_next_subsystem(cxt, chain, &sub) == 0) {
+ rc = is_hotpluggable_subsystem(sub);
+ if (rc) {
+ free(sub);
+ break;
+ }
+ free(sub);
+ }
+
+ return rc;
+}
+
+static int get_dm_wholedisk(struct sysfs_cxt *cxt, char *diskname,
+ size_t len, dev_t *diskdevno)
+{
+ int rc = 0;
+ char *name;
+
+ /* Note, sysfs_get_slave() returns the first slave only,
+ * if there is more slaves, then return NULL
+ */
+ name = sysfs_get_slave(cxt);
+ if (!name)
+ return -1;
+
+ if (diskname && len) {
+ strncpy(diskname, name, len);
+ diskname[len - 1] = '\0';
+ }
+
+ if (diskdevno) {
+ *diskdevno = sysfs_devname_to_devno(name, NULL);
+ if (!*diskdevno)
+ rc = -1;
+ }
+
+ free(name);
+ return rc;
+}
+
+/*
+ * Returns by @diskdevno whole disk device devno and (optionaly) by
+ * @diskname the whole disk device name.
+ */
+int sysfs_devno_to_wholedisk(dev_t dev, char *diskname,
+ size_t len, dev_t *diskdevno)
+{
+ struct sysfs_cxt cxt;
+ int is_part = 0;
+
+ if (!dev || sysfs_init(&cxt, dev, NULL) != 0)
+ return -1;
+
+ is_part = sysfs_has_attribute(&cxt, "partition");
+ if (!is_part) {
+ /*
+ * Extra case for partitions mapped by device-mapper.
+ *
+ * All regualar partitions (added by BLKPG ioctl or kernel PT
+ * parser) have the /sys/.../partition file. The partitions
+ * mapped by DM don't have such file, but they have "part"
+ * prefix in DM UUID.
+ */
+ char *uuid = sysfs_strdup(&cxt, "dm/uuid");
+ char *tmp = uuid;
+ char *prefix = uuid ? strsep(&tmp, "-") : NULL;
+
+ if (prefix && strncasecmp(prefix, "part", 4) == 0)
+ is_part = 1;
+ free(uuid);
+
+ if (is_part &&
+ get_dm_wholedisk(&cxt, diskname, len, diskdevno) == 0)
+ /*
+ * partitioned device, mapped by DM
+ */
+ goto done;
+
+ is_part = 0;
+ }
+
+ if (!is_part) {
+ /*
+ * unpartitioned device
+ */
+ if (diskname && len) {
+ if (!sysfs_get_devname(&cxt, diskname, len))
+ goto err;
+ }
+ if (diskdevno)
+ *diskdevno = dev;
+
+ } else {
+ /*
+ * partitioned device
+ * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1
+ * - dirname ../../block/sda/sda1 = ../../block/sda
+ * - basename ../../block/sda = sda
+ */
+ char linkpath[PATH_MAX];
+ char *name;
+ int linklen;
+
+ linklen = sysfs_readlink(&cxt, NULL,
+ linkpath, sizeof(linkpath) - 1);
+ if (linklen < 0)
+ goto err;
+ linkpath[linklen] = '\0';
+
+ stripoff_last_component(linkpath); /* dirname */
+ name = stripoff_last_component(linkpath); /* basename */
+ if (!name)
+ goto err;
+
+ if (diskname && len) {
+ strncpy(diskname, name, len);
+ diskname[len - 1] = '\0';
+ }
+
+ if (diskdevno) {
+ *diskdevno = sysfs_devname_to_devno(name, NULL);
+ if (!*diskdevno)
+ goto err;
+ }
+ }
+
+done:
+ sysfs_deinit(&cxt);
+ return 0;
+err:
+ sysfs_deinit(&cxt);
+ return -1;
+}
+
+/*
+ * Returns 1 if the device is private LVM device.
+ */
+int sysfs_devno_is_lvm_private(dev_t devno)
+{
+ struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
+ char *uuid = NULL;
+ int rc = 0;
+
+ if (sysfs_init(&cxt, devno, NULL) != 0)
+ return 0;
+
+ uuid = sysfs_strdup(&cxt, "dm/uuid");
+
+ /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important
+ * is the "LVM" prefix and "-<name>" postfix).
+ */
+ if (uuid && strncmp(uuid, "LVM-", 4) == 0) {
+ char *p = strrchr(uuid + 4, '-');
+
+ if (p && *(p + 1))
+ rc = 1;
+ }
+
+ sysfs_deinit(&cxt);
+ free(uuid);
+ return rc;
+}
+
+/*
+ * Return 0 or 1, or < 0 in case of error
+ */
+int sysfs_devno_is_wholedisk(dev_t devno)
+{
+ dev_t disk;
+
+ if (sysfs_devno_to_wholedisk(devno, NULL, 0, &disk) != 0)
+ return -1;
+
+ return devno == disk;
+}
+
+
+int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h, int *c, int *t, int *l)
+{
+ char buf[PATH_MAX], *hctl;
+ ssize_t len;
+
+ if (!cxt)
+ return -EINVAL;
+ if (cxt->has_hctl)
+ goto done;
+
+ len = sysfs_readlink(cxt, "device", buf, sizeof(buf) - 1);
+ if (len < 0)
+ return len;
+
+ buf[len] = '\0';
+ hctl = strrchr(buf, '/');
+ if (!hctl)
+ return -1;
+ hctl++;
+
+ if (sscanf(hctl, "%u:%u:%u:%u", &cxt->scsi_host, &cxt->scsi_channel,
+ &cxt->scsi_target, &cxt->scsi_lun) != 4)
+ return -1;
+
+ cxt->has_hctl = 1;
+done:
+ if (h)
+ *h = cxt->scsi_host;
+ if (c)
+ *c = cxt->scsi_channel;
+ if (t)
+ *t = cxt->scsi_target;
+ if (l)
+ *l = cxt->scsi_lun;
+ return 0;
+}
+
+
+static char *sysfs_scsi_host_attribute_path(struct sysfs_cxt *cxt,
+ const char *type, char *buf, size_t bufsz, const char *attr)
+{
+ int len;
+ int host;
+
+ if (sysfs_scsi_get_hctl(cxt, &host, NULL, NULL, NULL))
+ return NULL;
+
+ if (attr)
+ len = snprintf(buf, bufsz, _PATH_SYS_CLASS "/%s_host/host%d/%s",
+ type, host, attr);
+ else
+ len = snprintf(buf, bufsz, _PATH_SYS_CLASS "/%s_host/host%d",
+ type, host);
+
+ return (len < 0 || (size_t) len + 1 > bufsz) ? NULL : buf;
+}
+
+char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt *cxt,
+ const char *type, const char *attr)
+{
+ char buf[1024];
+ int rc;
+ FILE *f;
+
+ if (!attr || !type ||
+ !sysfs_scsi_host_attribute_path(cxt, type, buf, sizeof(buf), attr))
+ return NULL;
+
+ if (!(f = fopen(buf, "r" UL_CLOEXECSTR)))
+ return NULL;
+
+ rc = fscanf(f, "%1023[^\n]", buf);
+ fclose(f);
+
+ return rc == 1 ? strdup(buf) : NULL;
+}
+
+int sysfs_scsi_host_is(struct sysfs_cxt *cxt, const char *type)
+{
+ char buf[PATH_MAX];
+ struct stat st;
+
+ if (!type || !sysfs_scsi_host_attribute_path(cxt, type,
+ buf, sizeof(buf), NULL))
+ return 0;
+
+ return stat(buf, &st) == 0 && S_ISDIR(st.st_mode);
+}
+
+static char *sysfs_scsi_attribute_path(struct sysfs_cxt *cxt,
+ char *buf, size_t bufsz, const char *attr)
+{
+ int len, h, c, t, l;
+
+ if (sysfs_scsi_get_hctl(cxt, &h, &c, &t, &l) != 0)
+ return NULL;
+
+ if (attr)
+ len = snprintf(buf, bufsz, _PATH_SYS_SCSI "/devices/%d:%d:%d:%d/%s",
+ h,c,t,l, attr);
+ else
+ len = snprintf(buf, bufsz, _PATH_SYS_SCSI "/devices/%d:%d:%d:%d",
+ h,c,t,l);
+ return (len < 0 || (size_t) len + 1 > bufsz) ? NULL : buf;
+}
+
+int sysfs_scsi_has_attribute(struct sysfs_cxt *cxt, const char *attr)
+{
+ char path[PATH_MAX];
+ struct stat st;
+
+ if (!sysfs_scsi_attribute_path(cxt, path, sizeof(path), attr))
+ return 0;
+
+ return stat(path, &st) == 0;
+}
+
+int sysfs_scsi_path_contains(struct sysfs_cxt *cxt, const char *pattern)
+{
+ char path[PATH_MAX], linkc[PATH_MAX];
+ struct stat st;
+ ssize_t len;
+
+ if (!sysfs_scsi_attribute_path(cxt, path, sizeof(path), NULL))
+ return 0;
+
+ if (stat(path, &st) != 0)
+ return 0;
+
+ len = readlink(path, linkc, sizeof(linkc) - 1);
+ if (len < 0)
+ return 0;
+
+ linkc[len] = '\0';
+ return strstr(linkc, pattern) != NULL;
+}
+
+#ifdef TEST_PROGRAM_SYSFS
+#include <errno.h>
+#include <err.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+ struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
+ char *devname;
+ dev_t devno;
+ char path[PATH_MAX], *sub, *chain;
+ int i, is_part;
+ uint64_t u64;
+ ssize_t len;
+
+ if (argc != 2)
+ errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]);
+
+ devname = argv[1];
+ devno = sysfs_devname_to_devno(devname, NULL);
+
+ if (!devno)
+ err(EXIT_FAILURE, "failed to read devno");
+
+ is_part = sysfs_devno_has_attribute(devno, "partition");
+
+ printf("NAME: %s\n", devname);
+ printf("DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno));
+ printf("DEVNOPATH: %s\n", sysfs_devno_path(devno, path, sizeof(path)));
+ printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path)));
+ printf("PARTITION: %s\n", is_part ? "YES" : "NOT");
+
+ if (sysfs_init(&cxt, devno, NULL))
+ return EXIT_FAILURE;
+
+ len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1);
+ if (len > 0) {
+ path[len] = '\0';
+ printf("DEVNOLINK: %s\n", path);
+ }
+
+ if (!is_part) {
+ printf("First 5 partitions:\n");
+ for (i = 1; i <= 5; i++) {
+ dev_t dev = sysfs_partno_to_devno(&cxt, i);
+ if (dev)
+ printf("\t#%d %d:%d\n", i, major(dev), minor(dev));
+ }
+ }
+
+ printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves"));
+
+ if (sysfs_read_u64(&cxt, "size", &u64))
+ printf("read SIZE failed\n");
+ else
+ printf("SIZE: %jd\n", u64);
+
+ if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i))
+ printf("read SECTOR failed\n");
+ else
+ printf("SECTOR: %d\n", i);
+
+ printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path)));
+ printf("HOTPLUG: %s\n", sysfs_is_hotpluggable(&cxt) ? "yes" : "no");
+
+ chain = sysfs_get_devchain(&cxt, path, sizeof(path));
+ printf("SUBSUSTEMS:\n");
+
+ while (chain && sysfs_next_subsystem(&cxt, chain, &sub) == 0) {
+ printf("\t%s\n", sub);
+ free(sub);
+ }
+
+
+ sysfs_deinit(&cxt);
+ return EXIT_SUCCESS;
+}
+#endif
diff --git a/libblkid/lib/terminal-colors.d.5 b/libblkid/lib/terminal-colors.d.5
new file mode 100644
index 000000000..66ecf2c48
--- /dev/null
+++ b/libblkid/lib/terminal-colors.d.5
@@ -0,0 +1,190 @@
+.\" terminal-colors.d.5 --
+.\" Copyright 2014 Ondrej Oprala <ooprala@redhat.com>
+.\" Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+.\" Copyright 2014 Red Hat, Inc.
+.\" May be distributed under the GNU General Public License
+.TH "TERMINAL_COLORS.D" "5" "January 2014" "util-linux" "terminal-colors.d"
+.SH "NAME"
+terminal-colors.d \- Configure output colorization for various utilities
+.SH "SYNOPSIS"
+/etc/terminal-colors\&.d/[[\fIname\fR][@\fIterm\fR]\&.][\fItype\fR]
+.SH "DESCRIPTION"
+Files in this directory determine the default behavior for utilities
+when coloring output.
+
+The
+.I name
+is a utility name. The name is optional and when none is specified then the
+file is used for all unspecified utilities.
+
+The
+.I term
+is a terminal identifier (the TERM environment variable).
+The terminal identifier is optional and when none is specified then the file
+is used for all unspecified terminals.
+
+The
+.I type
+is a file type. Supported file types are:
+.TP
+.B disable
+Turns off output colorization for all compatible utilities.
+.TP
+.B enable
+Turns on output colorization; any matching
+.B disable
+files are ignored.
+.TP
+.B scheme
+Specifies colors used for output. The file format may be specific to the utility,
+the default format is described below.
+.PP
+If there are more files that match for a utility, then the file with the more
+specific filename wins. For example, the filename "@xterm.scheme" has less
+priority than "dmesg@xterm.scheme". The lowest priority are those files without a
+utility name and terminal identifier (e.g. "disable").
+
+The user-specific
+.I $XDG_CONFIG_HOME/terminal-colors.d
+or
+.I $HOME/.config/terminal-colors.d
+overrides the global setting.
+
+.SH EXAMPLES
+Disable colors for all compatible utilities:
+.RS
+.br
+.B "touch /etc/terminal-colors.d/disable"
+.br
+.RE
+
+Disable colors for all compatible utils on a vt100 terminal:
+.RS
+.br
+.B "touch /etc/terminal-colors.d/@vt100.disable"
+.br
+.RE
+
+Disable colors for all compatible utils except dmesg(1):
+.RS
+.br
+.B "touch /etc/terminal-colors.d/disable"
+.sp
+.B "touch /etc/terminal-colors.d/dmesg.enable"
+.br
+.RE
+
+.SH DEFAULT SCHEME FILES FORMAT
+The following statement is recognized:
+
+.RS
+.br
+.B "name color-sequence"
+.br
+.RE
+
+The
+.B name
+is a logical name of color sequence (for example "error"). The names are
+specific to the utilities. For more details always see the COLORS section
+in the man page for the utility.
+
+The
+.B color-sequence
+is a color name, ASCII color sequences or escape sequences.
+
+.SS Color names
+black, blue, brown, cyan, darkgray, gray, green, lightblue, lightcyan
+lightgray, lightgreen, lightmagenta, lightred, magenta, red and yellow
+.SS ANSI color sequences
+The color sequences are composed of sequences of numbers
+separated by semicolons. The most common codes are:
+.sp
+.RS
+.TS
+l l.
+ 0 to restore default color
+ 1 for brighter colors
+ 4 for underlined text
+ 5 for flashing text
+30 for black foreground
+31 for red foreground
+32 for green foreground
+33 for yellow (or brown) foreground
+34 for blue foreground
+35 for purple foreground
+36 for cyan foreground
+37 for white (or gray) foreground
+40 for black background
+41 for red background
+42 for green background
+43 for yellow (or brown) background
+44 for blue background
+45 for purple background
+46 for cyan background
+47 for white (or gray) background
+.TE
+.RE
+.SS Escape sequences
+To specify control or blank characters in the color sequences,
+C-style \e-escaped notation can be used:
+.sp
+.RS
+.TS
+lb l.
+\ea Bell (ASCII 7)
+\eb Backspace (ASCII 8)
+\ee Escape (ASCII 27)
+\ef Form feed (ASCII 12)
+\en Newline (ASCII 10)
+\er Carriage Return (ASCII 13)
+\et Tab (ASCII 9)
+\ev Vertical Tab (ASCII 11)
+\e? Delete (ASCII 127)
+\e_ Space
+\e\e Backslash (\e)
+\e^ Caret (^)
+\e# Hash mark (#)
+.TE
+.RE
+.sp
+Please note that escapes are necessary to enter a space, backslash,
+caret, or any control character anywhere in the string, as well as a
+hash mark as the first character.
+
+For example, to use a red background for alert messages in the output of
+.BR dmesg (1),
+use:
+
+.RS
+.br
+.B "echo 'alert 37;41' >> /etc/terminal-colors.d/dmesg.scheme"
+.br
+.RE
+
+.SS Comments
+Lines where the first non-blank character is a # (hash) are ignored.
+Any other use of the hash character is not interpreted as introducing
+a comment.
+
+.SH FILES
+.B $XDG_CONFIG_HOME/terminal-colors.d
+.br
+.B $HOME/.config/terminal-colors.d
+.br
+.B /etc/terminal-colors.d
+
+.SH ENVIRONMENT
+.IP TERMINAL_COLORS_DEBUG=all
+enables debug output.
+
+.SH COMPATIBILITY
+The terminal-colors.d functionality is currently supported by all util-linux
+utilities which provides colorized output. For more details always see the
+COLORS section in the man page for the utility.
+
+.SH AVAILABILITY
+terminal-colors.d is part of the util-linux package and is available from
+.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/libblkid/lib/timeutils.c b/libblkid/lib/timeutils.c
new file mode 100644
index 000000000..b811041e4
--- /dev/null
+++ b/libblkid/lib/timeutils.c
@@ -0,0 +1,342 @@
+/***
+ First set of functions in this file are part of systemd, and were
+ copied to util-linux at August 2013.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd 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.
+
+ systemd 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 util-linux; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/sysinfo.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "timeutils.h"
+
+#define WHITESPACE " \t\n\r"
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+
+static int parse_sec(const char *t, usec_t *usec)
+{
+ static const struct {
+ const char *suffix;
+ usec_t usec;
+ } table[] = {
+ { "seconds", USEC_PER_SEC },
+ { "second", USEC_PER_SEC },
+ { "sec", USEC_PER_SEC },
+ { "s", USEC_PER_SEC },
+ { "minutes", USEC_PER_MINUTE },
+ { "minute", USEC_PER_MINUTE },
+ { "min", USEC_PER_MINUTE },
+ { "months", USEC_PER_MONTH },
+ { "month", USEC_PER_MONTH },
+ { "msec", USEC_PER_MSEC },
+ { "ms", USEC_PER_MSEC },
+ { "m", USEC_PER_MINUTE },
+ { "hours", USEC_PER_HOUR },
+ { "hour", USEC_PER_HOUR },
+ { "hr", USEC_PER_HOUR },
+ { "h", USEC_PER_HOUR },
+ { "days", USEC_PER_DAY },
+ { "day", USEC_PER_DAY },
+ { "d", USEC_PER_DAY },
+ { "weeks", USEC_PER_WEEK },
+ { "week", USEC_PER_WEEK },
+ { "w", USEC_PER_WEEK },
+ { "years", USEC_PER_YEAR },
+ { "year", USEC_PER_YEAR },
+ { "y", USEC_PER_YEAR },
+ { "usec", 1ULL },
+ { "us", 1ULL },
+ { "", USEC_PER_SEC }, /* default is sec */
+ };
+
+ const char *p;
+ usec_t r = 0;
+ int something = FALSE;
+
+ assert(t);
+ assert(usec);
+
+ p = t;
+ for (;;) {
+ long long l, z = 0;
+ char *e;
+ unsigned i, n = 0;
+
+ p += strspn(p, WHITESPACE);
+
+ if (*p == 0) {
+ if (!something)
+ return -EINVAL;
+
+ break;
+ }
+
+ errno = 0;
+ l = strtoll(p, &e, 10);
+
+ if (errno > 0)
+ return -errno;
+
+ if (l < 0)
+ return -ERANGE;
+
+ if (*e == '.') {
+ char *b = e + 1;
+
+ errno = 0;
+ z = strtoll(b, &e, 10);
+ if (errno > 0)
+ return -errno;
+
+ if (z < 0)
+ return -ERANGE;
+
+ if (e == b)
+ return -EINVAL;
+
+ n = e - b;
+
+ } else if (e == p)
+ return -EINVAL;
+
+ e += strspn(e, WHITESPACE);
+
+ for (i = 0; i < ARRAY_SIZE(table); i++)
+ if (startswith(e, table[i].suffix)) {
+ usec_t k = (usec_t) z * table[i].usec;
+
+ for (; n > 0; n--)
+ k /= 10;
+
+ r += (usec_t) l *table[i].usec + k;
+ p = e + strlen(table[i].suffix);
+
+ something = TRUE;
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(table))
+ return -EINVAL;
+
+ }
+
+ *usec = r;
+
+ return 0;
+}
+
+int parse_timestamp(const char *t, usec_t *usec)
+{
+ static const struct {
+ const char *name;
+ const int nr;
+ } day_nr[] = {
+ { "Sunday", 0 },
+ { "Sun", 0 },
+ { "Monday", 1 },
+ { "Mon", 1 },
+ { "Tuesday", 2 },
+ { "Tue", 2 },
+ { "Wednesday", 3 },
+ { "Wed", 3 },
+ { "Thursday", 4 },
+ { "Thu", 4 },
+ { "Friday", 5 },
+ { "Fri", 5 },
+ { "Saturday", 6 },
+ { "Sat", 6 },
+ };
+
+ const char *k;
+ struct tm tm, copy;
+ time_t x;
+ usec_t plus = 0, minus = 0, ret;
+ int r, weekday = -1;
+ unsigned i;
+
+ /*
+ * Allowed syntaxes:
+ *
+ * 2012-09-22 16:34:22
+ * 2012-09-22 16:34 (seconds will be set to 0)
+ * 2012-09-22 (time will be set to 00:00:00)
+ * 16:34:22 (date will be set to today)
+ * 16:34 (date will be set to today, seconds to 0)
+ * now
+ * yesterday (time is set to 00:00:00)
+ * today (time is set to 00:00:00)
+ * tomorrow (time is set to 00:00:00)
+ * +5min
+ * -5days
+ *
+ */
+
+ assert(t);
+ assert(usec);
+
+ x = time(NULL);
+ localtime_r(&x, &tm);
+ tm.tm_isdst = -1;
+
+ if (streq(t, "now"))
+ goto finish;
+
+ else if (streq(t, "today")) {
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+
+ } else if (streq(t, "yesterday")) {
+ tm.tm_mday--;
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+
+ } else if (streq(t, "tomorrow")) {
+ tm.tm_mday++;
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+
+ } else if (t[0] == '+') {
+
+ r = parse_sec(t + 1, &plus);
+ if (r < 0)
+ return r;
+
+ goto finish;
+ } else if (t[0] == '-') {
+
+ r = parse_sec(t + 1, &minus);
+ if (r < 0)
+ return r;
+
+ goto finish;
+
+ } else if (endswith(t, " ago")) {
+ char *z;
+
+ z = strndup(t, strlen(t) - 4);
+ if (!z)
+ return -ENOMEM;
+
+ r = parse_sec(z, &minus);
+ free(z);
+ if (r < 0)
+ return r;
+
+ goto finish;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(day_nr); i++) {
+ size_t skip;
+
+ if (!startswith_no_case(t, day_nr[i].name))
+ continue;
+
+ skip = strlen(day_nr[i].name);
+ if (t[skip] != ' ')
+ continue;
+
+ weekday = day_nr[i].nr;
+ t += skip + 1;
+ break;
+ }
+
+ copy = tm;
+ k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
+ if (k && *k == 0)
+ goto finish;
+
+ tm = copy;
+ k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
+ if (k && *k == 0)
+ goto finish;
+
+ tm = copy;
+ k = strptime(t, "%y-%m-%d %H:%M", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = 0;
+ goto finish;
+ }
+
+ tm = copy;
+ k = strptime(t, "%Y-%m-%d %H:%M", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = 0;
+ goto finish;
+ }
+
+ tm = copy;
+ k = strptime(t, "%y-%m-%d", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+ }
+
+ tm = copy;
+ k = strptime(t, "%Y-%m-%d", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+ goto finish;
+ }
+
+ tm = copy;
+ k = strptime(t, "%H:%M:%S", &tm);
+ if (k && *k == 0)
+ goto finish;
+
+ tm = copy;
+ k = strptime(t, "%H:%M", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = 0;
+ goto finish;
+ }
+
+ tm = copy;
+ k = strptime(t, "%Y%m%d%H%M%S", &tm);
+ if (k && *k == 0) {
+ tm.tm_sec = 0;
+ goto finish;
+ }
+
+ return -EINVAL;
+
+ finish:
+ x = mktime(&tm);
+ if (x == (time_t)-1)
+ return -EINVAL;
+
+ if (weekday >= 0 && tm.tm_wday != weekday)
+ return -EINVAL;
+
+ ret = (usec_t) x *USEC_PER_SEC;
+
+ ret += plus;
+ if (ret > minus)
+ ret -= minus;
+ else
+ ret = 0;
+
+ *usec = ret;
+
+ return 0;
+}
diff --git a/libblkid/lib/ttyutils.c b/libblkid/lib/ttyutils.c
new file mode 100644
index 000000000..ea551e26c
--- /dev/null
+++ b/libblkid/lib/ttyutils.c
@@ -0,0 +1,95 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <ctype.h>
+
+#include "c.h"
+#include "ttyutils.h"
+
+int get_terminal_width(void)
+{
+#ifdef TIOCGSIZE
+ struct ttysize t_win;
+#endif
+#ifdef TIOCGWINSZ
+ struct winsize w_win;
+#endif
+ const char *cp;
+
+#ifdef TIOCGSIZE
+ if (ioctl (STDIN_FILENO, TIOCGSIZE, &t_win) == 0)
+ return t_win.ts_cols;
+#endif
+#ifdef TIOCGWINSZ
+ if (ioctl (STDIN_FILENO, TIOCGWINSZ, &w_win) == 0)
+ return w_win.ws_col;
+#endif
+ cp = getenv("COLUMNS");
+ if (cp) {
+ char *end = NULL;
+ long c;
+
+ errno = 0;
+ c = strtol(cp, &end, 10);
+
+ if (errno == 0 && end && *end == '\0' && end > cp &&
+ c > 0 && c <= INT_MAX)
+ return c;
+ }
+ return 0;
+}
+
+int get_terminal_name(int fd,
+ const char **path,
+ const char **name,
+ const char **number)
+{
+ const char *tty;
+ const char *p;
+
+ if (name)
+ *name = NULL;
+ if (path)
+ *path = NULL;
+ if (number)
+ *number = NULL;
+
+ tty = ttyname(fd);
+ if (!tty)
+ return -1;
+ if (path)
+ *path = tty;
+ tty = strncmp(tty, "/dev/", 5) == 0 ? tty + 5 : tty;
+ if (name)
+ *name = tty;
+ if (number) {
+ for (p = tty; p && *p; p++) {
+ if (isdigit(*p)) {
+ *number = p;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+
+#ifdef TEST_PROGRAM
+# include <stdlib.h>
+int main(void)
+{
+ const char *path, *name, *num;
+
+ if (get_terminal_name(STDERR_FILENO, &path, &name, &num) == 0) {
+ fprintf(stderr, "tty path: %s\n", path);
+ fprintf(stderr, "tty name: %s\n", name);
+ fprintf(stderr, "tty number: %s\n", num);
+ }
+ fprintf(stderr, "tty width: %d\n", get_terminal_width());
+
+ return EXIT_SUCCESS;
+}
+#endif
diff --git a/libblkid/libblkid.3 b/libblkid/libblkid.3
new file mode 100644
index 000000000..58ca91cbb
--- /dev/null
+++ b/libblkid/libblkid.3
@@ -0,0 +1,79 @@
+.\" Copyright 2001 Andreas Dilger (adilger@turbolinux.com)
+.\"
+.\" This man page was created for libblkid.so.1.0 from e2fsprogs-1.24.
+.\"
+.\" This file may be copied under the terms of the GNU Lesser General Public
+.\" License.
+.\"
+.\" Created Wed Sep 14 12:02:12 2001, Andreas Dilger
+.TH LIBBLKID 3 "May 2009" "util-linux" "Programmer's Manual"
+.SH NAME
+libblkid \- block device identification library
+.SH SYNOPSIS
+.B #include <blkid.h>
+.sp
+.B cc
+.I file.c
+.B \-lblkid
+.SH DESCRIPTION
+The
+.B libblkid
+library is used to identify block devices (disks) as to their content (e.g.
+filesystem type) as well as extracting additional information such as
+filesystem labels/volume names, unique identifiers/serial numbers.
+A common use is to allow use of LABEL= and UUID= tags instead of hard-coding
+specific block device names into configuration files.
+.P
+The low-level part of the library also allows to extract information about
+partitions and block device topology.
+.P
+The high-level part of the library keeps information about block devices in a
+cache file 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.
+.P
+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).
+.P
+The high-level part of the library supports two methods to evaluate LABEL/UUID.
+It reads information directly from a block device or read information from
+/dev/disk/by-* udev symlinks. The udev is preferred method by default.
+.P
+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.
+.P
+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
+.B required
+in this situation.
+.SH CONFIGURATION FILE
+The standard location of the
+.I /etc/blkid.conf
+config file can be overridden by the environment variable BLKID_CONF. For more
+details about the config file see
+.BR blkid (8)
+man page.
+.SH AUTHOR
+.B libblkid
+was written by Andreas Dilger for the ext2 filesystem utilties, with input
+from Ted Ts'o. The library was subsequently heavily modified by Ted Ts'o.
+
+The low-level probing code was rewritten by Karel Zak.
+.SH COPYING
+.B libblkid
+is available under the terms of the GNU Library General Public License (LGPL),
+version 2 (or at your discretion any later version).
+.SH "SEE ALSO"
+.BR blkid (8),
+.BR findfs (8)
+.SH AVAILABILITY
+libblkid is part of the util-linux package since version 2.15 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
diff --git a/libblkid/libfdisk/COPYING b/libblkid/libfdisk/COPYING
new file mode 100644
index 000000000..be1a5b3a1
--- /dev/null
+++ b/libblkid/libfdisk/COPYING
@@ -0,0 +1,8 @@
+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.
+
+The complete text of the license is available in the
+../Documentation/licenses/COPYING.LGPLv2.1 file.
diff --git a/libblkid/libfdisk/Makemodule.am b/libblkid/libfdisk/Makemodule.am
new file mode 100644
index 000000000..5d8334164
--- /dev/null
+++ b/libblkid/libfdisk/Makemodule.am
@@ -0,0 +1,14 @@
+if BUILD_LIBFDISK
+
+include libfdisk/src/Makemodule.am
+
+if ENABLE_GTK_DOC
+# Docs uses separate Makefiles
+SUBDIRS += libfdisk/docs
+endif
+
+pkgconfig_DATA += libfdisk/fdisk.pc
+PATHFILES += libfdisk/fdisk.pc
+EXTRA_DIST += libfdisk/COPYING
+
+endif # BUILD_LIBFDISK
diff --git a/libblkid/libfdisk/docs/.gitignore b/libblkid/libfdisk/docs/.gitignore
new file mode 100644
index 000000000..f91f93db7
--- /dev/null
+++ b/libblkid/libfdisk/docs/.gitignore
@@ -0,0 +1,18 @@
+*-decl-list.txt
+*-decl.txt
+*-overrides.txt
+*-undeclared.txt
+*-undocumented.txt
+*-unused.txt
+*.args
+*.bak
+*.hierarchy
+*.interfaces
+*.prerequisites
+*.signals
+*.stamp
+*.types
+html/*
+tmpl/*
+version.xml
+xml/*
diff --git a/libblkid/libfdisk/docs/Makefile.am b/libblkid/libfdisk/docs/Makefile.am
new file mode 100644
index 000000000..dc7097995
--- /dev/null
+++ b/libblkid/libfdisk/docs/Makefile.am
@@ -0,0 +1,93 @@
+## Process this file with automake to produce Makefile.in
+
+# We require automake 1.10 at least.
+AUTOMAKE_OPTIONS = 1.10
+
+# This is a blank Makefile.am for using gtk-doc.
+# Copy this to your project's API docs directory and modify the variables to
+# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
+# of using the various options.
+
+# The name of the module, e.g. 'glib'.
+DOC_MODULE=libfdisk
+
+# Uncomment for versioned docs and specify the version of the module, e.g. '2'.
+#DOC_MODULE_VERSION=2
+
+# The top-level SGML file. You can change this if you want to.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting the functions and macros.
+# e.g. DOC_SOURCE_DIR=../../../gtk
+DOC_SOURCE_DIR=../src
+
+# Extra options to pass to gtkdoc-scangobj. Not normally needed.
+SCANGOBJ_OPTIONS=
+
+# Extra options to supply to gtkdoc-scan.
+# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
+SCAN_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkdb.
+# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml
+MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space fdisk
+
+# Extra options to supply to gtkdoc-mktmpl
+# e.g. MKTMPL_OPTIONS=--only-section-tmpl
+MKTMPL_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkhtml
+MKHTML_OPTIONS=
+
+# Extra options to supply to gtkdoc-fixref. Not normally needed.
+# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
+FIXXREF_OPTIONS=
+
+# Used for dependencies. The docs will be rebuilt if any of these change.
+# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
+# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
+HFILE_GLOB=$(top_builddir)/libfdisk/src/libfdisk.h
+CFILE_GLOB=$(top_srcdir)/libfdisk/src/*.c
+
+# Extra header to include when scanning, which are not under DOC_SOURCE_DIR
+# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
+EXTRA_HFILES=
+
+# Header files to ignore when scanning. Use base file name, no paths
+# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
+IGNORE_HFILES=fdiskP.h
+
+# Images to copy into HTML directory.
+# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
+HTML_IMAGES=
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
+content_files = $(builddir)/version.xml
+
+# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
+# These files must be listed here *and* in content_files
+# e.g. expand_content_files=running.sgml
+expand_content_files=
+
+# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
+# Only needed if you are using gtkdoc-scangobj to dynamically query widget
+# signals and properties.
+# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
+# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
+GTKDOC_CFLAGS=
+GTKDOC_LIBS=
+
+# This includes the standard gtk-doc make rules, copied by gtkdocize.
+include $(top_srcdir)/config/gtk-doc.make
+
+# Other files to distribute
+# e.g. EXTRA_DIST += version.xml.in
+EXTRA_DIST += version.xml.in
+
+# Files not to distribute
+# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
+# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
+DISTCLEANFILES += version.xml
diff --git a/libblkid/libfdisk/docs/libfdisk-docs.xml b/libblkid/libfdisk/docs/libfdisk-docs.xml
new file mode 100644
index 000000000..546d007a7
--- /dev/null
+++ b/libblkid/libfdisk/docs/libfdisk-docs.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+ "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+ <!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+ <bookinfo>
+ <title>libfdisk Reference Manual</title>
+ <releaseinfo>for libfdisk version &version;</releaseinfo>
+ <copyright>
+ <year>2014</year>
+ <holder>Karel Zak &lt;kzak@redhat.com&gt;</holder>
+ </copyright>
+ </bookinfo>
+
+ <part id="over">
+ <title>libfdisk Overview</title>
+ <partintro>
+ <para>
+The libfdisk library is used for manipulating with partition tables.
+ </para>
+ <para>
+The library is part of the util-linux package since version 2.26 and is
+available from ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+ </para>
+ </partintro>
+ </part>
+
+ <part>
+ <title>Basic handlers and setting</title>
+ <xi:include href="xml/context.xml"/>
+ <xi:include href="xml/ask.xml"/>
+ <xi:include href="xml/alignment.xml"/>
+ <xi:include href="xml/script.xml"/>
+ </part>
+ <part>
+ <title>Partitining</title>
+ <xi:include href="xml/label.xml"/>
+ <xi:include href="xml/partition.xml"/>
+ <xi:include href="xml/table.xml"/>
+ <xi:include href="xml/parttype.xml"/>
+ </part>
+ <part>
+ <title>Label specific functions</title>
+ <xi:include href="xml/dos.xml"/>
+ <xi:include href="xml/gpt.xml"/>
+ <xi:include href="xml/sun.xml"/>
+ <xi:include href="xml/sgi.xml"/>
+ <xi:include href="xml/bsd.xml"/>
+ </part>
+ <part>
+ <title>Misc</title>
+ <xi:include href="xml/iter.xml"/>
+ <xi:include href="xml/utils.xml"/>
+ <xi:include href="xml/init.xml"/>
+ </part>
+ <index id="api-index-full">
+ <title>API Index</title>
+ <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+ </index>
+</book>
diff --git a/libblkid/libfdisk/docs/libfdisk-sections.txt b/libblkid/libfdisk/docs/libfdisk-sections.txt
new file mode 100644
index 000000000..c0aaeaef8
--- /dev/null
+++ b/libblkid/libfdisk/docs/libfdisk-sections.txt
@@ -0,0 +1,303 @@
+<SECTION>
+<FILE>init</FILE>
+fdisk_init_debug
+</SECTION>
+
+<SECTION>
+<FILE>ask</FILE>
+fdisk_info
+fdisk_warn
+fdisk_warnx
+fdisk_set_ask
+<SUBSECTION>
+fdisk_ask
+fdisk_ask_get_query
+fdisk_ask_get_type
+fdisk_ask_menu_get_default
+fdisk_ask_menu_get_item
+fdisk_ask_menu_get_nitems
+fdisk_ask_menu_get_result
+fdisk_ask_menu_set_result
+fdisk_ask_number
+fdisk_ask_number_get_base
+fdisk_ask_number_get_default
+fdisk_ask_number_get_high
+fdisk_ask_number_get_low
+fdisk_ask_number_get_range
+fdisk_ask_number_get_result
+fdisk_ask_number_get_unit
+fdisk_ask_number_inchars
+fdisk_ask_number_set_relative
+fdisk_ask_number_set_result
+fdisk_ask_partnum
+fdisk_ask_print_get_errno
+fdisk_ask_print_get_mesg
+fdisk_ask_string
+fdisk_ask_string_get_result
+fdisk_ask_string_set_result
+fdisk_ask_yesno
+fdisk_ask_yesno_get_result
+fdisk_ask_yesno_set_result
+fdisk_ref_ask
+fdisk_unref_ask
+</SECTION>
+
+<SECTION>
+<FILE>alignment</FILE>
+fdisk_align_lba
+fdisk_align_lba_in_range
+fdisk_has_user_device_properties
+fdisk_lba_is_phy_aligned
+fdisk_override_geometry
+fdisk_reread_partition_table
+fdisk_reset_alignment
+fdisk_reset_device_properties
+fdisk_save_user_geometry
+fdisk_save_user_sector_size
+</SECTION>
+
+<SECTION>
+<FILE>label</FILE>
+fdisk_create_disklabel
+fdisk_list_disklabel
+fdisk_locate_disklabel
+fdisk_reorder_partitions
+fdisk_set_disklabel_id
+fdisk_set_partition_type
+fdisk_toggle_partition_flag
+fdisk_verify_disklabel
+fdisk_write_disklabel
+<SUBSECTION>
+fdisk_get_disklabel_id
+fdisk_get_label
+fdisk_get_nlabels
+fdisk_next_label
+fdisk_get_npartitions
+<SUBSECTION>
+fdisk_field
+fdisk_field_get_id
+fdisk_field_get_name
+fdisk_field_get_width
+fdisk_field_is_number
+<SUBSECTION>
+fdisk_label
+fdisk_label_get_field
+fdisk_label_get_field_by_name
+fdisk_label_get_fields_ids
+fdisk_label_get_name
+fdisk_label_get_nparttypes
+fdisk_label_get_parttype
+fdisk_label_get_parttype_from_code
+fdisk_label_get_parttype_from_string
+fdisk_label_get_type
+fdisk_label_has_code_parttypes
+fdisk_label_is_changed
+fdisk_label_is_disabled
+fdisk_label_parse_parttype
+fdisk_label_require_geometry
+fdisk_label_set_changed
+fdisk_label_set_disabled
+</SECTION>
+
+<SECTION>
+<FILE>script</FILE>
+fdisk_set_script
+fdisk_get_script
+<SUBSECTION>
+fdisk_apply_script
+fdisk_apply_script_headers
+<SUBSECTION>
+fdisk_script
+fdisk_new_script
+fdisk_new_script_from_file
+fdisk_ref_script
+fdisk_script_get_header
+fdisk_script_get_nlines
+fdisk_script_get_table
+fdisk_script_read_context
+fdisk_script_read_file
+fdisk_script_read_line
+fdisk_script_set_header
+fdisk_script_write_file
+fdisk_unref_script
+</SECTION>
+
+<SECTION>
+<FILE>bsd</FILE>
+fdisk_bsd_edit_disklabel
+fdisk_bsd_link_partition
+fdisk_bsd_write_bootstrap
+</SECTION>
+
+<SECTION>
+<FILE>partition</FILE>
+fdisk_add_partition
+fdisk_delete_all_partitions
+fdisk_delete_partition
+fdisk_get_partition
+fdisk_is_partition_used
+fdisk_set_partition
+<SUBSECTION>
+fdisk_partition
+fdisk_new_partition
+fdisk_partition_cmp_partno
+fdisk_partition_cmp_start
+fdisk_partition_end_follow_default
+fdisk_partition_end_is_default
+fdisk_partition_get_attrs
+fdisk_partition_get_end
+fdisk_partition_get_name
+fdisk_partition_get_parent
+fdisk_partition_get_partno
+fdisk_partition_get_size
+fdisk_partition_get_start
+fdisk_partition_get_type
+fdisk_partition_get_uuid
+fdisk_partition_has_end
+fdisk_partition_has_partno
+fdisk_partition_has_size
+fdisk_partition_has_start
+fdisk_partition_is_bootable
+fdisk_partition_is_container
+fdisk_partition_is_freespace
+fdisk_partition_is_nested
+fdisk_partition_is_used
+fdisk_partition_next_partno
+fdisk_partition_partno_follow_default
+fdisk_partition_set_attrs
+fdisk_partition_set_name
+fdisk_partition_set_partno
+fdisk_partition_set_size
+fdisk_partition_set_start
+fdisk_partition_set_type
+fdisk_partition_set_uuid
+fdisk_partition_size_explicit
+fdisk_partition_start_follow_default
+fdisk_partition_start_is_default
+fdisk_partition_to_string
+fdisk_partition_unset_partno
+fdisk_partition_unset_size
+fdisk_partition_unset_start
+fdisk_ref_partition
+fdisk_reset_partition
+fdisk_unref_partition
+</SECTION>
+
+<SECTION>
+<FILE>dos</FILE>
+fdisk_dos_enable_compatible
+fdisk_dos_is_compatible
+fdisk_dos_move_begin
+</SECTION>
+
+<SECTION>
+<FILE>sgi</FILE>
+fdisk_sgi_create_info
+fdisk_sgi_set_bootfile
+</SECTION>
+
+<SECTION>
+<FILE>gpt</FILE>
+fdisk_gpt_is_hybrid
+</SECTION>
+
+<SECTION>
+<FILE>sun</FILE>
+fdisk_sun_set_alt_cyl
+fdisk_sun_set_ilfact
+fdisk_sun_set_pcylcount
+fdisk_sun_set_rspeed
+fdisk_sun_set_xcyl
+</SECTION>
+
+<SECTION>
+<FILE>parttype</FILE>
+fdisk_parttype
+fdisk_copy_parttype
+fdisk_new_parttype
+fdisk_new_unknown_parttype
+fdisk_parttype_get_code
+fdisk_parttype_get_name
+fdisk_parttype_get_string
+fdisk_parttype_is_unknown
+fdisk_parttype_set_code
+fdisk_parttype_set_name
+fdisk_parttype_set_typestr
+fdisk_ref_parttype
+fdisk_unref_parttype
+</SECTION>
+
+<SECTION>
+<FILE>table</FILE>
+fdisk_get_freespaces
+fdisk_get_partitions
+<SUBSECTION>
+fdisk_table
+fdisk_apply_table
+fdisk_new_table
+fdisk_ref_table
+fdisk_reset_table
+fdisk_table_add_partition
+fdisk_table_get_nents
+fdisk_table_get_partition
+fdisk_table_is_empty
+fdisk_table_next_partition
+fdisk_table_remove_partition
+fdisk_table_sort_partitions
+fdisk_table_wrong_order
+fdisk_unref_table
+</SECTION>
+
+
+<SECTION>
+<FILE>context</FILE>
+fdisk_context
+fdisk_assign_device
+fdisk_deassign_device
+fdisk_enable_details
+fdisk_enable_listonly
+fdisk_get_alignment_offset
+fdisk_get_devfd
+fdisk_get_devname
+fdisk_get_first_lba
+fdisk_get_geom_cylinders
+fdisk_get_geom_heads
+fdisk_get_geom_sectors
+fdisk_get_grain_size
+fdisk_get_last_lba
+fdisk_get_minimal_iosize
+fdisk_get_nsectors
+fdisk_get_optimal_iosize
+fdisk_get_parent
+fdisk_get_physector_size
+fdisk_get_sector_size
+fdisk_get_unit
+fdisk_get_units_per_sector
+fdisk_has_label
+fdisk_has_user_device_properties
+fdisk_is_details
+fdisk_is_labeltype
+fdisk_is_listonly
+fdisk_is_readonly
+fdisk_new_context
+fdisk_new_nested_context
+fdisk_ref_context
+fdisk_set_first_lba
+fdisk_set_last_lba
+fdisk_set_unit
+fdisk_unref_context
+fdisk_use_cylinders
+</SECTION>
+
+<SECTION>
+<FILE>utils</FILE>
+fdisk_partname
+</SECTION>
+
+<SECTION>
+<FILE>iter</FILE>
+fdisk_free_iter
+fdisk_iter_get_direction
+fdisk_new_iter
+fdisk_reset_iter
+</SECTION>
diff --git a/libblkid/libfdisk/docs/version.xml.in b/libblkid/libfdisk/docs/version.xml.in
new file mode 100644
index 000000000..d78bda934
--- /dev/null
+++ b/libblkid/libfdisk/docs/version.xml.in
@@ -0,0 +1 @@
+@VERSION@
diff --git a/libblkid/libfdisk/fdisk.pc.in b/libblkid/libfdisk/fdisk.pc.in
new file mode 100644
index 000000000..bf81df009
--- /dev/null
+++ b/libblkid/libfdisk/fdisk.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@usrlib_execdir@
+includedir=@includedir@
+
+Name: fdisk
+Description: fdisk library
+Version: @LIBFDISK_VERSION@
+Requires.private: @LIBFDISK_PC_REQUIRES@
+Cflags: -I${includedir}/libfdisk
+Libs: -L${libdir} -lfdisk
diff --git a/libblkid/libfdisk/src/.gitignore b/libblkid/libfdisk/src/.gitignore
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/libblkid/libfdisk/src/.gitignore
diff --git a/libblkid/libfdisk/src/Makemodule.am b/libblkid/libfdisk/src/Makemodule.am
new file mode 100644
index 000000000..18ddec739
--- /dev/null
+++ b/libblkid/libfdisk/src/Makemodule.am
@@ -0,0 +1,112 @@
+
+# libfdisk.h is generated, so it's stored in builddir!
+fdiskincdir = $(includedir)/libfdisk
+nodist_fdiskinc_HEADERS = $(top_builddir)/libfdisk/src/libfdisk.h
+
+usrlib_exec_LTLIBRARIES += libfdisk.la
+libfdisk_la_SOURCES = \
+ include/list.h \
+ \
+ libfdisk/src/fdiskP.h \
+ libfdisk/src/init.c \
+ libfdisk/src/test.c \
+ libfdisk/src/ask.c \
+ libfdisk/src/alignment.c \
+ libfdisk/src/label.c \
+ libfdisk/src/utils.c \
+ libfdisk/src/context.c \
+ libfdisk/src/parttype.c \
+ libfdisk/src/partition.c \
+ libfdisk/src/table.c \
+ libfdisk/src/iter.c \
+ libfdisk/src/script.c \
+ \
+ libfdisk/src/sun.c \
+ libfdisk/src/sgi.c \
+ libfdisk/src/dos.c \
+ libfdisk/src/bsd.c \
+ libfdisk/src/gpt.c \
+ $(nodist_fdiskinc_HEADERS)
+
+
+nodist_libfdisk_la_SOURCES = libfdisk/src/fdiskP.h
+
+libfdisk_la_LIBADD = libcommon.la libuuid.la
+
+libfdisk_la_CFLAGS = \
+ $(SOLIB_CFLAGS) \
+ -I$(ul_libuuid_incdir) \
+ -I$(ul_libfdisk_incdir) \
+ -I$(top_srcdir)/libfdisk/src
+
+libfdisk_la_DEPENDENCIES = \
+ libcommon.la \
+ libuuid.la \
+ libfdisk/src/libfdisk.sym \
+ libfdisk/src/libfdisk.h.in
+
+libfdisk_la_LDFLAGS = \
+ $(SOLIB_LDFLAGS) \
+ -Wl,--version-script=$(top_srcdir)/libfdisk/src/libfdisk.sym \
+ -version-info $(LIBFDISK_VERSION_INFO)
+
+if BUILD_LIBBLKID
+libfdisk_la_LIBADD += libblkid.la
+libfdisk_la_DEPENDENCIES += libblkid.la
+libfdisk_la_CFLAGS += -I$(ul_libblkid_incdir)
+endif
+
+EXTRA_DIST += \
+ libfdisk/src/libfdisk.sym \
+ libfdisk/src/libfdisk.h.in
+
+if BUILD_LIBFDISK_TESTS
+check_PROGRAMS += \
+ test_fdisk_ask \
+ test_fdisk_script \
+ test_fdisk_utils
+
+libfdisk_tests_cflags = -DTEST_PROGRAM $(libfdisk_la_CFLAGS)
+libfdisk_tests_ldflags = libuuid.la -static
+libfdisk_tests_ldadd = libfdisk.la $(UUID_LIBS)
+
+if BUILD_LIBBLKID
+libfdisk_tests_ldflags += libblkid.la
+endif
+
+test_fdisk_ask_SOURCES = libfdisk/src/ask.c
+test_fdisk_ask_CFLAGS = $(libfdisk_tests_cflags)
+test_fdisk_ask_LDFLAGS = $(libfdisk_tests_ldflags)
+test_fdisk_ask_LDADD = $(libfdisk_tests_ldadd)
+
+test_fdisk_utils_SOURCES = libfdisk/src/utils.c
+test_fdisk_utils_CFLAGS = $(libfdisk_tests_cflags)
+test_fdisk_utils_LDFLAGS = $(libfdisk_tests_ldflags)
+test_fdisk_utils_LDADD = $(libfdisk_tests_ldadd)
+
+test_fdisk_script_SOURCES = libfdisk/src/script.c
+test_fdisk_script_CFLAGS = $(libfdisk_tests_cflags)
+test_fdisk_script_LDFLAGS = $(libfdisk_tests_ldflags)
+test_fdisk_script_LDADD = $(libfdisk_tests_ldadd)
+
+endif # BUILD_LIBFDISK_TESTS
+
+
+# move lib from $(usrlib_execdir) to $(libdir) if needed
+install-exec-hook-libfdisk:
+ if test "$(usrlib_execdir)" != "$(libdir)" -a -f "$(DESTDIR)$(usrlib_execdir)/libfdisk.so"; then \
+ mkdir -p $(DESTDIR)$(libdir); \
+ mv $(DESTDIR)$(usrlib_execdir)/libfdisk.so.* $(DESTDIR)$(libdir); \
+ so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/libfdisk.so); \
+ so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \
+ (cd $(DESTDIR)$(usrlib_execdir) && \
+ rm -f libfdisk.so && \
+ $(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name libfdisk.so); \
+ fi
+
+uninstall-hook-libfdisk:
+ rm -f $(DESTDIR)$(libdir)/libfdisk.so*
+
+INSTALL_EXEC_HOOKS += install-exec-hook-libfdisk
+UNINSTALL_HOOKS += uninstall-hook-libfdisk
+
diff --git a/libblkid/libfdisk/src/alignment.c b/libblkid/libfdisk/src/alignment.c
new file mode 100644
index 000000000..67f1ddd08
--- /dev/null
+++ b/libblkid/libfdisk/src/alignment.c
@@ -0,0 +1,654 @@
+
+#ifdef HAVE_LIBBLKID
+#include <blkid.h>
+#endif
+#include "blkdev.h"
+
+#include "fdiskP.h"
+
+/**
+ * SECTION: alignment
+ * @title: Alignment
+ * @short_description: functions to align partitions and work with disk topology and geometry
+ *
+ * The libfdisk aligns the end of the partitions to make it possible to align
+ * the next partition to the "grain" (see fdisk_get_grain()). The grain is
+ * usually 1MiB (or more for devices where optimal I/O is greater than 1MiB).
+ *
+ * It means that the library does not align strictly to physical sector size
+ * (or minimal or optimal I/O), but it uses greater granularity. It makes
+ * partition tables more portable. If you copy disk layout from 512-sector to
+ * 4K-sector device, all partitions are still aligned to physical sectors.
+ *
+ * This unified concept also makes partition tables more user friendly, all
+ * tables look same, LBA of the first partition is 2048 sectors everywhere, etc.
+ *
+ * It's recommended to not change any alignment or device properties. All is
+ * initialized by default by fdisk_assign_device().
+ *
+ * Note that terminology used by libfdisk is:
+ * - device properties: I/O limits (topology), geometry, sector size, ...
+ * - alignment: first, last LBA, grain, ...
+ *
+ * The alignment setting may be modified by disk label driver.
+ */
+
+/*
+ * Alignment according to logical granularity (usually 1MiB)
+ */
+static int lba_is_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+ unsigned long granularity = max(cxt->phy_sector_size, cxt->min_io_size);
+ uintmax_t offset;
+
+ if (cxt->grain > granularity)
+ granularity = cxt->grain;
+ offset = (lba * cxt->sector_size) & (granularity - 1);
+
+ return !((granularity + cxt->alignment_offset - offset) & (granularity - 1));
+}
+
+/*
+ * Alignment according to physical device topology (usually minimal i/o size)
+ */
+static int lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+ unsigned long granularity = max(cxt->phy_sector_size, cxt->min_io_size);
+ uintmax_t offset = (lba * cxt->sector_size) & (granularity - 1);
+
+ return !((granularity + cxt->alignment_offset - offset) & (granularity - 1));
+}
+
+/**
+ * fdisk_align_lba:
+ * @cxt: context
+ * @lba: address to align
+ * @direction: FDISK_ALIGN_{UP,DOWN,NEAREST}
+ *
+ * This function aligns @lba to the "grain" (see fdisk_get_grain()). If the
+ * device uses alignment offset then the result is moved according the offset
+ * to be on the physical boundary.
+ *
+ * Returns: alignment LBA.
+ */
+fdisk_sector_t fdisk_align_lba(struct fdisk_context *cxt, fdisk_sector_t lba, int direction)
+{
+ fdisk_sector_t res;
+
+ if (lba_is_aligned(cxt, lba))
+ res = lba;
+ else {
+ fdisk_sector_t sects_in_phy = cxt->grain / cxt->sector_size;
+
+ if (lba < cxt->first_lba)
+ res = cxt->first_lba;
+
+ else if (direction == FDISK_ALIGN_UP)
+ res = ((lba + sects_in_phy) / sects_in_phy) * sects_in_phy;
+
+ else if (direction == FDISK_ALIGN_DOWN)
+ res = (lba / sects_in_phy) * sects_in_phy;
+
+ else /* FDISK_ALIGN_NEAREST */
+ res = ((lba + sects_in_phy / 2) / sects_in_phy) * sects_in_phy;
+
+ if (cxt->alignment_offset && !lba_is_aligned(cxt, res) &&
+ res > cxt->alignment_offset / cxt->sector_size) {
+ /*
+ * apply alignment_offset
+ *
+ * On disk with alignment compensation physical blocks starts
+ * at LBA < 0 (usually LBA -1). It means we have to move LBA
+ * according the offset to be on the physical boundary.
+ */
+ /* fprintf(stderr, "LBA: %llu apply alignment_offset\n", res); */
+ res -= (max(cxt->phy_sector_size, cxt->min_io_size) -
+ cxt->alignment_offset) / cxt->sector_size;
+
+ if (direction == FDISK_ALIGN_UP && res < lba)
+ res += sects_in_phy;
+ }
+ }
+
+ if (lba != res)
+ DBG(CXT, ul_debugobj(cxt, "LBA %ju -aligned-to-> %ju",
+ (uintmax_t) lba,
+ (uintmax_t) res));
+ return res;
+}
+
+/**
+ * fdisk_align_lba_in_range:
+ * @cxt: context
+ * @lba: LBA
+ * @start: range start
+ * @stop: range stop
+ *
+ * Align @lba, the result has to be between @start and @stop
+ *
+ * Returns: aligned LBA
+ */
+fdisk_sector_t fdisk_align_lba_in_range(struct fdisk_context *cxt,
+ fdisk_sector_t lba, fdisk_sector_t start, fdisk_sector_t stop)
+{
+ fdisk_sector_t res;
+
+ start = fdisk_align_lba(cxt, start, FDISK_ALIGN_UP);
+ stop = fdisk_align_lba(cxt, stop, FDISK_ALIGN_DOWN);
+ lba = fdisk_align_lba(cxt, lba, FDISK_ALIGN_NEAREST);
+
+ if (lba < start)
+ res = start;
+ else if (lba > stop)
+ res = stop;
+ else
+ res = lba;
+
+ DBG(CXT, ul_debugobj(cxt, "LBA %ju range:<%ju..%ju>, result: %ju",
+ (uintmax_t) lba,
+ (uintmax_t) start,
+ (uintmax_t) stop,
+ (uintmax_t) res));
+ return res;
+}
+
+/**
+ * fdisk_lba_is_phy_aligned:
+ * @cxt: context
+ * @lba: LBA to check
+ *
+ * Check if the @lba is aligned to physical sector boundary.
+ *
+ * Returns: 1 if aligned.
+ */
+int fdisk_lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+ return lba_is_phy_aligned(cxt, lba);
+}
+
+static unsigned long get_sector_size(int fd)
+{
+ int sect_sz;
+
+ if (!blkdev_get_sector_size(fd, &sect_sz))
+ return (unsigned long) sect_sz;
+ return DEFAULT_SECTOR_SIZE;
+}
+
+static void recount_geometry(struct fdisk_context *cxt)
+{
+ if (!cxt->geom.heads)
+ cxt->geom.heads = 255;
+ if (!cxt->geom.sectors)
+ cxt->geom.sectors = 63;
+
+ cxt->geom.cylinders = cxt->total_sectors /
+ (cxt->geom.heads * cxt->geom.sectors);
+}
+
+/**
+ * fdisk_override_geometry:
+ * @cxt: fdisk context
+ * @cylinders: user specified cylinders
+ * @heads: user specified heads
+ * @sectors: user specified sectors
+ *
+ * Overrides auto-discovery. The function fdisk_reset_device_properties()
+ * restores the original setting.
+ *
+ * The difference between fdisk_override_geometry() and fdisk_save_user_geometry()
+ * is that saved user geometry is persistent setting and it's applied always
+ * when device is assigned to the context or device properties are reseted.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_override_geometry(struct fdisk_context *cxt,
+ unsigned int cylinders,
+ unsigned int heads,
+ unsigned int sectors)
+{
+ if (!cxt)
+ return -EINVAL;
+ if (heads)
+ cxt->geom.heads = heads;
+ if (sectors)
+ cxt->geom.sectors = sectors;
+
+ if (cylinders)
+ cxt->geom.cylinders = cylinders;
+ else
+ recount_geometry(cxt);
+
+ fdisk_reset_alignment(cxt);
+
+ DBG(CXT, ul_debugobj(cxt, "override C/H/S: %u/%u/%u",
+ (unsigned) cxt->geom.cylinders,
+ (unsigned) cxt->geom.heads,
+ (unsigned) cxt->geom.sectors));
+
+ return 0;
+}
+
+/**
+ * fdisk_save_user_geometry:
+ * @cxt: context
+ * @cylinders: C
+ * @heads: H
+ * @sectors: S
+ *
+ * Save user defined geometry to use it for partitioning.
+ *
+ * The user properties are applied by fdisk_assign_device() or
+ * fdisk_reset_device_properties().
+
+ * Returns: <0 on error, 0 on success.
+ */
+int fdisk_save_user_geometry(struct fdisk_context *cxt,
+ unsigned int cylinders,
+ unsigned int heads,
+ unsigned int sectors)
+{
+ if (!cxt)
+ return -EINVAL;
+
+ if (heads)
+ cxt->user_geom.heads = heads > 256 ? 0 : heads;
+ if (sectors)
+ cxt->user_geom.sectors = sectors >= 64 ? 0 : sectors;
+ if (cylinders)
+ cxt->user_geom.cylinders = cylinders;
+
+ DBG(CXT, ul_debugobj(cxt, "user C/H/S: %u/%u/%u",
+ (unsigned) cxt->user_geom.cylinders,
+ (unsigned) cxt->user_geom.heads,
+ (unsigned) cxt->user_geom.sectors));
+
+ return 0;
+}
+
+/**
+ * fdisk_save_user_sector_size:
+ * @cxt: context
+ * @phy: physical sector size
+ * @log: logicla sector size
+ *
+ * Save user defined sector sizes to use it for partitioning.
+ *
+ * The user properties are applied by fdisk_assign_device() or
+ * fdisk_reset_device_properties().
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int fdisk_save_user_sector_size(struct fdisk_context *cxt,
+ unsigned int phy,
+ unsigned int log)
+{
+ if (!cxt)
+ return -EINVAL;
+
+ DBG(CXT, ul_debugobj(cxt, "user phy/log sector size: %u/%u", phy, log));
+
+ cxt->user_pyh_sector = phy;
+ cxt->user_log_sector = log;
+
+ return 0;
+}
+
+/**
+ * fdisk_has_user_device_properties:
+ * @cxt: context
+ *
+ * Returns: 1 if user specified any properties
+ */
+int fdisk_has_user_device_properties(struct fdisk_context *cxt)
+{
+ return (cxt->user_pyh_sector
+ || cxt->user_log_sector
+ || cxt->user_geom.heads
+ || cxt->user_geom.sectors
+ || cxt->user_geom.cylinders);
+}
+
+int fdisk_apply_user_device_properties(struct fdisk_context *cxt)
+{
+ if (!cxt)
+ return -EINVAL;
+
+ DBG(CXT, ul_debugobj(cxt, "appling user device properties"));
+
+ if (cxt->user_pyh_sector)
+ cxt->phy_sector_size = cxt->user_pyh_sector;
+ if (cxt->user_log_sector)
+ cxt->sector_size = cxt->min_io_size =
+ cxt->io_size = cxt->user_log_sector;
+
+ if (cxt->user_geom.heads)
+ cxt->geom.heads = cxt->user_geom.heads;
+ if (cxt->user_geom.sectors)
+ cxt->geom.sectors = cxt->user_geom.sectors;
+
+ if (cxt->user_geom.cylinders)
+ cxt->geom.cylinders = cxt->user_geom.cylinders;
+ else if (cxt->user_geom.heads || cxt->user_geom.sectors)
+ recount_geometry(cxt);
+
+ fdisk_reset_alignment(cxt);
+ if (cxt->firstsector_bufsz != cxt->sector_size)
+ fdisk_read_firstsector(cxt);
+
+ DBG(CXT, ul_debugobj(cxt, "new C/H/S: %u/%u/%u",
+ (unsigned) cxt->geom.cylinders,
+ (unsigned) cxt->geom.heads,
+ (unsigned) cxt->geom.sectors));
+ DBG(CXT, ul_debugobj(cxt, "new log/phy sector size: %u/%u",
+ (unsigned) cxt->sector_size,
+ (unsigned) cxt->phy_sector_size));
+
+ return 0;
+}
+
+void fdisk_zeroize_device_properties(struct fdisk_context *cxt)
+{
+ assert(cxt);
+
+ cxt->io_size = 0;
+ cxt->optimal_io_size = 0;
+ cxt->min_io_size = 0;
+ cxt->phy_sector_size = 0;
+ cxt->sector_size = 0;
+ cxt->alignment_offset = 0;
+ cxt->grain = 0;
+ cxt->first_lba = 0;
+ cxt->last_lba = 0;
+ cxt->total_sectors = 0;
+
+ memset(&cxt->geom, 0, sizeof(struct fdisk_geometry));
+}
+
+/**
+ * fdisk_reset_device_properties:
+ * @cxt: context
+ *
+ * Resets and discovery topology (I/O limits), geometry, re-read the first
+ * rector on the device if necessary and apply user device setting (geometry
+ * and sector size), then initialize alignment according to label driver (see
+ * fdisk_reset_alignment()).
+ *
+ * You don't have to use this function by default, fdisk_assign_device() is
+ * smart enough to initialize all necessary setting.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_reset_device_properties(struct fdisk_context *cxt)
+{
+ int rc;
+
+ if (!cxt)
+ return -EINVAL;
+
+ DBG(CXT, ul_debugobj(cxt, "*** reseting device properties"));
+
+ fdisk_zeroize_device_properties(cxt);
+ fdisk_discover_topology(cxt);
+ fdisk_discover_geometry(cxt);
+
+ rc = fdisk_read_firstsector(cxt);
+ if (rc)
+ return rc;
+
+ fdisk_apply_user_device_properties(cxt);
+ return 0;
+}
+
+/*
+ * Generic (label independent) geometry
+ */
+int fdisk_discover_geometry(struct fdisk_context *cxt)
+{
+ fdisk_sector_t nsects;
+
+ assert(cxt);
+ assert(cxt->geom.heads == 0);
+
+ DBG(CXT, ul_debugobj(cxt, "%s: discovering geometry...", cxt->dev_path));
+
+ /* get number of 512-byte sectors, and convert it the real sectors */
+ if (!blkdev_get_sectors(cxt->dev_fd, (unsigned long long *) &nsects))
+ cxt->total_sectors = (nsects / (cxt->sector_size >> 9));
+
+ DBG(CXT, ul_debugobj(cxt, "total sectors: %ju (ioctl=%ju)",
+ (uintmax_t) cxt->total_sectors,
+ (uintmax_t) nsects));
+
+ /* what the kernel/bios thinks the geometry is */
+ blkdev_get_geometry(cxt->dev_fd, &cxt->geom.heads, (unsigned int *) &cxt->geom.sectors);
+
+ /* obtained heads and sectors */
+ recount_geometry(cxt);
+
+ DBG(CXT, ul_debugobj(cxt, "result: C/H/S: %u/%u/%u",
+ (unsigned) cxt->geom.cylinders,
+ (unsigned) cxt->geom.heads,
+ (unsigned) cxt->geom.sectors));
+ return 0;
+}
+
+int fdisk_discover_topology(struct fdisk_context *cxt)
+{
+#ifdef HAVE_LIBBLKID
+ blkid_probe pr;
+#endif
+ assert(cxt);
+ assert(cxt->sector_size == 0);
+
+ DBG(CXT, ul_debugobj(cxt, "%s: discovering topology...", cxt->dev_path));
+#ifdef HAVE_LIBBLKID
+ DBG(CXT, ul_debugobj(cxt, "initialize libblkid prober"));
+
+ pr = blkid_new_probe();
+ if (pr && blkid_probe_set_device(pr, cxt->dev_fd, 0, 0) == 0) {
+ blkid_topology tp = blkid_probe_get_topology(pr);
+
+ if (tp) {
+ cxt->min_io_size = blkid_topology_get_minimum_io_size(tp);
+ cxt->optimal_io_size = blkid_topology_get_optimal_io_size(tp);
+ cxt->phy_sector_size = blkid_topology_get_physical_sector_size(tp);
+ cxt->alignment_offset = blkid_topology_get_alignment_offset(tp);
+
+ /* I/O size used by fdisk */
+ cxt->io_size = cxt->optimal_io_size;
+ if (!cxt->io_size)
+ /* optimal IO is optional, default to minimum IO */
+ cxt->io_size = cxt->min_io_size;
+ }
+ }
+ blkid_free_probe(pr);
+#endif
+
+ cxt->sector_size = get_sector_size(cxt->dev_fd);
+ if (!cxt->phy_sector_size) /* could not discover physical size */
+ cxt->phy_sector_size = cxt->sector_size;
+
+ /* no blkid or error, use default values */
+ if (!cxt->min_io_size)
+ cxt->min_io_size = cxt->sector_size;
+ if (!cxt->io_size)
+ cxt->io_size = cxt->sector_size;
+
+ DBG(CXT, ul_debugobj(cxt, "result: log/phy sector size: %ld/%ld",
+ cxt->sector_size, cxt->phy_sector_size));
+ DBG(CXT, ul_debugobj(cxt, "result: fdisk/min/optimal io: %ld/%ld/%ld",
+ cxt->io_size, cxt->optimal_io_size, cxt->min_io_size));
+ return 0;
+}
+
+static int has_topology(struct fdisk_context *cxt)
+{
+ /*
+ * Assume that the device provides topology info if
+ * optimal_io_size is set or alignment_offset is set or
+ * minimum_io_size is not power of 2.
+ */
+ if (cxt &&
+ (cxt->optimal_io_size ||
+ cxt->alignment_offset ||
+ !is_power_of_2(cxt->min_io_size)))
+ return 1;
+ return 0;
+}
+
+/*
+ * The LBA of the first partition is based on the device geometry and topology.
+ * This offset is generic (and recommended) for all labels.
+ *
+ * Returns: 0 on error or number of logical sectors.
+ */
+static fdisk_sector_t topology_get_first_lba(struct fdisk_context *cxt)
+{
+ fdisk_sector_t x = 0, res;
+
+ if (!cxt)
+ return 0;
+
+ if (!cxt->io_size)
+ fdisk_discover_topology(cxt);
+
+ /*
+ * Align the begin of partitions to:
+ *
+ * a) topology
+ * a2) alignment offset
+ * a1) or physical sector (minimal_io_size, aka "grain")
+ *
+ * b) or default to 1MiB (2048 sectrors, Windows Vista default)
+ *
+ * c) or for very small devices use 1 phy.sector
+ */
+ if (has_topology(cxt)) {
+ if (cxt->alignment_offset)
+ x = cxt->alignment_offset;
+ else if (cxt->io_size > 2048 * 512)
+ x = cxt->io_size;
+ }
+ /* default to 1MiB */
+ if (!x)
+ x = 2048 * 512;
+
+ res = x / cxt->sector_size;
+
+ /* don't use huge offset on small devices */
+ if (cxt->total_sectors <= res * 4)
+ res = cxt->phy_sector_size / cxt->sector_size;
+
+ return res;
+}
+
+static unsigned long topology_get_grain(struct fdisk_context *cxt)
+{
+ unsigned long res;
+
+ if (!cxt)
+ return 0;
+
+ if (!cxt->io_size)
+ fdisk_discover_topology(cxt);
+
+ res = cxt->io_size;
+
+ /* use 1MiB grain always when possible */
+ if (res < 2048 * 512)
+ res = 2048 * 512;
+
+ /* don't use huge grain on small devices */
+ if (cxt->total_sectors <= (res * 4 / cxt->sector_size))
+ res = cxt->phy_sector_size;
+
+ return res;
+}
+
+/**
+ * fdisk_reset_alignment:
+ * @cxt: fdisk context
+ *
+ * Resets alignment setting to the default and label specific values. This
+ * function does not change device properties (I/O limits, geometry etc.).
+ *
+ * Returns: 0 on success, < 0 in case of error.
+ */
+int fdisk_reset_alignment(struct fdisk_context *cxt)
+{
+ int rc = 0;
+
+ if (!cxt)
+ return -EINVAL;
+
+ DBG(CXT, ul_debugobj(cxt, "reseting alignment..."));
+
+ /* default */
+ cxt->grain = topology_get_grain(cxt);
+ cxt->first_lba = topology_get_first_lba(cxt);
+ cxt->last_lba = cxt->total_sectors - 1;
+
+ /* overwrite default by label stuff */
+ if (cxt->label && cxt->label->op->reset_alignment)
+ rc = cxt->label->op->reset_alignment(cxt);
+
+ DBG(CXT, ul_debugobj(cxt, "alignment reseted to: "
+ "first LBA=%ju, last LBA=%ju, grain=%lu [rc=%d]",
+ (uintmax_t) cxt->first_lba, (uintmax_t) cxt->last_lba,
+ cxt->grain, rc));
+ return rc;
+}
+
+
+fdisk_sector_t fdisk_scround(struct fdisk_context *cxt, fdisk_sector_t num)
+{
+ fdisk_sector_t un = fdisk_get_units_per_sector(cxt);
+ return (num + un - 1) / un;
+}
+
+fdisk_sector_t fdisk_cround(struct fdisk_context *cxt, fdisk_sector_t num)
+{
+ return fdisk_use_cylinders(cxt) ?
+ (num / fdisk_get_units_per_sector(cxt)) + 1 : num;
+}
+
+/**
+ * fdisk_reread_partition_table:
+ * @cxt: context
+ *
+ * Force *kernel* to re-read partition table on block devices.
+ *
+ * Returns: 0 on success, < 0 in case of error.
+ */
+int fdisk_reread_partition_table(struct fdisk_context *cxt)
+{
+ int i;
+ struct stat statbuf;
+
+ assert(cxt);
+ assert(cxt->dev_fd >= 0);
+
+ i = fstat(cxt->dev_fd, &statbuf);
+ if (i == 0 && S_ISBLK(statbuf.st_mode)) {
+ sync();
+#ifdef BLKRRPART
+ fdisk_info(cxt, _("Calling ioctl() to re-read partition table."));
+ i = ioctl(cxt->dev_fd, BLKRRPART);
+#else
+ errno = ENOSYS;
+ i = 1;
+#endif
+ }
+
+ if (i) {
+ fdisk_warn(cxt, _("Re-reading the partition table failed."));
+ fdisk_info(cxt, _(
+ "The kernel still uses the old table. The "
+ "new table will be used at the next reboot "
+ "or after you run partprobe(8) or kpartx(8)."));
+ return -errno;
+ }
+
+ return 0;
+}
diff --git a/libblkid/libfdisk/src/ask.c b/libblkid/libfdisk/src/ask.c
new file mode 100644
index 000000000..7e0c3c218
--- /dev/null
+++ b/libblkid/libfdisk/src/ask.c
@@ -0,0 +1,1044 @@
+
+#include "strutils.h"
+#include "fdiskP.h"
+
+/**
+ * SECTION: ask
+ * @title: Ask
+ * @short_description: interface for dialog driven partitioning, warning and info messages
+ *
+ */
+
+static void fdisk_ask_menu_reset_items(struct fdisk_ask *ask);
+
+
+/**
+ * fdisk_set_ask:
+ * @cxt: context
+ * @ask_cb: callback
+ * @data: callback data
+ *
+ * Set callback for dialog driven partitioning and library warnings/errors.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_set_ask(struct fdisk_context *cxt,
+ int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *),
+ void *data)
+{
+ assert(cxt);
+
+ cxt->ask_cb = ask_cb;
+ cxt->ask_data = data;
+ return 0;
+}
+
+struct fdisk_ask *fdisk_new_ask(void)
+{
+ struct fdisk_ask *ask = calloc(1, sizeof(struct fdisk_ask));
+ DBG(ASK, ul_debugobj(ask, "alloc"));
+ ask->refcount = 1;
+ return ask;
+}
+
+void fdisk_reset_ask(struct fdisk_ask *ask)
+{
+ int refcount;
+
+ assert(ask);
+ free(ask->query);
+
+ DBG(ASK, ul_debugobj(ask, "reset"));
+ refcount = ask->refcount;
+
+ if (fdisk_is_ask(ask, MENU))
+ fdisk_ask_menu_reset_items(ask);
+
+ memset(ask, 0, sizeof(*ask));
+ ask->refcount = refcount;
+}
+
+/**
+ * fdisk_ref_ask:
+ * @ask: ask instance
+ *
+ * Incremparts reference counter.
+ */
+void fdisk_ref_ask(struct fdisk_ask *ask)
+{
+ if (ask)
+ ask->refcount++;
+}
+
+
+/**
+ * fdisk_unref_ask:
+ * @ask: ask instance
+ *
+ * De-incremparts reference counter, on zero the @ask is automatically
+ * deallocated.
+ */
+void fdisk_unref_ask(struct fdisk_ask *ask)
+{
+ if (!ask)
+ return;
+ ask->refcount--;
+
+ if (ask->refcount <= 0) {
+ fdisk_reset_ask(ask);
+ DBG(ASK, ul_debugobj(ask, "free"));
+ free(ask);
+ }
+}
+
+/**
+ * fdisk_ask_get_query:
+ * @ask: ask instance
+ *
+ * Returns: pointer to dialog string.
+ */
+const char *fdisk_ask_get_query(struct fdisk_ask *ask)
+{
+ assert(ask);
+ return ask->query;
+}
+
+int fdisk_ask_set_query(struct fdisk_ask *ask, const char *str)
+{
+ assert(ask);
+ return !strdup_to_struct_member(ask, query, str) ? -ENOMEM : 0;
+}
+
+/**
+ * fdisk_ask_get_type:
+ * @ask: ask instance
+ *
+ * Returns: FDISK_ASKTYPE_*
+ */
+int fdisk_ask_get_type(struct fdisk_ask *ask)
+{
+ assert(ask);
+ return ask->type;
+}
+
+int fdisk_ask_set_type(struct fdisk_ask *ask, int type)
+{
+ assert(ask);
+ ask->type = type;
+ return 0;
+}
+
+int fdisk_do_ask(struct fdisk_context *cxt, struct fdisk_ask *ask)
+{
+ int rc;
+
+ assert(ask);
+ assert(cxt);
+
+ DBG(ASK, ul_debugobj(ask, "do_ask for '%s'",
+ ask->query ? ask->query :
+ ask->type == FDISK_ASKTYPE_INFO ? "info" :
+ ask->type == FDISK_ASKTYPE_WARNX ? "warnx" :
+ ask->type == FDISK_ASKTYPE_WARN ? "warn" :
+ "?nothing?"));
+
+ if (!cxt->ask_cb) {
+ DBG(ASK, ul_debugobj(ask, "no ask callback specified!"));
+ return -EINVAL;
+ }
+
+ rc = cxt->ask_cb(cxt, ask, cxt->ask_data);
+
+ DBG(ASK, ul_debugobj(ask, "do_ask done [rc=%d]", rc));
+ return rc;
+}
+
+#define is_number_ask(a) (fdisk_is_ask(a, NUMBER) || fdisk_is_ask(a, OFFSET))
+
+/**
+ * fdisk_ask_number_get_range:
+ * @ask: ask instance
+ *
+ * Returns: string with range (e.g. "1,3,5-10")
+ */
+const char *fdisk_ask_number_get_range(struct fdisk_ask *ask)
+{
+ assert(ask);
+ assert(is_number_ask(ask));
+ return ask->data.num.range;
+}
+
+int fdisk_ask_number_set_range(struct fdisk_ask *ask, const char *range)
+{
+ assert(ask);
+ assert(is_number_ask(ask));
+ ask->data.num.range = range;
+ return 0;
+}
+
+/**
+ * fdisk_ask_number_get_default:
+ * @ask: ask instance
+ *
+ * Returns: default number
+ *
+ */
+uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask)
+{
+ assert(ask);
+ assert(is_number_ask(ask));
+ return ask->data.num.dfl;
+}
+
+int fdisk_ask_number_set_default(struct fdisk_ask *ask, uint64_t dflt)
+{
+ assert(ask);
+ ask->data.num.dfl = dflt;
+ return 0;
+}
+
+/**
+ * fdisk_ask_number_get_low:
+ * @ask: ask instance
+ *
+ * Returns: minimal possible number when ask for numbers in range
+ */
+uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask)
+{
+ assert(ask);
+ assert(is_number_ask(ask));
+ return ask->data.num.low;
+}
+
+int fdisk_ask_number_set_low(struct fdisk_ask *ask, uint64_t low)
+{
+ assert(ask);
+ ask->data.num.low = low;
+ return 0;
+}
+
+/**
+ * fdisk_ask_number_get_high:
+ * @ask: ask instance
+ *
+ * Returns: maximal possible number when ask for numbers in range
+ */
+uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask)
+{
+ assert(ask);
+ assert(is_number_ask(ask));
+ return ask->data.num.hig;
+}
+
+int fdisk_ask_number_set_high(struct fdisk_ask *ask, uint64_t high)
+{
+ assert(ask);
+ ask->data.num.hig = high;
+ return 0;
+}
+
+/**
+ * fdisk_ask_number_get_result:
+ * @ask: ask instance
+ *
+ * Returns: result
+ */
+uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask)
+{
+ assert(ask);
+ assert(is_number_ask(ask));
+ return ask->data.num.result;
+}
+
+/**
+ * fdisk_ask_number_set_result:
+ * @ask: ask instance
+ * @result: dialog result
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result)
+{
+ assert(ask);
+ ask->data.num.result = result;
+ return 0;
+}
+
+/**
+ * fdisk_ask_number_get_base:
+ * @ask: ask instance
+ *
+ * Returns: base when user specify number in relative notation (+size)
+ */
+uint64_t fdisk_ask_number_get_base(struct fdisk_ask *ask)
+{
+ assert(ask);
+ assert(is_number_ask(ask));
+ return ask->data.num.base;
+}
+
+int fdisk_ask_number_set_base(struct fdisk_ask *ask, uint64_t base)
+{
+ assert(ask);
+ ask->data.num.base = base;
+ return 0;
+}
+
+/**
+ * fdisk_ask_number_get_unit:
+ * @ask: ask instance
+ *
+ * Returns: number of bytes per the unit
+ */
+uint64_t fdisk_ask_number_get_unit(struct fdisk_ask *ask)
+{
+ assert(ask);
+ assert(is_number_ask(ask));
+ return ask->data.num.unit;
+}
+
+int fdisk_ask_number_set_unit(struct fdisk_ask *ask, uint64_t unit)
+{
+ assert(ask);
+ ask->data.num.unit = unit;
+ return 0;
+}
+
+int fdisk_ask_number_is_relative(struct fdisk_ask *ask)
+{
+ assert(ask);
+ assert(is_number_ask(ask));
+ return ask->data.num.relative;
+}
+
+/**
+ * fdisk_ask_number_set_relative
+ * @ask: ask instance
+ * @relative: 0 or 1
+ *
+ * Inform libfdisk that user specified number in relative notation rather than
+ * by explicit number. This info allows to fdisk do some optimization (e.g.
+ * align end of partiton, etc.)
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative)
+{
+ assert(ask);
+ ask->data.num.relative = relative ? 1 : 0;
+ return 0;
+}
+
+/**
+ * fdisk_ask_number_inchars:
+ * @ask: ask instance
+ *
+ * For example for BSD is normal to address partition by chars rather than by
+ * number (first partition is 'a').
+ *
+ * Returns: 1 if number should be presented as chars
+ *
+ */
+int fdisk_ask_number_inchars(struct fdisk_ask *ask)
+{
+ assert(ask);
+ assert(is_number_ask(ask));
+ return ask->data.num.inchars;
+}
+
+/*
+ * Generates string with list ranges (e.g. 1,2,5-8) for the 'cur'
+ */
+#define tochar(num) ((int) ('a' + num - 1))
+static char *mk_string_list(char *ptr, size_t *len, size_t *begin,
+ size_t *run, ssize_t cur, int inchar)
+{
+ int rlen;
+
+ if (cur != -1) {
+ if (!*begin) { /* begin of the list */
+ *begin = cur + 1;
+ return ptr;
+ }
+
+ if (*begin + *run == cur) { /* no gap, continue */
+ (*run)++;
+ return ptr;
+ }
+ } else if (!*begin) {
+ *ptr = '\0';
+ return ptr; /* end of empty list */
+ }
+
+ /* add to the list */
+ if (!*run)
+ rlen = inchar ? snprintf(ptr, *len, "%c,", tochar(*begin)) :
+ snprintf(ptr, *len, "%zu,", *begin);
+ else if (*run == 1)
+ rlen = inchar ?
+ snprintf(ptr, *len, "%c,%c,", tochar(*begin), tochar(*begin + 1)) :
+ snprintf(ptr, *len, "%zu,%zu,", *begin, *begin + 1);
+ else
+ rlen = inchar ?
+ snprintf(ptr, *len, "%c-%c,", tochar(*begin), tochar(*begin + *run)) :
+ snprintf(ptr, *len, "%zu-%zu,", *begin, *begin + *run);
+
+ if (rlen < 0 || (size_t) rlen + 1 > *len)
+ return NULL;
+
+ ptr += rlen;
+
+ if (rlen > 0 && *len > (size_t) rlen)
+ *len -= rlen;
+ else
+ *len = 0;
+
+ if (cur == -1 && *begin) {
+ /* end of the list */
+ *(ptr - 1) = '\0'; /* remove tailing ',' from the list */
+ return ptr;
+ }
+
+ *begin = cur + 1;
+ *run = 0;
+
+ return ptr;
+}
+
+/**
+ * fdisk_ask_partnum:
+ * @cxt: context
+ * @partnum: returns partition number
+ * @wantnew: 0|1
+ *
+ * High-level API to ask for used or unused partition number.
+ *
+ * Returns: 0 on success, < 0 on error, 1 if no free/used partition
+ */
+int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew)
+{
+ int rc = 0, inchar = 0;
+ char range[BUFSIZ], *ptr = range;
+ size_t i, len = sizeof(range), begin = 0, run = 0;
+ struct fdisk_ask *ask = NULL;
+ __typeof__(ask->data.num) *num;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(partnum);
+
+ if (cxt->label && cxt->label->flags & FDISK_LABEL_FL_INCHARS_PARTNO)
+ inchar = 1;
+
+ DBG(ASK, ul_debug("%s: asking for %s partition number "
+ "(max: %zu, inchar: %s)",
+ cxt->label->name,
+ wantnew ? "new" : "used",
+ cxt->label->nparts_max,
+ inchar ? "yes" : "not"));
+
+ ask = fdisk_new_ask();
+ if (!ask)
+ return -ENOMEM;
+
+ fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+ num = &ask->data.num;
+
+ ask->data.num.inchars = inchar ? 1 : 0;
+
+ for (i = 0; i < cxt->label->nparts_max; i++) {
+ int used = fdisk_is_partition_used(cxt, i);
+
+ if (wantnew && !used) {
+ ptr = mk_string_list(ptr, &len, &begin, &run, i, inchar);
+ if (!ptr) {
+ rc = -EINVAL;
+ break;
+ }
+ if (!num->low)
+ num->dfl = num->low = i + 1;
+ num->hig = i + 1;
+ } else if (!wantnew && used) {
+ ptr = mk_string_list(ptr, &len, &begin, &run, i, inchar);
+ if (!num->low)
+ num->low = i + 1;
+ num->dfl = num->hig = i + 1;
+ }
+ }
+
+ DBG(ASK, ul_debugobj(ask, "ask limits: low: %ju, high: %ju, default: %ju",
+ num->low, num->hig, num->dfl));
+
+ if (!rc && !wantnew && num->low == num->hig) {
+ if (num->low > 0) {
+ /* only one existing partiton, don't ask, return the number */
+ fdisk_ask_number_set_result(ask, num->low);
+ fdisk_info(cxt, _("Selected partition %ju"), num->low);
+
+ } else if (num->low == 0) {
+ fdisk_warnx(cxt, _("No partition is defined yet!"));
+ rc = 1;
+ }
+ goto dont_ask;
+ }
+ if (!rc && wantnew && num->low == num->hig) {
+ if (num->low > 0) {
+ /* only one free partition, don't ask, return the number */
+ fdisk_ask_number_set_result(ask, num->low);
+ fdisk_info(cxt, _("Selected partition %ju"), num->low);
+ }
+ if (num->low == 0) {
+ fdisk_warnx(cxt, _("No free partition available!"));
+ rc = 1;
+ }
+ goto dont_ask;
+ }
+ if (!rc) {
+ mk_string_list(ptr, &len, &begin, &run, -1, inchar); /* terminate the list */
+ rc = fdisk_ask_number_set_range(ask, range);
+ }
+ if (!rc)
+ rc = fdisk_ask_set_query(ask, _("Partition number"));
+ if (!rc)
+ rc = fdisk_do_ask(cxt, ask);
+
+dont_ask:
+ if (!rc) {
+ *partnum = fdisk_ask_number_get_result(ask);
+ if (*partnum)
+ *partnum -= 1;
+ }
+ DBG(ASK, ul_debugobj(ask, "result: %ju [rc=%d]\n", fdisk_ask_number_get_result(ask), rc));
+ fdisk_unref_ask(ask);
+ return rc;
+}
+
+/**
+ * fdisk_ask_number:
+ * @cxt: context
+ * @low: minimal possible number
+ * @dflt: default suggestion
+ * @high: maximal possible number
+ * @query: question string
+ * @result: returns result
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_ask_number(struct fdisk_context *cxt,
+ uintmax_t low,
+ uintmax_t dflt,
+ uintmax_t high,
+ const char *query,
+ uintmax_t *result)
+{
+ struct fdisk_ask *ask;
+ int rc;
+
+ assert(cxt);
+
+ ask = fdisk_new_ask();
+ if (!ask)
+ return -ENOMEM;
+
+ rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+ if (!rc)
+ fdisk_ask_number_set_low(ask, low);
+ if (!rc)
+ fdisk_ask_number_set_default(ask, dflt);
+ if (!rc)
+ fdisk_ask_number_set_high(ask, high);
+ if (!rc)
+ fdisk_ask_set_query(ask, query);
+ if (!rc)
+ rc = fdisk_do_ask(cxt, ask);
+ if (!rc)
+ *result = fdisk_ask_number_get_result(ask);
+
+ DBG(ASK, ul_debugobj(ask, "result: %ju [rc=%d]\n", *result, rc));
+ fdisk_unref_ask(ask);
+ return rc;
+}
+
+/**
+ * fdisk_ask_string_get_result:
+ * @ask: ask instance
+ *
+ * Returns: pointer to dialog result
+ */
+char *fdisk_ask_string_get_result(struct fdisk_ask *ask)
+{
+ assert(ask);
+ assert(fdisk_is_ask(ask, STRING));
+ return ask->data.str.result;
+}
+
+/**
+ * fdisk_ask_string_set_result:
+ * @ask: ask instance
+ * @result: pointer to allocated buffer with string
+ *
+ * You don't have to care about the @result deallocation, libfdisk is going to
+ * deallocate the result when destroy @ask instance.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_string_set_result(struct fdisk_ask *ask, char *result)
+{
+ assert(ask);
+ ask->data.str.result = result;
+ return 0;
+}
+
+/**
+ * fdisk_ask_string:
+ * @cxt: context:
+ * @query: question string
+ * @result: returns allocated buffer
+ *
+ * High-level API to ask for strings. Don't forget to deallocate the @result.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_ask_string(struct fdisk_context *cxt,
+ const char *query,
+ char **result)
+{
+ struct fdisk_ask *ask;
+ int rc;
+
+ assert(cxt);
+
+ ask = fdisk_new_ask();
+ if (!ask)
+ return -ENOMEM;
+
+ rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_STRING);
+ if (!rc)
+ fdisk_ask_set_query(ask, query);
+ if (!rc)
+ rc = fdisk_do_ask(cxt, ask);
+ if (!rc)
+ *result = fdisk_ask_string_get_result(ask);
+
+ DBG(ASK, ul_debugobj(ask, "result: %s [rc=%d]\n", *result, rc));
+ fdisk_unref_ask(ask);
+ return rc;
+}
+
+/**
+ * fdisk_ask_yesno:
+ * @cxt: context
+ * @query: question string
+ * @result: returns 0 (no) or 1 (yes)
+ *
+ * Hight-level API to ask Yes/No questions
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_yesno(struct fdisk_context *cxt,
+ const char *query,
+ int *result)
+{
+ struct fdisk_ask *ask;
+ int rc;
+
+ assert(cxt);
+
+ ask = fdisk_new_ask();
+ if (!ask)
+ return -ENOMEM;
+
+ rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_YESNO);
+ if (!rc)
+ fdisk_ask_set_query(ask, query);
+ if (!rc)
+ rc = fdisk_do_ask(cxt, ask);
+ if (!rc)
+ *result = fdisk_ask_yesno_get_result(ask) == 1 ? 1 : 0;
+
+ DBG(ASK, ul_debugobj(ask, "result: %d [rc=%d]\n", *result, rc));
+ fdisk_unref_ask(ask);
+ return rc;
+}
+
+/**
+ * fdisk_ask_yesno_get_result:
+ * @ask: ask instance
+ *
+ * Returns: 0 or 1
+ */
+int fdisk_ask_yesno_get_result(struct fdisk_ask *ask)
+{
+ assert(ask);
+ assert(fdisk_is_ask(ask, YESNO));
+ return ask->data.yesno.result;
+}
+
+/**
+ * fdisk_ask_yesno_set_result:
+ * @ask: ask instance
+ * @result: 1 or 0
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, int result)
+{
+ assert(ask);
+ ask->data.yesno.result = result;
+ return 0;
+}
+
+/*
+ * menu
+ */
+int fdisk_ask_menu_set_default(struct fdisk_ask *ask, int dfl)
+{
+ assert(ask);
+ assert(fdisk_is_ask(ask, MENU));
+ ask->data.menu.dfl = dfl;
+ return 0;
+}
+
+/**
+ * fdisk_ask_menu_get_default:
+ * @ask: ask instance
+ *
+ * Returns: default menu item key
+ */
+int fdisk_ask_menu_get_default(struct fdisk_ask *ask)
+{
+ assert(ask);
+ assert(fdisk_is_ask(ask, MENU));
+ return ask->data.menu.dfl;
+}
+
+/**
+ * fdisk_ask_menu_set_result:
+ * @ask: ask instance
+ * @key: result
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_ask_menu_set_result(struct fdisk_ask *ask, int key)
+{
+ assert(ask);
+ assert(fdisk_is_ask(ask, MENU));
+ ask->data.menu.result = key;
+ DBG(ASK, ul_debugobj(ask, "menu result: %c\n", key));
+ return 0;
+
+}
+
+/**
+ * fdisk_ask_menu_get_result:
+ * @ask: ask instance
+ * @key: returns selected menu item key
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_ask_menu_get_result(struct fdisk_ask *ask, int *key)
+{
+ assert(ask);
+ assert(fdisk_is_ask(ask, MENU));
+ if (key)
+ *key = ask->data.menu.result;
+ return 0;
+}
+
+/**
+ * fdisk_ask_menu_get_item:
+ * @ask: ask menu instance
+ * @idx: wanted menu item index
+ * @key: returns key of the menu item
+ * @name: returns name of the menu item
+ * @desc: returns description of the menu item
+ *
+ * Returns: 0 on success, <0 on error, >0 if idx out-of-range
+ */
+int fdisk_ask_menu_get_item(struct fdisk_ask *ask, size_t idx, int *key,
+ const char **name, const char **desc)
+{
+ size_t i;
+ struct ask_menuitem *mi;
+
+ assert(ask);
+ assert(fdisk_is_ask(ask, MENU));
+
+ for (i = 0, mi = ask->data.menu.first; mi; mi = mi->next, i++) {
+ if (i == idx)
+ break;
+ }
+
+ if (!mi)
+ return 1; /* no more items */
+ if (key)
+ *key = mi->key;
+ if (name)
+ *name = mi->name;
+ if (desc)
+ *desc = mi->desc;
+ return 0;
+}
+
+static void fdisk_ask_menu_reset_items(struct fdisk_ask *ask)
+{
+ struct ask_menuitem *mi;
+
+ assert(ask);
+ assert(fdisk_is_ask(ask, MENU));
+
+ for (mi = ask->data.menu.first; mi; ) {
+ struct ask_menuitem *next = mi->next;
+ free(mi);
+ mi = next;
+ }
+}
+
+/**
+ * fdisk_ask_menu_get_nitems:
+ * @ask: ask instance
+ *
+ * Returns: number of menu items
+ */
+size_t fdisk_ask_menu_get_nitems(struct fdisk_ask *ask)
+{
+ struct ask_menuitem *mi;
+ size_t n;
+
+ assert(ask);
+ assert(fdisk_is_ask(ask, MENU));
+
+ for (n = 0, mi = ask->data.menu.first; mi; mi = mi->next, n++);
+
+ return n;
+}
+
+int fdisk_ask_menu_add_item(struct fdisk_ask *ask, int key,
+ const char *name, const char *desc)
+{
+ struct ask_menuitem *mi;
+
+ assert(ask);
+ assert(fdisk_is_ask(ask, MENU));
+
+ mi = calloc(1, sizeof(*mi));
+ if (!mi)
+ return -ENOMEM;
+ mi->key = key;
+ mi->name = name;
+ mi->desc = desc;
+
+ if (!ask->data.menu.first)
+ ask->data.menu.first = mi;
+ else {
+ struct ask_menuitem *last = ask->data.menu.first;
+
+ while (last->next)
+ last = last->next;
+ last->next = mi;
+ }
+
+ DBG(ASK, ul_debugobj(ask, "new menu item: %c, \"%s\" (%s)\n", mi->key, mi->name, mi->desc));
+ return 0;
+}
+
+
+/*
+ * print-like
+ */
+
+#define is_print_ask(a) (fdisk_is_ask(a, WARN) || fdisk_is_ask(a, WARNX) || fdisk_is_ask(a, INFO))
+
+/**
+ * fdisk_ask_print_get_errno:
+ * @ask: ask instance
+ *
+ * Returns: error number for warning/error messages
+ */
+int fdisk_ask_print_get_errno(struct fdisk_ask *ask)
+{
+ assert(ask);
+ assert(is_print_ask(ask));
+ return ask->data.print.errnum;
+}
+
+int fdisk_ask_print_set_errno(struct fdisk_ask *ask, int errnum)
+{
+ assert(ask);
+ ask->data.print.errnum = errnum;
+ return 0;
+}
+
+/**
+ * fdisk_ask_print_get_mesg:
+ * @ask: ask instance
+ *
+ * Returns: pointer to message
+ */
+const char *fdisk_ask_print_get_mesg(struct fdisk_ask *ask)
+{
+ assert(ask);
+ assert(is_print_ask(ask));
+ return ask->data.print.mesg;
+}
+
+/* does not reallocate the message! */
+int fdisk_ask_print_set_mesg(struct fdisk_ask *ask, const char *mesg)
+{
+ assert(ask);
+ ask->data.print.mesg = mesg;
+ return 0;
+}
+
+static int do_vprint(struct fdisk_context *cxt, int errnum, int type,
+ const char *fmt, va_list va)
+{
+ struct fdisk_ask *ask;
+ int rc;
+ char *mesg;
+
+ assert(cxt);
+
+ if (vasprintf(&mesg, fmt, va) < 0)
+ return -ENOMEM;
+
+ ask = fdisk_new_ask();
+ if (!ask) {
+ free(mesg);
+ return -ENOMEM;
+ }
+
+ fdisk_ask_set_type(ask, type);
+ fdisk_ask_print_set_mesg(ask, mesg);
+ if (errnum >= 0)
+ fdisk_ask_print_set_errno(ask, errnum);
+ rc = fdisk_do_ask(cxt, ask);
+
+ fdisk_unref_ask(ask);
+ free(mesg);
+ return rc;
+}
+
+/**
+ * fdisk_info:
+ * @cxt: context
+ * @fmt: printf-like formatted string
+ * @...: variable parametrs
+ *
+ * High-level API to print info messages,
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...)
+{
+ int rc;
+ va_list ap;
+
+ assert(cxt);
+ va_start(ap, fmt);
+ rc = do_vprint(cxt, -1, FDISK_ASKTYPE_INFO, fmt, ap);
+ va_end(ap);
+ return rc;
+}
+
+/**
+ * fdisk_info:
+ * @cxt: context
+ * @fmt: printf-like formatted string
+ * @...: variable parametrs
+ *
+ * High-level API to print warning message (errno expected)
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...)
+{
+ int rc;
+ va_list ap;
+
+ assert(cxt);
+ va_start(ap, fmt);
+ rc = do_vprint(cxt, errno, FDISK_ASKTYPE_WARN, fmt, ap);
+ va_end(ap);
+ return rc;
+}
+
+/**
+ * fdisk_warnx:
+ * @cxt: context
+ * @fmt: printf-like formatted string
+ * @...: variable options
+ *
+ * High-level API to print warning message
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...)
+{
+ int rc;
+ va_list ap;
+
+ assert(cxt);
+ va_start(ap, fmt);
+ rc = do_vprint(cxt, -1, FDISK_ASKTYPE_WARNX, fmt, ap);
+ va_end(ap);
+ return rc;
+}
+
+int fdisk_info_new_partition(
+ struct fdisk_context *cxt,
+ int num, fdisk_sector_t start, fdisk_sector_t stop,
+ struct fdisk_parttype *t)
+{
+ int rc;
+ char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE,
+ (uint64_t)(stop - start + 1) * cxt->sector_size);
+
+ rc = fdisk_info(cxt,
+ _("Created a new partition %d of type '%s' and of size %s."),
+ num, t ? t->name : _("Unknown"), str);
+ free(str);
+ return rc;
+}
+
+#ifdef TEST_PROGRAM
+int test_ranges(struct fdisk_test *ts, int argc, char *argv[])
+{
+ /* 1 - 3, 6, 8, 9, 11 13 */
+ size_t nums[] = { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1 };
+ size_t numx[] = { 0, 0, 0 };
+ char range[BUFSIZ], *ptr = range;
+ size_t i, len = sizeof(range), begin = 0, run = 0;
+
+ for (i = 0; i < ARRAY_SIZE(nums); i++) {
+ if (!nums[i])
+ continue;
+ ptr = mk_string_list(ptr, &len, &begin, &run, i, 0);
+ }
+ mk_string_list(ptr, &len, &begin, &run, -1, 0);
+ printf("list: '%s'\n", range);
+
+ ptr = range;
+ len = sizeof(range), begin = 0, run = 0;
+ for (i = 0; i < ARRAY_SIZE(numx); i++) {
+ if (!numx[i])
+ continue;
+ ptr = mk_string_list(ptr, &len, &begin, &run, i, 0);
+ }
+ mk_string_list(ptr, &len, &begin, &run, -1, 0);
+ printf("empty list: '%s'\n", range);
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct fdisk_test tss[] = {
+ { "--ranges", test_ranges, "generates ranges" },
+ { NULL }
+ };
+
+ return fdisk_run_test(tss, argc, argv);
+}
+
+#endif
diff --git a/libblkid/libfdisk/src/bsd.c b/libblkid/libfdisk/src/bsd.c
new file mode 100644
index 000000000..618a3eef9
--- /dev/null
+++ b/libblkid/libfdisk/src/bsd.c
@@ -0,0 +1,992 @@
+/*
+ * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
+ *
+ * Based on the original code from fdisk
+ * written by Bernhard Fastenrath (fasten@informatik.uni-bonn.de)
+ * with code from the NetBSD disklabel command.
+ *
+ * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, March 1999
+ * David Huggins-Daines <dhuggins@linuxcare.com>, January 2000
+ */
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/param.h>
+
+#include "nls.h"
+#include "blkdev.h"
+#include "fdiskP.h"
+#include "pt-mbr.h"
+#include "pt-bsd.h"
+#include "all-io.h"
+
+
+/**
+ * SECTION: bsd
+ * @title: BSD
+ * @short_description: disk label specific functions
+ *
+ */
+
+static const char *bsd_dktypenames[] = {
+ "unknown",
+ "SMD",
+ "MSCP",
+ "old DEC",
+ "SCSI",
+ "ESDI",
+ "ST506",
+ "HP-IB",
+ "HP-FL",
+ "type 9",
+ "floppy",
+ 0
+};
+#define BSD_DKMAXTYPES (ARRAY_SIZE(bsd_dktypenames) - 1)
+
+static struct fdisk_parttype bsd_fstypes[] = {
+ {BSD_FS_UNUSED, "unused"},
+ {BSD_FS_SWAP, "swap"},
+ {BSD_FS_V6, "Version 6"},
+ {BSD_FS_V7, "Version 7"},
+ {BSD_FS_SYSV, "System V"},
+ {BSD_FS_V71K, "4.1BSD"},
+ {BSD_FS_V8, "Eighth Edition"},
+ {BSD_FS_BSDFFS, "4.2BSD"},
+#ifdef __alpha__
+ {BSD_FS_EXT2, "ext2"},
+#else
+ {BSD_FS_MSDOS, "MS-DOS"},
+#endif
+ {BSD_FS_BSDLFS, "4.4LFS"},
+ {BSD_FS_OTHER, "unknown"},
+ {BSD_FS_HPFS, "HPFS"},
+ {BSD_FS_ISO9660,"ISO-9660"},
+ {BSD_FS_BOOT, "boot"},
+ {BSD_FS_ADOS, "ADOS"},
+ {BSD_FS_HFS, "HFS"},
+ {BSD_FS_ADVFS, "AdvFS"},
+ { 0, NULL }
+};
+#define BSD_FSMAXTYPES (ARRAY_SIZE(bsd_fstypes)-1)
+
+/*
+ * in-memory fdisk BSD stuff
+ */
+struct fdisk_bsd_label {
+ struct fdisk_label head; /* generic part */
+
+ struct dos_partition *dos_part; /* parent */
+ struct bsd_disklabel bsd; /* on disk label */
+#if defined (__alpha__)
+ /* We access this through a u_int64_t * when checksumming */
+ char bsdbuffer[BSD_BBSIZE] __attribute__((aligned(8)));
+#else
+ char bsdbuffer[BSD_BBSIZE];
+#endif
+};
+
+static int bsd_list_disklabel(struct fdisk_context *cxt);
+static int bsd_initlabel(struct fdisk_context *cxt);
+static int bsd_readlabel(struct fdisk_context *cxt);
+static void sync_disks(struct fdisk_context *cxt);
+
+static inline struct fdisk_bsd_label *self_label(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, BSD));
+
+ return (struct fdisk_bsd_label *) cxt->label;
+}
+
+static inline struct bsd_disklabel *self_disklabel(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, BSD));
+
+ return &((struct fdisk_bsd_label *) cxt->label)->bsd;
+}
+
+static struct fdisk_parttype *bsd_partition_parttype(
+ struct fdisk_context *cxt,
+ struct bsd_partition *p)
+{
+ struct fdisk_parttype *t
+ = fdisk_label_get_parttype_from_code(cxt->label, p->p_fstype);
+ return t ? : fdisk_new_unknown_parttype(p->p_fstype, NULL);
+}
+
+
+#if defined (__alpha__)
+static void alpha_bootblock_checksum (char *boot)
+{
+ uint64_t *dp = (uint64_t *) boot, sum = 0;
+ int i;
+
+ for (i = 0; i < 63; i++)
+ sum += dp[i];
+ dp[63] = sum;
+}
+#endif /* __alpha__ */
+
+#define HIDDEN_MASK 0x10
+
+static int is_bsd_partition_type(int type)
+{
+ return (type == MBR_FREEBSD_PARTITION ||
+ type == (MBR_FREEBSD_PARTITION ^ HIDDEN_MASK) ||
+ type == MBR_NETBSD_PARTITION ||
+ type == (MBR_NETBSD_PARTITION ^ HIDDEN_MASK) ||
+ type == MBR_OPENBSD_PARTITION ||
+ type == (MBR_OPENBSD_PARTITION ^ HIDDEN_MASK));
+}
+
+/*
+ * look for DOS partition usable for nested BSD partition table
+ */
+static int bsd_assign_dos_partition(struct fdisk_context *cxt)
+{
+ struct fdisk_bsd_label *l = self_label(cxt);
+ size_t i;
+
+ for (i = 0; i < 4; i++) {
+ fdisk_sector_t ss;
+
+ l->dos_part = fdisk_dos_get_partition(cxt->parent, i);
+
+ if (!l->dos_part || !is_bsd_partition_type(l->dos_part->sys_ind))
+ continue;
+
+ ss = dos_partition_get_start(l->dos_part);
+ if (!ss) {
+ fdisk_warnx(cxt, _("Partition %zd: has invalid starting "
+ "sector 0."), i + 1);
+ return -1;
+ }
+
+ if (cxt->parent->dev_path) {
+ free(cxt->dev_path);
+ cxt->dev_path = fdisk_partname(
+ cxt->parent->dev_path, i + 1);
+ }
+
+ DBG(LABEL, ul_debug("partition %zu assigned to BSD", i + 1));
+ return 0;
+ }
+
+ fdisk_warnx(cxt, _("There is no *BSD partition on %s."),
+ cxt->parent->dev_path);
+ free(cxt->dev_path);
+ cxt->dev_path = NULL;
+ l->dos_part = NULL;
+ return 1;
+}
+
+static int bsd_probe_label(struct fdisk_context *cxt)
+{
+ int rc = 0;
+
+ if (cxt->parent)
+ rc = bsd_assign_dos_partition(cxt); /* nested BSD partiotn table */
+ if (!rc)
+ rc = bsd_readlabel(cxt);
+ if (!rc)
+ return 1; /* found BSD */
+ return 0; /* not found */
+}
+
+static int set_parttype(
+ struct fdisk_context *cxt,
+ size_t partnum,
+ struct fdisk_parttype *t)
+{
+ struct bsd_partition *p;
+ struct bsd_disklabel *d = self_disklabel(cxt);
+
+ if (partnum >= d->d_npartitions || !t || t->code > UINT8_MAX)
+ return -EINVAL;
+
+ p = &d->d_partitions[partnum];
+ if (t->code == p->p_fstype)
+ return 0;
+
+ p->p_fstype = t->code;
+ fdisk_label_set_changed(cxt->label, 1);
+ return 0;
+}
+
+static int bsd_add_partition(struct fdisk_context *cxt,
+ struct fdisk_partition *pa,
+ size_t *partno)
+{
+ struct fdisk_bsd_label *l = self_label(cxt);
+ struct bsd_disklabel *d = self_disklabel(cxt);
+ size_t i;
+ unsigned int begin = 0, end;
+ int rc = 0;
+
+ rc = fdisk_partition_next_partno(pa, cxt, &i);
+ if (rc)
+ return rc;
+ if (i >= BSD_MAXPARTITIONS)
+ return -ERANGE;
+ if (l->dos_part) {
+ begin = dos_partition_get_start(l->dos_part);
+ end = begin + dos_partition_get_size(l->dos_part) - 1;
+ } else
+ end = d->d_secperunit - 1;
+
+ /*
+ * First sector
+ */
+ if (pa && pa->start_follow_default)
+ ;
+ else if (pa && fdisk_partition_has_start(pa)) {
+ if (pa->start < begin || pa->start > end)
+ return -ERANGE;
+ begin = pa->start;
+ } else {
+ struct fdisk_ask *ask = fdisk_new_ask();
+
+ if (!ask)
+ return -ENOMEM;
+ fdisk_ask_set_query(ask,
+ fdisk_use_cylinders(cxt) ?
+ _("First cylinder") : _("First sector"));
+ fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+ fdisk_ask_number_set_low(ask, fdisk_cround(cxt, begin));
+ fdisk_ask_number_set_default(ask, fdisk_cround(cxt, begin));
+ fdisk_ask_number_set_high(ask, fdisk_cround(cxt, end));
+
+ rc = fdisk_do_ask(cxt, ask);
+ begin = fdisk_ask_number_get_result(ask);
+ fdisk_unref_ask(ask);
+ if (rc)
+ return rc;
+ if (fdisk_use_cylinders(cxt))
+ begin = (begin - 1) * d->d_secpercyl;
+ }
+
+ /*
+ * Last sector
+ */
+ if (pa && pa->end_follow_default)
+ ;
+ else if (pa && fdisk_partition_has_size(pa)) {
+ if (begin + pa->size > end)
+ return -ERANGE;
+ end = begin + pa->size - 1ULL;
+ } else {
+ /* ask user by dialog */
+ struct fdisk_ask *ask = fdisk_new_ask();
+
+ if (!ask)
+ return -ENOMEM;
+ fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+
+ if (fdisk_use_cylinders(cxt)) {
+ fdisk_ask_set_query(ask, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
+ fdisk_ask_number_set_unit(ask,
+ cxt->sector_size *
+ fdisk_get_units_per_sector(cxt));
+ } else {
+ fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
+ fdisk_ask_number_set_unit(ask,cxt->sector_size);
+ }
+
+ fdisk_ask_number_set_low(ask, fdisk_cround(cxt, begin));
+ fdisk_ask_number_set_default(ask, fdisk_cround(cxt, end));
+ fdisk_ask_number_set_high(ask, fdisk_cround(cxt, end));
+ fdisk_ask_number_set_base(ask, fdisk_cround(cxt, begin));
+
+ rc = fdisk_do_ask(cxt, ask);
+ end = fdisk_ask_number_get_result(ask);
+ fdisk_unref_ask(ask);
+ if (rc)
+ return rc;
+ if (fdisk_use_cylinders(cxt))
+ end = end * d->d_secpercyl - 1;
+ }
+
+ d->d_partitions[i].p_size = end - begin + 1;
+ d->d_partitions[i].p_offset = begin;
+ d->d_partitions[i].p_fstype = BSD_FS_UNUSED;
+
+ if (i >= d->d_npartitions)
+ d->d_npartitions = i + 1;
+ cxt->label->nparts_cur = d->d_npartitions;
+
+ if (pa && pa->type)
+ set_parttype(cxt, i, pa->type);
+
+ fdisk_label_set_changed(cxt->label, 1);
+ if (partno)
+ *partno = i;
+ return 0;
+}
+
+static int bsd_set_partition(struct fdisk_context *cxt, size_t n,
+ struct fdisk_partition *pa)
+{
+ struct bsd_partition *p;
+ struct fdisk_bsd_label *l = self_label(cxt);
+ struct bsd_disklabel *d = self_disklabel(cxt);
+
+ if (n >= d->d_npartitions)
+ return -EINVAL;
+
+ p = &d->d_partitions[n];
+
+ /* we have to stay within parental DOS partition */
+ if (l->dos_part && (fdisk_partition_has_start(pa) ||
+ fdisk_partition_has_size(pa))) {
+
+ fdisk_sector_t dosbegin = dos_partition_get_start(l->dos_part);
+ fdisk_sector_t dosend = dosbegin + dos_partition_get_size(l->dos_part) - 1;
+ fdisk_sector_t begin = fdisk_partition_has_start(pa) ? pa->start : p->p_offset;
+ fdisk_sector_t end = begin + (fdisk_partition_has_size(pa) ? pa->size : p->p_size) - 1;
+
+ if (begin < dosbegin || begin > dosend)
+ return -ERANGE;
+ if (end < dosbegin || end > dosend)
+ return -ERANGE;
+ }
+
+ if (pa->type) {
+ int rc = set_parttype(cxt, n, pa->type);
+ if (rc)
+ return rc;
+ }
+
+ if (fdisk_partition_has_start(pa))
+ d->d_partitions[n].p_offset = pa->start;
+ if (fdisk_partition_has_size(pa))
+ d->d_partitions[n].p_size = pa->size;
+
+ fdisk_label_set_changed(cxt->label, 1);
+ return 0;
+}
+
+
+/* Returns 0 on success, < 0 on error. */
+static int bsd_create_disklabel(struct fdisk_context *cxt)
+{
+ int rc, yes = 0;
+ struct bsd_disklabel *d = self_disklabel(cxt);
+
+ fdisk_info(cxt, _("The device %s does not contain BSD disklabel."), cxt->dev_path);
+ rc = fdisk_ask_yesno(cxt,
+ _("Do you want to create a BSD disklabel?"),
+ &yes);
+ if (rc)
+ return rc;
+ if (!yes)
+ return 1;
+ if (cxt->parent) {
+ rc = bsd_assign_dos_partition(cxt);
+ if (rc == 1)
+ /* not found DOS partition usable for BSD label */
+ rc = -EINVAL;
+ }
+ if (rc)
+ return rc;
+
+ rc = bsd_initlabel(cxt);
+ if (!rc) {
+ int org = fdisk_is_details(cxt);
+
+ cxt->label->nparts_cur = d->d_npartitions;
+ cxt->label->nparts_max = BSD_MAXPARTITIONS;
+
+ fdisk_enable_details(cxt, 1);
+ bsd_list_disklabel(cxt);
+ fdisk_enable_details(cxt, org);
+ }
+
+ return rc;
+}
+
+static int bsd_delete_part(
+ struct fdisk_context *cxt,
+ size_t partnum)
+{
+ struct bsd_disklabel *d = self_disklabel(cxt);
+
+ d->d_partitions[partnum].p_size = 0;
+ d->d_partitions[partnum].p_offset = 0;
+ d->d_partitions[partnum].p_fstype = BSD_FS_UNUSED;
+
+ if (d->d_npartitions == partnum + 1)
+ while (!d->d_partitions[d->d_npartitions - 1].p_size)
+ d->d_npartitions--;
+
+ cxt->label->nparts_cur = d->d_npartitions;
+ fdisk_label_set_changed(cxt->label, 1);
+ return 0;
+}
+
+static int bsd_list_disklabel(struct fdisk_context *cxt)
+{
+ struct bsd_disklabel *d = self_disklabel(cxt);
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, BSD));
+
+ if (fdisk_is_details(cxt)) {
+ fdisk_info(cxt, "# %s:", cxt->dev_path);
+
+ if ((unsigned) d->d_type < BSD_DKMAXTYPES)
+ fdisk_info(cxt, _("type: %s"), bsd_dktypenames[d->d_type]);
+ else
+ fdisk_info(cxt, _("type: %d"), d->d_type);
+
+ fdisk_info(cxt, _("disk: %.*s"), (int) sizeof(d->d_typename), d->d_typename);
+ fdisk_info(cxt, _("label: %.*s"), (int) sizeof(d->d_packname), d->d_packname);
+
+ fdisk_info(cxt, _("flags: %s"),
+ d->d_flags & BSD_D_REMOVABLE ? _(" removable") :
+ d->d_flags & BSD_D_ECC ? _(" ecc") :
+ d->d_flags & BSD_D_BADSECT ? _(" badsect") : "");
+
+ /* On various machines the fields of *lp are short/int/long */
+ /* In order to avoid problems, we cast them all to long. */
+ fdisk_info(cxt, _("bytes/sector: %ld"), (long) d->d_secsize);
+ fdisk_info(cxt, _("sectors/track: %ld"), (long) d->d_nsectors);
+ fdisk_info(cxt, _("tracks/cylinder: %ld"), (long) d->d_ntracks);
+ fdisk_info(cxt, _("sectors/cylinder: %ld"), (long) d->d_secpercyl);
+ fdisk_info(cxt, _("cylinders: %ld"), (long) d->d_ncylinders);
+ fdisk_info(cxt, _("rpm: %d"), d->d_rpm);
+ fdisk_info(cxt, _("interleave: %d"), d->d_interleave);
+ fdisk_info(cxt, _("trackskew: %d"), d->d_trackskew);
+ fdisk_info(cxt, _("cylinderskew: %d"), d->d_cylskew);
+ fdisk_info(cxt, _("headswitch: %ld (milliseconds)"), (long) d->d_headswitch);
+ fdisk_info(cxt, _("track-to-track seek: %ld (milliseconds)"), (long) d->d_trkseek);
+ }
+
+ fdisk_info(cxt, _("partitions: %d"), d->d_npartitions);
+
+ return 0;
+}
+
+static int bsd_get_partition(struct fdisk_context *cxt, size_t n,
+ struct fdisk_partition *pa)
+{
+ struct bsd_partition *p;
+ struct bsd_disklabel *d = self_disklabel(cxt);
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, BSD));
+
+ if (n >= d->d_npartitions)
+ return -EINVAL;
+
+ p = &d->d_partitions[n];
+
+ pa->used = p->p_size ? 1 : 0;
+ if (!pa->used)
+ return 0;
+
+ if (fdisk_use_cylinders(cxt) && d->d_secpercyl) {
+ pa->start_post = p->p_offset % d->d_secpercyl ? '*' : ' ';
+ pa->end_post = (p->p_offset + p->p_size) % d->d_secpercyl ? '*' : ' ';
+ }
+
+ pa->start = p->p_offset;
+ pa->size = p->p_size;
+ pa->type = bsd_partition_parttype(cxt, p);
+
+ if (p->p_fstype == BSD_FS_UNUSED || p->p_fstype == BSD_FS_BSDFFS) {
+ pa->fsize = p->p_fsize;
+ pa->bsize = p->p_fsize * p->p_frag;
+ }
+ if (p->p_fstype == BSD_FS_BSDFFS)
+ pa->cpg = p->p_cpg;
+
+ return 0;
+}
+
+static uint32_t ask_uint32(struct fdisk_context *cxt,
+ uint32_t dflt, char *mesg)
+{
+ uintmax_t res;
+
+ if (fdisk_ask_number(cxt, min(dflt, (uint32_t) 1), dflt,
+ UINT32_MAX, mesg, &res) == 0)
+ return res;
+ return dflt;
+}
+
+static uint16_t ask_uint16(struct fdisk_context *cxt,
+ uint16_t dflt, char *mesg)
+{
+ uintmax_t res;
+
+ if (fdisk_ask_number(cxt, min(dflt, (uint16_t) 1),
+ dflt, UINT16_MAX, mesg, &res) == 0)
+ return res;
+ return dflt;
+}
+
+/**
+ * fdisk_bsd_edit_disklabel:
+ * @cxt: context
+ *
+ * Edits fields in BSD disk label.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_bsd_edit_disklabel(struct fdisk_context *cxt)
+{
+ struct bsd_disklabel *d = self_disklabel(cxt);
+ uintmax_t res;
+
+#if defined (__alpha__) || defined (__ia64__)
+ if (fdisk_ask_number(cxt, DEFAULT_SECTOR_SIZE, d->d_secsize,
+ UINT32_MAX, _("bytes/sector"), &res) == 0)
+ d->d_secsize = res;
+
+ d->d_nsectors = ask_uint32(cxt, d->d_nsectors, _("sectors/track"));
+ d->d_ntracks = ask_uint32(cxt, d->d_ntracks, _("tracks/cylinder"));
+ d->d_ncylinders = ask_uint32(cxt, d->d_ncylinders ,_("cylinders"));
+#endif
+ if (fdisk_ask_number(cxt, 1, d->d_nsectors * d->d_ntracks,
+ d->d_nsectors * d->d_ntracks,
+ _("sectors/cylinder"), &res) == 0)
+ d->d_secpercyl = res;
+
+ d->d_rpm = ask_uint16(cxt, d->d_rpm, _("rpm"));
+ d->d_interleave = ask_uint16(cxt, d->d_interleave, _("interleave"));
+ d->d_trackskew = ask_uint16(cxt, d->d_trackskew, _("trackskew"));
+ d->d_cylskew = ask_uint16(cxt, d->d_cylskew, _("cylinderskew"));
+
+ d->d_headswitch = ask_uint32(cxt, d->d_headswitch, _("headswitch"));
+ d->d_trkseek = ask_uint32(cxt, d->d_trkseek, _("track-to-track seek"));
+
+ d->d_secperunit = d->d_secpercyl * d->d_ncylinders;
+ return 0;
+}
+
+static int bsd_get_bootstrap(struct fdisk_context *cxt,
+ char *path, void *ptr, int size)
+{
+ int fd;
+
+ if ((fd = open(path, O_RDONLY)) < 0) {
+ fdisk_warn(cxt, _("cannot open %s"), path);
+ return -errno;
+ }
+
+ if (read_all(fd, ptr, size) != size) {
+ fdisk_warn(cxt, _("cannot read %s"), path);
+ close(fd);
+ return -errno;
+ }
+
+ fdisk_info(cxt, _("The bootstrap file %s successfully loaded."), path);
+ close (fd);
+ return 0;
+}
+
+/**
+ * fdisk_bsd_write_bootstrap:
+ * @cxt: context
+ *
+ * Install bootstrap file to the BSD device
+ */
+int fdisk_bsd_write_bootstrap(struct fdisk_context *cxt)
+{
+ struct bsd_disklabel dl, *d = self_disklabel(cxt);
+ struct fdisk_bsd_label *l = self_label(cxt);
+ char *name = d->d_type == BSD_DTYPE_SCSI ? "sd" : "wd";
+ char buf[BUFSIZ];
+ char *res, *dp, *p;
+ int rc;
+ fdisk_sector_t sector;
+
+ snprintf(buf, sizeof(buf),
+ _("Bootstrap: %1$sboot -> boot%1$s (default %1$s)"),
+ name);
+ rc = fdisk_ask_string(cxt, buf, &res);
+ if (rc)
+ goto done;
+ if (res && *res)
+ name = res;
+
+ snprintf(buf, sizeof(buf), "%s/%sboot", BSD_LINUX_BOOTDIR, name);
+ rc = bsd_get_bootstrap(cxt, buf, l->bsdbuffer, (int) d->d_secsize);
+ if (rc)
+ goto done;
+
+ /* We need a backup of the disklabel (might have changed). */
+ dp = &l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE];
+ memmove(&dl, dp, sizeof(struct bsd_disklabel));
+
+ /* The disklabel will be overwritten by 0's from bootxx anyway */
+ memset(dp, 0, sizeof(struct bsd_disklabel));
+
+ snprintf(buf, sizeof(buf), "%s/boot%s", BSD_LINUX_BOOTDIR, name);
+ rc = bsd_get_bootstrap(cxt, buf,
+ &l->bsdbuffer[d->d_secsize],
+ (int) d->d_bbsize - d->d_secsize);
+ if (rc)
+ goto done;
+
+ /* check end of the bootstrap */
+ for (p = dp; p < dp + sizeof(struct bsd_disklabel); p++) {
+ if (!*p)
+ continue;
+ fdisk_warnx(cxt, _("Bootstrap overlaps with disklabel!"));
+ return -EINVAL;
+ }
+
+ /* move disklabel back */
+ memmove(dp, &dl, sizeof(struct bsd_disklabel));
+
+ sector = 0;
+ if (l->dos_part)
+ sector = dos_partition_get_start(l->dos_part);
+#if defined (__alpha__)
+ alpha_bootblock_checksum(l->bsdbuffer);
+#endif
+ if (lseek(cxt->dev_fd, (off_t) sector * DEFAULT_SECTOR_SIZE, SEEK_SET) == -1) {
+ fdisk_warn(cxt, _("seek on %s failed"), cxt->dev_path);
+ rc = -errno;
+ goto done;
+ }
+ if (write_all(cxt->dev_fd, l->bsdbuffer, BSD_BBSIZE)) {
+ fdisk_warn(cxt, _("cannot write %s"), cxt->dev_path);
+ rc = -errno;
+ goto done;
+ }
+
+ fdisk_info(cxt, _("Bootstrap installed on %s."), cxt->dev_path);
+ sync_disks(cxt);
+
+ rc = 0;
+done:
+ free(res);
+ return rc;
+}
+
+static unsigned short bsd_dkcksum (struct bsd_disklabel *lp)
+{
+ unsigned short *start, *end;
+ unsigned short sum = 0;
+
+ start = (unsigned short *) lp;
+ end = (unsigned short *) &lp->d_partitions[lp->d_npartitions];
+ while (start < end)
+ sum ^= *start++;
+ return sum;
+}
+
+static int bsd_initlabel (struct fdisk_context *cxt)
+{
+ struct fdisk_bsd_label *l = self_label(cxt);
+ struct bsd_disklabel *d = self_disklabel(cxt);
+ struct bsd_partition *pp;
+
+ memset (d, 0, sizeof (struct bsd_disklabel));
+
+ d -> d_magic = BSD_DISKMAGIC;
+
+ if (strncmp (cxt->dev_path, "/dev/sd", 7) == 0)
+ d -> d_type = BSD_DTYPE_SCSI;
+ else
+ d -> d_type = BSD_DTYPE_ST506;
+
+#if !defined (__alpha__)
+ d -> d_flags = BSD_D_DOSPART;
+#else
+ d -> d_flags = 0;
+#endif
+ d -> d_secsize = DEFAULT_SECTOR_SIZE; /* bytes/sector */
+ d -> d_nsectors = cxt->geom.sectors; /* sectors/track */
+ d -> d_ntracks = cxt->geom.heads; /* tracks/cylinder (heads) */
+ d -> d_ncylinders = cxt->geom.cylinders;
+ d -> d_secpercyl = cxt->geom.sectors * cxt->geom.heads;/* sectors/cylinder */
+ if (d -> d_secpercyl == 0)
+ d -> d_secpercyl = 1; /* avoid segfaults */
+ d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders;
+
+ d -> d_rpm = 3600;
+ d -> d_interleave = 1;
+ d -> d_trackskew = 0;
+ d -> d_cylskew = 0;
+ d -> d_headswitch = 0;
+ d -> d_trkseek = 0;
+
+ d -> d_magic2 = BSD_DISKMAGIC;
+ d -> d_bbsize = BSD_BBSIZE;
+ d -> d_sbsize = BSD_SBSIZE;
+
+ if (l->dos_part) {
+ d->d_npartitions = 4;
+
+ pp = &d->d_partitions[2]; /* Partition C should be the NetBSD partition */
+ pp->p_offset = dos_partition_get_start(l->dos_part);
+ pp->p_size = dos_partition_get_size(l->dos_part);
+ pp->p_fstype = BSD_FS_UNUSED;
+
+ pp = &d -> d_partitions[3]; /* Partition D should be the whole disk */
+ pp->p_offset = 0;
+ pp->p_size = d->d_secperunit;
+ pp->p_fstype = BSD_FS_UNUSED;
+ } else {
+ d->d_npartitions = 3;
+
+ pp = &d->d_partitions[2]; /* Partition C should be the whole disk */
+ pp->p_offset = 0;
+ pp->p_size = d->d_secperunit;
+ pp->p_fstype = BSD_FS_UNUSED;
+ }
+
+ return 0;
+}
+
+/*
+ * Read a bsd_disklabel from sector 0 or from the starting sector of p.
+ * If it has the right magic, return 0.
+ */
+static int bsd_readlabel(struct fdisk_context *cxt)
+{
+ struct fdisk_bsd_label *l;
+ struct bsd_disklabel *d;
+ int t;
+ off_t offset = 0;
+
+ l = self_label(cxt);
+ d = self_disklabel(cxt);
+
+ if (l->dos_part)
+ /* BSD is nested within DOS partition, get the begin of the
+ * partition. Note that DOS uses native sector size. */
+ offset = dos_partition_get_start(l->dos_part) * cxt->sector_size;
+
+ if (lseek(cxt->dev_fd, offset, SEEK_SET) == -1)
+ return -1;
+ if (read_all(cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer)) < 0)
+ return errno ? -errno : -1;
+
+ /* The offset to begin of the disk label. Note that BSD uses
+ * 512-byte (default) sectors. */
+ memmove(d, &l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE
+ + BSD_LABELOFFSET], sizeof(*d));
+
+ if (d->d_magic != BSD_DISKMAGIC || d->d_magic2 != BSD_DISKMAGIC) {
+ DBG(LABEL, ul_debug("not found magic"));
+ return -1;
+ }
+
+ for (t = d->d_npartitions; t < BSD_MAXPARTITIONS; t++) {
+ d->d_partitions[t].p_size = 0;
+ d->d_partitions[t].p_offset = 0;
+ d->d_partitions[t].p_fstype = BSD_FS_UNUSED;
+ }
+
+ if (d->d_npartitions > BSD_MAXPARTITIONS)
+ fdisk_warnx(cxt, ("Too many partitions (%d, maximum is %d)."),
+ d->d_npartitions, BSD_MAXPARTITIONS);
+
+ /* let's follow in-PT geometry */
+ cxt->geom.sectors = d->d_nsectors;
+ cxt->geom.heads = d->d_ntracks;
+ cxt->geom.cylinders = d->d_ncylinders;
+
+ cxt->label->nparts_cur = d->d_npartitions;
+ cxt->label->nparts_max = BSD_MAXPARTITIONS;
+ DBG(LABEL, ul_debug("read BSD label"));
+ return 0;
+}
+
+static int bsd_write_disklabel(struct fdisk_context *cxt)
+{
+ off_t offset = 0;
+ struct fdisk_bsd_label *l = self_label(cxt);
+ struct bsd_disklabel *d = self_disklabel(cxt);
+
+
+ if (l->dos_part)
+ offset = dos_partition_get_start(l->dos_part) * cxt->sector_size;
+
+ d->d_checksum = 0;
+ d->d_checksum = bsd_dkcksum(d);
+
+ /* Update label within boot block. */
+ memmove(&l->bsdbuffer[BSD_LABELSECTOR * DEFAULT_SECTOR_SIZE
+ + BSD_LABELOFFSET], d, sizeof(*d));
+
+#if defined (__alpha__) && BSD_LABELSECTOR == 0
+ /* Write the checksum to the end of the first sector. */
+ alpha_bootblock_checksum(l->bsdbuffer);
+#endif
+ if (lseek(cxt->dev_fd, offset, SEEK_SET) == -1) {
+ fdisk_warn(cxt, _("seek on %s failed"), cxt->dev_path);
+ return -errno;
+ }
+ if (write_all(cxt->dev_fd, l->bsdbuffer, sizeof(l->bsdbuffer))) {
+ fdisk_warn(cxt, _("cannot write %s"), cxt->dev_path);
+ return -errno;
+ }
+ sync_disks(cxt);
+
+ fdisk_info(cxt, _("Disklabel written to %s."), cxt->dev_path);
+ return 0;
+}
+
+static void sync_disks(struct fdisk_context *cxt)
+{
+ fdisk_info(cxt, _("Syncing disks."));
+ sync();
+}
+
+static int bsd_translate_fstype (int linux_type)
+{
+ switch (linux_type) {
+ case 0x01: /* DOS 12-bit FAT */
+ case 0x04: /* DOS 16-bit <32M */
+ case 0x06: /* DOS 16-bit >=32M */
+ case 0xe1: /* DOS access */
+ case 0xe3: /* DOS R/O */
+#if !defined (__alpha__)
+ case 0xf2: /* DOS secondary */
+ return BSD_FS_MSDOS;
+#endif
+ case 0x07: /* OS/2 HPFS */
+ return BSD_FS_HPFS;
+ default:
+ break;
+ }
+
+ return BSD_FS_OTHER;
+}
+
+/**
+ * fdisk_bsd_link_partition:
+ * @cxt: context
+ *
+ * Links partition from parent (DOS) to nested BSD partition table.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_bsd_link_partition(struct fdisk_context *cxt)
+{
+ size_t k, i;
+ int rc;
+ struct dos_partition *p;
+ struct bsd_disklabel *d = self_disklabel(cxt);
+
+ if (!cxt->parent || !fdisk_is_label(cxt->parent, DOS)) {
+ fdisk_warnx(cxt, _("BSD label is not nested within a DOS partition."));
+ return -EINVAL;
+ }
+
+ /* ask for DOS partition */
+ rc = fdisk_ask_partnum(cxt->parent, &k, FALSE);
+ if (rc)
+ return rc;
+ /* ask for BSD partition */
+ rc = fdisk_ask_partnum(cxt, &i, TRUE);
+ if (rc)
+ return rc;
+
+ if (i >= BSD_MAXPARTITIONS)
+ return -EINVAL;
+
+ p = fdisk_dos_get_partition(cxt->parent, k);
+
+ d->d_partitions[i].p_size = dos_partition_get_size(p);
+ d->d_partitions[i].p_offset = dos_partition_get_start(p);
+ d->d_partitions[i].p_fstype = bsd_translate_fstype(p->sys_ind);
+
+ if (i >= d->d_npartitions)
+ d->d_npartitions = i + 1;
+
+ cxt->label->nparts_cur = d->d_npartitions;
+ fdisk_label_set_changed(cxt->label, 1);
+
+ fdisk_info(cxt, _("BSD partition '%c' linked to DOS partition %zu."),
+ 'a' + (int) i, k + 1);
+ return 0;
+}
+
+
+static int bsd_partition_is_used(
+ struct fdisk_context *cxt,
+ size_t partnum)
+{
+ struct bsd_disklabel *d = self_disklabel(cxt);
+
+ if (partnum >= BSD_MAXPARTITIONS)
+ return 0;
+
+ return d->d_partitions[partnum].p_size ? 1 : 0;
+}
+
+
+static const struct fdisk_label_operations bsd_operations =
+{
+ .probe = bsd_probe_label,
+ .list = bsd_list_disklabel,
+ .write = bsd_write_disklabel,
+ .create = bsd_create_disklabel,
+
+ .del_part = bsd_delete_part,
+ .get_part = bsd_get_partition,
+ .set_part = bsd_set_partition,
+ .add_part = bsd_add_partition,
+
+ .part_is_used = bsd_partition_is_used,
+};
+
+static const struct fdisk_field bsd_fields[] =
+{
+ { FDISK_FIELD_DEVICE, N_("Slice"), 1, 0 },
+ { FDISK_FIELD_START, N_("Start"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_END, N_("End"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_SECTORS, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_CYLINDERS,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_SIZE, N_("Size"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_TYPE, N_("Type"), 8, 0 },
+ { FDISK_FIELD_FSIZE, N_("Fsize"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_BSIZE, N_("Bsize"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_CPG, N_("Cpg"), 5, FDISK_FIELDFL_NUMBER }
+};
+
+/*
+ * allocates BSD label driver
+ */
+struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt)
+{
+ struct fdisk_label *lb;
+ struct fdisk_bsd_label *bsd;
+
+ assert(cxt);
+
+ bsd = calloc(1, sizeof(*bsd));
+ if (!bsd)
+ return NULL;
+
+ /* initialize generic part of the driver */
+ lb = (struct fdisk_label *) bsd;
+ lb->name = "bsd";
+ lb->id = FDISK_DISKLABEL_BSD;
+ lb->op = &bsd_operations;
+ lb->parttypes = bsd_fstypes;
+ lb->nparttypes = ARRAY_SIZE(bsd_fstypes) - 1;
+
+ lb->fields = bsd_fields;
+ lb->nfields = ARRAY_SIZE(bsd_fields);
+
+ lb->flags |= FDISK_LABEL_FL_INCHARS_PARTNO;
+ lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
+
+ return lb;
+}
diff --git a/libblkid/libfdisk/src/context.c b/libblkid/libfdisk/src/context.c
new file mode 100644
index 000000000..2a4d377e0
--- /dev/null
+++ b/libblkid/libfdisk/src/context.c
@@ -0,0 +1,1017 @@
+#ifdef HAVE_LIBBLKID
+# include <blkid.h>
+#endif
+
+#include "fdiskP.h"
+
+
+/**
+ * SECTION: context
+ * @title: Context
+ * @short_description: stores info about device, labels etc.
+ *
+ * The library distinguish between three types of partitioning objects.
+ *
+ * on-disk data
+ * - disk label specific
+ * - probed and read by disklabel drivers when assign device to the context
+ * or when switch to another disk label type
+ * - only fdisk_write_disklabel() modify on-disk data
+ *
+ * in-memory data
+ * - generic data and disklabel specific data stored in struct fdisk_label
+ * - all partitioning operations are based on in-memory data only
+ *
+ * struct fdisk_partition
+ * - provides abstraction to present partitions to users
+ * - fdisk_partition is possible to gather to fdisk_table container
+ * - used as unified template for new partitions
+ * - the struct fdisk_partition is always completely independent object and
+ * any change to the object has no effect to in-memory (or on-disk) label data
+ */
+
+/**
+ * fdisk_new_context:
+ *
+ * Returns: newly allocated libfdisk handler
+ */
+struct fdisk_context *fdisk_new_context(void)
+{
+ struct fdisk_context *cxt;
+
+ cxt = calloc(1, sizeof(*cxt));
+ if (!cxt)
+ return NULL;
+
+ DBG(CXT, ul_debugobj(cxt, "alloc"));
+ cxt->dev_fd = -1;
+ cxt->refcount = 1;
+
+ /*
+ * Allocate label specific structs.
+ *
+ * This is necessary (for example) to store label specific
+ * context setting.
+ */
+ cxt->labels[ cxt->nlabels++ ] = fdisk_new_gpt_label(cxt);
+ cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
+ cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
+ cxt->labels[ cxt->nlabels++ ] = fdisk_new_sgi_label(cxt);
+ cxt->labels[ cxt->nlabels++ ] = fdisk_new_sun_label(cxt);
+
+ return cxt;
+}
+
+static int init_nested_from_parent(struct fdisk_context *cxt, int isnew)
+{
+ struct fdisk_context *parent;
+
+ assert(cxt);
+ assert(cxt->parent);
+
+ parent = cxt->parent;
+
+ cxt->alignment_offset = parent->alignment_offset;
+ cxt->ask_cb = parent->ask_cb;
+ cxt->ask_data = parent->ask_data;
+ cxt->dev_fd = parent->dev_fd;
+ cxt->first_lba = parent->first_lba;
+ cxt->firstsector_bufsz = parent->firstsector_bufsz;
+ cxt->firstsector = parent->firstsector;
+ cxt->geom = parent->geom;
+ cxt->grain = parent->grain;
+ cxt->io_size = parent->io_size;
+ cxt->last_lba = parent->last_lba;
+ cxt->min_io_size = parent->min_io_size;
+ cxt->optimal_io_size = parent->optimal_io_size;
+ cxt->phy_sector_size = parent->phy_sector_size;
+ cxt->readonly = parent->readonly;
+ cxt->script = parent->script;
+ fdisk_ref_script(cxt->script);
+ cxt->sector_size = parent->sector_size;
+ cxt->total_sectors = parent->total_sectors;
+ cxt->user_geom = parent->user_geom;
+ cxt->user_log_sector = parent->user_log_sector;
+ cxt->user_pyh_sector = parent->user_pyh_sector;
+
+ /* parent <--> nested independent setting, initialize for new nested
+ * contexts only */
+ if (isnew) {
+ cxt->listonly = parent->listonly;
+ cxt->display_details = parent->display_details;
+ cxt->display_in_cyl_units = parent->display_in_cyl_units;
+ }
+
+ free(cxt->dev_path);
+ cxt->dev_path = NULL;
+
+ if (parent->dev_path) {
+ cxt->dev_path = strdup(parent->dev_path);
+ if (!cxt->dev_path)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/**
+ * fdisk_new_nested_context:
+ * @parent: parental context
+ * @name: optional label name (e.g. "bsd")
+ *
+ * Create a new nested fdisk context for nested disk labels (e.g. BSD or PMBR).
+ * The function also probes for the nested label on the device if device is
+ * already assigned to parent.
+ *
+ * The new context is initialized according to @parent and both context shares
+ * some settings and file descriptor to the device. The child propagate some
+ * changes (like fdisk_assign_device()) to parent, but it does not work
+ * vice-versa. The behavior is undefined if you assign another device to
+ * parent.
+ *
+ * Returns: new context for nested partition table.
+ */
+struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent,
+ const char *name)
+{
+ struct fdisk_context *cxt;
+ struct fdisk_label *lb = NULL;
+
+ assert(parent);
+
+ cxt = calloc(1, sizeof(*cxt));
+ if (!cxt)
+ return NULL;
+
+ DBG(CXT, ul_debugobj(parent, "alloc nested [%p]", cxt));
+ cxt->refcount = 1;
+
+ fdisk_ref_context(parent);
+ cxt->parent = parent;
+
+ if (init_nested_from_parent(cxt, 1) != 0)
+ return NULL;
+
+ if (name) {
+ if (strcmp(name, "bsd") == 0)
+ lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
+ else if (strcmp(name, "dos") == 0)
+ lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
+ }
+
+ if (lb && parent->dev_fd >= 0) {
+ DBG(CXT, ul_debugobj(cxt, "probing for nested %s", lb->name));
+
+ cxt->label = lb;
+
+ if (lb->op->probe(cxt) == 1)
+ __fdisk_switch_label(cxt, lb);
+ else {
+ DBG(CXT, ul_debugobj(cxt, "not found %s label", lb->name));
+ if (lb->op->deinit)
+ lb->op->deinit(lb);
+ cxt->label = NULL;
+ }
+ }
+
+ return cxt;
+}
+
+
+/**
+ * fdisk_ref_context:
+ * @cxt: context pointer
+ *
+ * Increments reference counter.
+ */
+void fdisk_ref_context(struct fdisk_context *cxt)
+{
+ if (cxt)
+ cxt->refcount++;
+}
+
+/**
+ * fdisk_get_label:
+ * @cxt: context instance
+ * @name: label name (e.g. "gpt")
+ *
+ * If no @name specified then returns the current context label.
+ *
+ * The label is allocated and maintained within the context #cxt. There is
+ * nothing like reference counting for labels, you cannot delallocate the
+ * label.
+ *
+ * Returns: label struct or NULL in case of error.
+ */
+struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name)
+{
+ size_t i;
+
+ assert(cxt);
+
+ if (!name)
+ return cxt->label;
+
+ for (i = 0; i < cxt->nlabels; i++)
+ if (cxt->labels[i]
+ && strcmp(cxt->labels[i]->name, name) == 0)
+ return cxt->labels[i];
+
+ DBG(CXT, ul_debugobj(cxt, "failed to found %s label driver", name));
+ return NULL;
+}
+
+/**
+ * fdisk_next_label:
+ * @cxt: context instance
+ * @lb: returns pointer to the next label
+ *
+ * <informalexample>
+ * <programlisting>
+ * // print all supported labels
+ * struct fdisk_context *cxt = fdisk_new_context();
+ * struct fdisk_label *lb = NULL;
+ *
+ * while (fdisk_next_label(cxt, &lb) == 0)
+ * print("label name: %s\n", fdisk_label_get_name(lb));
+ * fdisk_unref_context(cxt);
+ * </programlisting>
+ * </informalexample>
+ *
+ * Returns: <0 in case of error, 0 on success, 1 at the end.
+ */
+int fdisk_next_label(struct fdisk_context *cxt, struct fdisk_label **lb)
+{
+ size_t i;
+ struct fdisk_label *res = NULL;
+
+ if (!lb || !cxt)
+ return -EINVAL;
+
+ if (!*lb)
+ res = cxt->labels[0];
+ else {
+ for (i = 1; i < cxt->nlabels; i++) {
+ if (*lb == cxt->labels[i - 1]) {
+ res = cxt->labels[i];
+ break;
+ }
+ }
+ }
+
+ *lb = res;
+ return res ? 0 : 1;
+}
+
+/**
+ * fdisk_get_nlabels:
+ * @cxt: context
+ *
+ * Returns: number of supported label types
+ */
+size_t fdisk_get_nlabels(struct fdisk_context *cxt)
+{
+ return cxt ? cxt->nlabels : 0;
+}
+
+int __fdisk_switch_label(struct fdisk_context *cxt, struct fdisk_label *lb)
+{
+ if (!lb || !cxt)
+ return -EINVAL;
+ if (lb->disabled) {
+ DBG(CXT, ul_debugobj(cxt, "*** attempt to switch to disabled label %s -- ignore!", lb->name));
+ return -EINVAL;
+ }
+ cxt->label = lb;
+ DBG(CXT, ul_debugobj(cxt, "--> switching context to %s!", lb->name));
+ return 0;
+}
+
+/**
+ * fdisk_has_label:
+ * @cxt: fdisk context
+ *
+ * Returns: return 1 if there is label on the device.
+ */
+int fdisk_has_label(struct fdisk_context *cxt)
+{
+ return cxt && cxt->label;
+}
+
+/**
+ * fdisk_get_npartitions:
+ * @cxt: context
+ *
+ * The maximal number of the partitions depends on disklabel and does not
+ * have to describe the real limit of PT.
+ *
+ * For example the limit for MBR without extend partition is 4, with extended
+ * partition it's unlimited (so the function returns the current number of all
+ * partitions in this case).
+ *
+ * And for example for GPT it depends on space allocated on disk for array of
+ * entry records (usually 128).
+ *
+ * It's fine to use fdisk_get_npartitions() in loops, but don't forget that
+ * partition may be unused (see fdisk_is_partition_used()).
+ *
+ * <informalexample>
+ * <programlisting>
+ * struct fdisk_partition *pa = NULL;
+ * size_t i, nmax = fdisk_get_npartitions(cxt);
+ *
+ * for (i = 0; i < nmax; i++) {
+ * if (!fdisk_is_partition_used(cxt, i))
+ * continue;
+ * ... do something ...
+ * }
+ * </programlisting>
+ * </informalexample>
+ *
+ * Note that the recommended way to list partitions is to use
+ * fdisk_get_partitions() and struct fdisk_table than ask disk driver for each
+ * individual partitions.
+ *
+ * Returns: maximal number of partitions for the current label.
+ */
+size_t fdisk_get_npartitions(struct fdisk_context *cxt)
+{
+ return cxt && cxt->label ? cxt->label->nparts_max : 0;
+}
+
+/**
+ * fdisk_is_labeltype:
+ * @cxt: fdisk context
+ * @id: FDISK_DISKLABEL_*
+ *
+ * See also fdisk_is_label() macro in libfdisk.h.
+ *
+ * Returns: return 1 if the current label is @id
+ */
+int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id)
+{
+ assert(cxt);
+
+ return cxt->label && fdisk_label_get_type(cxt->label) == id;
+}
+
+/**
+ * fdisk_get_parent:
+ * @cxt: nested fdisk context
+ *
+ * Returns: pointer to parental context, or NULL
+ */
+struct fdisk_context *fdisk_get_parent(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->parent;
+}
+
+static void reset_context(struct fdisk_context *cxt)
+{
+ size_t i;
+
+ DBG(CXT, ul_debugobj(cxt, "*** resetting context"));
+
+ /* reset drives' private data */
+ for (i = 0; i < cxt->nlabels; i++)
+ fdisk_deinit_label(cxt->labels[i]);
+
+ if (cxt->parent) {
+ /* the first sector may be independent on parent */
+ if (cxt->parent->firstsector != cxt->firstsector)
+ free(cxt->firstsector);
+ } else {
+ /* we close device only in primary context */
+ if (cxt->dev_fd > -1)
+ close(cxt->dev_fd);
+ free(cxt->firstsector);
+ }
+
+ free(cxt->dev_path);
+ cxt->dev_path = NULL;
+
+ cxt->dev_fd = -1;
+ cxt->firstsector = NULL;
+ cxt->firstsector_bufsz = 0;
+
+ fdisk_zeroize_device_properties(cxt);
+
+ fdisk_unref_script(cxt->script);
+ cxt->script = NULL;
+
+ cxt->label = NULL;
+}
+
+/*
+ * This function prints a warning if the device is not wiped (e.g. wipefs(8).
+ * Please don't call this function if there is already a PT.
+ *
+ * Returns: 0 if nothing found, < 0 on error, 1 if found a signature
+ */
+static int warn_wipe(struct fdisk_context *cxt)
+{
+#ifdef HAVE_LIBBLKID
+ blkid_probe pr;
+#endif
+ int rc = 0;
+
+ assert(cxt);
+
+ if (fdisk_has_label(cxt) || cxt->dev_fd < 0)
+ return -EINVAL;
+#ifdef HAVE_LIBBLKID
+ DBG(CXT, ul_debugobj(cxt, "wipe check: initialize libblkid prober"));
+
+ pr = blkid_new_probe();
+ if (!pr)
+ return -ENOMEM;
+ rc = blkid_probe_set_device(pr, cxt->dev_fd, 0, 0);
+ if (rc)
+ return rc;
+
+ blkid_probe_enable_superblocks(pr, 1);
+ blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE);
+ blkid_probe_enable_partitions(pr, 1);
+
+ /* we care about the first found FS/raid, so don't call blkid_do_probe()
+ * in loop or don't use blkid_do_fullprobe() ... */
+ rc = blkid_do_probe(pr);
+ if (rc == 0) {
+ const char *name = NULL;
+
+ if (blkid_probe_lookup_value(pr, "TYPE", &name, 0) == 0 ||
+ blkid_probe_lookup_value(pr, "PTTYPE", &name, 0) == 0) {
+ fdisk_warnx(cxt, _(
+ "%s: device contains a valid '%s' signature; it is "
+ "strongly recommended to wipe the device with "
+ "wipefs(8) if this is unexpected, in order to "
+ "avoid possible collisions"), cxt->dev_path, name);
+ rc = 1;
+ }
+ }
+
+ blkid_free_probe(pr);
+#endif
+ return rc;
+}
+
+/**
+ * fdisk_assign_device:
+ * @cxt: context
+ * @fname: path to the device to be handled
+ * @readonly: how to open the device
+ *
+ * Open the device, discovery topology, geometry, detect disklabel and switch
+ * the current label driver to reflect the probing result.
+ *
+ * Note that this function resets all generic setting in context. If the @cxt
+ * is nested context then the device is assigned to the parental context and
+ * necessary properties are copied to the @cxt. The change is propagated in
+ * child->parent direction only. It's impossible to use a different device for
+ * primary and nested contexts.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_assign_device(struct fdisk_context *cxt,
+ const char *fname, int readonly)
+{
+ int fd;
+
+ DBG(CXT, ul_debugobj(cxt, "assigning device %s", fname));
+ assert(cxt);
+
+ /* redirect request to parent */
+ if (cxt->parent) {
+ int rc, org = fdisk_is_listonly(cxt->parent);
+
+ /* assign_device() is sensitive to "listonly" mode, so let's
+ * follow the current context setting for the parent to avoid
+ * unwanted extra warnings. */
+ fdisk_enable_listonly(cxt->parent, fdisk_is_listonly(cxt));
+
+ rc = fdisk_assign_device(cxt->parent, fname, readonly);
+ fdisk_enable_listonly(cxt->parent, org);
+
+ if (!rc)
+ rc = init_nested_from_parent(cxt, 0);
+ if (!rc)
+ fdisk_probe_labels(cxt);
+ return rc;
+ }
+
+ reset_context(cxt);
+
+ fd = open(fname, (readonly ? O_RDONLY : O_RDWR ) | O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ cxt->readonly = readonly;
+ cxt->dev_fd = fd;
+ cxt->dev_path = strdup(fname);
+ if (!cxt->dev_path)
+ goto fail;
+
+ fdisk_discover_topology(cxt);
+ fdisk_discover_geometry(cxt);
+
+ if (fdisk_read_firstsector(cxt) < 0)
+ goto fail;
+
+ /* detect labels and apply labes specific stuff (e.g geomery)
+ * to the context */
+ fdisk_probe_labels(cxt);
+
+ /* let's apply user geometry *after* label prober
+ * to make it possible to override in-label setting */
+ fdisk_apply_user_device_properties(cxt);
+
+ /* warn about obsolete stuff on the device if we aren't in
+ * list-only mode and there is not PT yet */
+ if (!fdisk_is_listonly(cxt) && !fdisk_has_label(cxt))
+ warn_wipe(cxt);
+
+ DBG(CXT, ul_debugobj(cxt, "initialized for %s [%s]",
+ fname, readonly ? "READ-ONLY" : "READ-WRITE"));
+ return 0;
+fail:
+ DBG(CXT, ul_debugobj(cxt, "failed to assign device"));
+ return -errno;
+}
+
+/**
+ * fdisk_deassign_device:
+ * @cxt: context
+ * @nosync: disable fsync()
+ *
+ * Close device and call fsync(). If the @cxt is nested context than the
+ * request is redirected to the parent.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_deassign_device(struct fdisk_context *cxt, int nosync)
+{
+ assert(cxt);
+ assert(cxt->dev_fd >= 0);
+
+ if (cxt->parent) {
+ int rc = fdisk_deassign_device(cxt->parent, nosync);
+
+ if (!rc)
+ rc = init_nested_from_parent(cxt, 0);
+ return rc;
+ }
+
+ if (cxt->readonly)
+ close(cxt->dev_fd);
+ else {
+ if (fsync(cxt->dev_fd) || close(cxt->dev_fd)) {
+ fdisk_warn(cxt, _("%s: close device failed"),
+ cxt->dev_path);
+ return -errno;
+ }
+
+ if (!nosync) {
+ fdisk_info(cxt, _("Syncing disks."));
+ sync();
+ }
+ }
+
+ free(cxt->dev_path);
+ cxt->dev_path = NULL;
+
+ cxt->dev_fd = -1;
+
+ return 0;
+}
+
+/**
+ * fdisk_is_readonly:
+ * @cxt: context
+ *
+ * Returns: 1 if device open readonly
+ */
+int fdisk_is_readonly(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->readonly;
+}
+
+/**
+ * fdisk_unref_context:
+ * @cxt: fdisk context
+ *
+ * Deallocates context struct.
+ */
+void fdisk_unref_context(struct fdisk_context *cxt)
+{
+ int i;
+
+ if (!cxt)
+ return;
+
+ cxt->refcount--;
+ if (cxt->refcount <= 0) {
+ DBG(CXT, ul_debugobj(cxt, "freeing context %p for %s", cxt, cxt->dev_path));
+
+ reset_context(cxt); /* this is sensitive to parent<->child relationship! */
+
+ /* deallocate label's private stuff */
+ for (i = 0; i < cxt->nlabels; i++) {
+ if (!cxt->labels[i])
+ continue;
+ if (cxt->labels[i]->op->free)
+ cxt->labels[i]->op->free(cxt->labels[i]);
+ else
+ free(cxt->labels[i]);
+ }
+
+ fdisk_unref_context(cxt->parent);
+ cxt->parent = NULL;
+
+ free(cxt);
+ }
+}
+
+
+/**
+ * fdisk_enable_details:
+ * @cxt: context
+ * @enable: true/flase
+ *
+ * Enables or disables "details" display mode. This function has effect to
+ * fdisk_partition_to_string() function.
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_enable_details(struct fdisk_context *cxt, int enable)
+{
+ assert(cxt);
+ cxt->display_details = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * fdisk_is_details:
+ * @cxt: context
+ *
+ * Returns: 1 if details are enabled
+ */
+int fdisk_is_details(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->display_details == 1;
+}
+
+/**
+ * fdisk_enable_listonly:
+ * @cxt: context
+ * @enable: true/flase
+ *
+ * Just list partition only, don't care about another details, mistakes, ...
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_enable_listonly(struct fdisk_context *cxt, int enable)
+{
+ assert(cxt);
+ cxt->listonly = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * fdisk_is_listonly:
+ * @cxt: context
+ *
+ * Returns: 1 if list-only mode enabled
+ */
+int fdisk_is_listonly(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->listonly == 1;
+}
+
+
+/**
+ * fdisk_set_unit:
+ * @cxt: context
+ * @str: "cylinder" or "sector".
+ *
+ * This is pure shit, unfortunately for example Sun addresses begin of the
+ * partition by cylinders...
+ *
+ * Returns: 0 on succes, <0 on error.
+ */
+int fdisk_set_unit(struct fdisk_context *cxt, const char *str)
+{
+ assert(cxt);
+
+ cxt->display_in_cyl_units = 0;
+
+ if (!str)
+ return 0;
+
+ if (strcmp(str, "cylinder") == 0 || strcmp(str, "cylinders") == 0)
+ cxt->display_in_cyl_units = 1;
+
+ else if (strcmp(str, "sector") == 0 || strcmp(str, "sectors") == 0)
+ cxt->display_in_cyl_units = 0;
+
+ DBG(CXT, ul_debugobj(cxt, "display unit: %s", fdisk_get_unit(cxt, 0)));
+ return 0;
+}
+
+/**
+ * fdisk_get_unit:
+ * @cxt: context
+ * @n: FDISK_PLURAL or FDISK_SINGULAR
+ *
+ * Returns: unit name.
+ */
+const char *fdisk_get_unit(struct fdisk_context *cxt, int n)
+{
+ assert(cxt);
+
+ if (fdisk_use_cylinders(cxt))
+ return P_("cylinder", "cylinders", n);
+ return P_("sector", "sectors", n);
+}
+
+/**
+ * fdisk_use_cylinders:
+ * @cxt: context
+ *
+ * Returns: 1 if user wants to display in cylinders.
+ */
+int fdisk_use_cylinders(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->display_in_cyl_units == 1;
+}
+
+/**
+ * fdisk_get_units_per_sector:
+ * @cxt: context
+ *
+ * This is necessary only for brain dead situations when we use "cylinders";
+ *
+ * Returns: number of "units" per sector, default is 1 if display unit is sector.
+ */
+unsigned int fdisk_get_units_per_sector(struct fdisk_context *cxt)
+{
+ assert(cxt);
+
+ if (fdisk_use_cylinders(cxt)) {
+ assert(cxt->geom.heads);
+ return cxt->geom.heads * cxt->geom.sectors;
+ }
+ return 1;
+}
+
+/**
+ * fdisk_get_optimal_iosize:
+ * @cxt: context
+ *
+ * The optimal I/O is optional and does not have to be provided by device,
+ * anyway libfdisk never returns zero. If the optimal I/O size is not provided
+ * then libfdisk returns minimal I/O size or sector size.
+ *
+ * Returns: optimal I/O size in bytes.
+ */
+unsigned long fdisk_get_optimal_iosize(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->optimal_io_size ? cxt->optimal_io_size : cxt->io_size;
+}
+
+/**
+ * fdisk_get_minimal_iosize:
+ * @cxt: context
+ *
+ * Returns: minimal I/O size in bytes
+ */
+unsigned long fdisk_get_minimal_iosize(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->min_io_size;
+}
+
+/**
+ * fdisk_get_physector_size:
+ * @cxt: context
+ *
+ * Returns: physical sector size in bytes
+ */
+unsigned long fdisk_get_physector_size(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->phy_sector_size;
+}
+
+/**
+ * fdisk_get_sector_size:
+ * @cxt: context
+ *
+ * Returns: logical sector size in bytes
+ */
+unsigned long fdisk_get_sector_size(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->sector_size;
+}
+
+/**
+ * fdisk_get_alignment_offset
+ * @cxt: context
+ *
+ * The alignment offset is offset between logical and physical sectors. For
+ * backward compatibility the first logical sector on 4K disks does no have to
+ * start on the same place like physical sectors.
+ *
+ * Returns: alignment offset in bytes
+ */
+unsigned long fdisk_get_alignment_offset(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->alignment_offset;
+}
+
+/**
+ * fdisk_get_grain_size:
+ * @cxt: context
+ *
+ * Returns: grain in bytes used to align partitions (usually 1MiB)
+ */
+unsigned long fdisk_get_grain_size(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->grain;
+}
+
+/**
+ * fdisk_get_first_lba:
+ * @cxt: context
+ *
+ * Returns: first possible LBA on disk for data partitions.
+ */
+fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->first_lba;
+}
+
+/**
+ * fdisk_set_first_lba:
+ * @cxt: fdisk context
+ * @lba: first possible logical sector for data
+ *
+ * It's strongly recommended to use the default library setting. The first LBA
+ * is always reseted by fdisk_assign_device(), fdisk_override_geometry()
+ * and fdisk_reset_alignment(). This is very low level function and library
+ * does not check if your setting makes any sense.
+ *
+ * This function is necessary only when you want to work with very unusual
+ * partition tables like GPT protective MBR or hybrid partition tables on
+ * bootable media where the first partition may start on very crazy offsets.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+ assert(cxt);
+ DBG(CXT, ul_debugobj(cxt, "setting first LBA from %ju to %ju",
+ (uintmax_t) cxt->first_lba, (uintmax_t) lba));
+ cxt->first_lba = lba;
+ return 0;
+}
+
+/**
+ * fdisk_get_last_lba:
+ * @cxt: fdisk context
+ *
+ * Note that the device has to be already assigned.
+ *
+ * Returns: last possible LBA on device
+ */
+fdisk_sector_t fdisk_get_last_lba(struct fdisk_context *cxt)
+{
+ return cxt->last_lba;
+}
+
+/**
+ * fdisk_set_last_lba:
+ * @cxt: fdisk context
+ * @lba: last possible logical sector
+ *
+ * It's strongly recommended to use the default library setting. The last LBA
+ * is always reseted by fdisk_assign_device(), fdisk_override_geometry() and
+ * fdisk_reset_alignment().
+ *
+ * The default is number of sectors on the device, but maybe modified by the
+ * current disklabel driver (for example GPT uses and of disk for backup
+ * header, so last_lba is smaller than total number of sectors).
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+fdisk_sector_t fdisk_set_last_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
+{
+ assert(cxt);
+
+ if (lba > cxt->total_sectors - 1 && lba < 1)
+ return -ERANGE;
+ cxt->last_lba = lba;
+ return 0;
+}
+
+
+/**
+ * fdisk_get_nsectors:
+ * @cxt: context
+ *
+ * Returns: size of the device in logical sectors.
+ */
+fdisk_sector_t fdisk_get_nsectors(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->total_sectors;
+}
+
+/**
+ * fdisk_get_devname:
+ * @cxt: context
+ *
+ * Returns: device name.
+ */
+const char *fdisk_get_devname(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->dev_path;
+}
+
+/**
+ * fdisk_get_devfd:
+ * @cxt: context
+ *
+ * Retruns: device file descriptor.
+ */
+int fdisk_get_devfd(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->dev_fd;
+}
+
+/**
+ * fdisk_get_geom_heads:
+ * @cxt: context
+ *
+ * Returns: number of geometry heads.
+ */
+unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->geom.heads;
+}
+/**
+ * fdisk_get_geom_sectors:
+ * @cxt: context
+ *
+ * Returns: number of geometry sectors.
+ */
+fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->geom.sectors;
+
+}
+
+/**
+ * fdisk_get_geom_cylinders:
+ * @cxt: context
+ *
+ * Returns: number of geometry cylinders
+ */
+fdisk_sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->geom.cylinders;
+}
+
+int fdisk_missing_geometry(struct fdisk_context *cxt)
+{
+ int rc;
+
+ assert(cxt);
+
+ if (!cxt || !cxt->label)
+ return 0;
+
+ rc = (fdisk_label_require_geometry(cxt->label) &&
+ (!cxt->geom.heads || !cxt->geom.sectors
+ || !cxt->geom.cylinders));
+
+ if (rc && !fdisk_is_listonly(cxt))
+ fdisk_warnx(cxt, _("Incomplete geometry setting."));
+
+ return rc;
+}
+
diff --git a/libblkid/libfdisk/src/dos.c b/libblkid/libfdisk/src/dos.c
new file mode 100644
index 000000000..2a067076e
--- /dev/null
+++ b/libblkid/libfdisk/src/dos.c
@@ -0,0 +1,2331 @@
+/*
+ *
+ * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
+ * 2012 Davidlohr Bueso <dave@gnu.org>
+ *
+ * This is re-written version for libfdisk, the original was fdiskdoslabel.c
+ * from util-linux fdisk.
+ */
+#include "c.h"
+#include "nls.h"
+#include "randutils.h"
+#include "pt-mbr.h"
+#include "strutils.h"
+
+#include "fdiskP.h"
+
+#include <ctype.h>
+
+#define MAXIMUM_PARTS 60
+#define ACTIVE_FLAG 0x80
+
+/**
+ * SECTION: dos
+ * @title: DOS (MBR)
+ * @short_description: disk label specific functions
+ *
+ */
+
+
+#define IS_EXTENDED(i) \
+ ((i) == MBR_DOS_EXTENDED_PARTITION \
+ || (i) == MBR_W95_EXTENDED_PARTITION \
+ || (i) == MBR_LINUX_EXTENDED_PARTITION)
+
+/*
+ * per partition table entry data
+ *
+ * The four primary partitions have the same sectorbuffer
+ * and have NULL ex_entry.
+ *
+ * Each logical partition table entry has two pointers, one for the
+ * partition and one link to the next one.
+ */
+struct pte {
+ struct dos_partition *pt_entry; /* on-disk MBR entry */
+ struct dos_partition *ex_entry; /* on-disk EBR entry */
+ fdisk_sector_t offset; /* disk sector number */
+ unsigned char *sectorbuffer; /* disk sector contents */
+
+ unsigned int changed : 1,
+ private_sectorbuffer : 1;
+};
+
+/*
+ * in-memory fdisk GPT stuff
+ */
+struct fdisk_dos_label {
+ struct fdisk_label head; /* generic part */
+
+ struct pte ptes[MAXIMUM_PARTS]; /* partition */
+ fdisk_sector_t ext_offset; /* start of the ext.partition */
+ size_t ext_index; /* ext.partition index (if ext_offset is set) */
+ unsigned int compatible : 1, /* is DOS compatible? */
+ non_pt_changed : 1; /* MBR, but no PT changed */
+};
+
+/*
+ * Partition types
+ */
+static struct fdisk_parttype dos_parttypes[] = {
+ #include "pt-mbr-partnames.h"
+};
+
+#define set_hsc(h,s,c,sector) { \
+ s = sector % cxt->geom.sectors + 1; \
+ sector /= cxt->geom.sectors; \
+ h = sector % cxt->geom.heads; \
+ sector /= cxt->geom.heads; \
+ c = sector & 0xff; \
+ s |= (sector >> 2) & 0xc0; \
+ }
+
+
+#define sector(s) ((s) & 0x3f)
+#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
+
+#define alignment_required(_x) ((_x)->grain != (_x)->sector_size)
+
+#define is_dos_compatible(_x) \
+ (fdisk_is_label(_x, DOS) && \
+ fdisk_dos_is_compatible(fdisk_get_label(_x, NULL)))
+
+#define cround(c, n) fdisk_cround(c, n)
+
+
+static inline struct fdisk_dos_label *self_label(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ return (struct fdisk_dos_label *) cxt->label;
+}
+
+static inline struct pte *self_pte(struct fdisk_context *cxt, size_t i)
+{
+ struct fdisk_dos_label *l = self_label(cxt);
+
+ if (i >= ARRAY_SIZE(l->ptes))
+ return NULL;
+
+ return &l->ptes[i];
+}
+
+static inline struct dos_partition *self_partition(
+ struct fdisk_context *cxt,
+ size_t i)
+{
+ struct pte *pe = self_pte(cxt, i);
+ return pe ? pe->pt_entry : NULL;
+}
+
+struct dos_partition *fdisk_dos_get_partition(
+ struct fdisk_context *cxt,
+ size_t i)
+{
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ return self_partition(cxt, i);
+}
+
+static struct fdisk_parttype *dos_partition_parttype(
+ struct fdisk_context *cxt,
+ struct dos_partition *p)
+{
+ struct fdisk_parttype *t
+ = fdisk_label_get_parttype_from_code(cxt->label, p->sys_ind);
+ return t ? : fdisk_new_unknown_parttype(p->sys_ind, NULL);
+}
+
+/*
+ * Linux kernel cares about partition size only. Things like
+ * partition type or so are completely irrelevant -- kzak Nov-2013
+ */
+static int is_used_partition(struct dos_partition *p)
+{
+ return p && dos_partition_get_size(p) != 0;
+}
+
+static void partition_set_changed(
+ struct fdisk_context *cxt,
+ size_t i,
+ int changed)
+{
+ struct pte *pe = self_pte(cxt, i);
+
+ if (!pe)
+ return;
+
+ DBG(LABEL, ul_debug("DOS: setting %zu partition to %s", i,
+ changed ? "changed" : "unchanged"));
+
+ pe->changed = changed ? 1 : 0;
+ if (changed)
+ fdisk_label_set_changed(cxt->label, 1);
+}
+
+static fdisk_sector_t get_abs_partition_start(struct pte *pe)
+{
+ assert(pe);
+ assert(pe->pt_entry);
+
+ return pe->offset + dos_partition_get_start(pe->pt_entry);
+}
+
+static fdisk_sector_t get_abs_partition_end(struct pte *pe)
+{
+ fdisk_sector_t size;
+
+ assert(pe);
+ assert(pe->pt_entry);
+
+ size = dos_partition_get_size(pe->pt_entry);
+ return get_abs_partition_start(pe) + size - (size ? 1 : 0);
+}
+
+static int is_cleared_partition(struct dos_partition *p)
+{
+ return !(!p || p->boot_ind || p->bh || p->bs || p->bc ||
+ p->sys_ind || p->eh || p->es || p->ec ||
+ dos_partition_get_start(p) || dos_partition_get_size(p));
+}
+
+static int get_partition_unused_primary(struct fdisk_context *cxt,
+ struct fdisk_partition *pa,
+ size_t *partno)
+{
+ size_t org, n;
+ int rc;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(partno);
+
+ org = cxt->label->nparts_max;
+
+ cxt->label->nparts_max = 4;
+ rc = fdisk_partition_next_partno(pa, cxt, &n);
+ cxt->label->nparts_max = org;
+
+ if (rc == 1) {
+ fdisk_info(cxt, _("All primary partitions have been defined already."));
+ rc = -1;
+ } else if (rc == 0)
+ *partno = n;
+ return rc;
+}
+
+static int seek_sector(struct fdisk_context *cxt, fdisk_sector_t secno)
+{
+ off_t offset = (off_t) secno * cxt->sector_size;
+
+ return lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1 ? -errno : 0;
+}
+
+static int read_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
+ unsigned char *buf)
+{
+ int rc = seek_sector(cxt, secno);
+ ssize_t r;
+
+ if (rc < 0)
+ return rc;
+
+ r = read(cxt->dev_fd, buf, cxt->sector_size);
+ if (r == (ssize_t) cxt->sector_size)
+ return 0;
+ if (r < 0)
+ return -errno;
+ return -1;
+}
+
+/* Allocate a buffer and read a partition table sector */
+static int read_pte(struct fdisk_context *cxt, size_t pno, fdisk_sector_t offset)
+{
+ int rc;
+ unsigned char *buf;
+ struct pte *pe = self_pte(cxt, pno);
+
+ buf = calloc(1, cxt->sector_size);
+ if (!buf)
+ return -ENOMEM;
+
+ DBG(LABEL, ul_debug("DOS: reading EBR %zu: offset=%ju, buffer=%p",
+ pno, (uintmax_t) offset, buf));
+
+ pe->offset = offset;
+ pe->sectorbuffer = buf;
+ pe->private_sectorbuffer = 1;
+
+ rc = read_sector(cxt, offset, pe->sectorbuffer);
+ if (rc) {
+ fdisk_warn(cxt, _("Failed to read extended partition table "
+ "(offset=%ju)"), (uintmax_t) offset);
+ return rc;
+ }
+
+ pe->changed = 0;
+ pe->pt_entry = pe->ex_entry = NULL;
+ return 0;
+}
+
+
+static void clear_partition(struct dos_partition *p)
+{
+ if (!p)
+ return;
+ p->boot_ind = 0;
+ p->bh = 0;
+ p->bs = 0;
+ p->bc = 0;
+ p->sys_ind = 0;
+ p->eh = 0;
+ p->es = 0;
+ p->ec = 0;
+ dos_partition_set_start(p,0);
+ dos_partition_set_size(p,0);
+}
+
+static void dos_init(struct fdisk_context *cxt)
+{
+ struct fdisk_dos_label *l = self_label(cxt);
+ size_t i;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ DBG(LABEL, ul_debug("DOS: initialize, first sector buffer %p", cxt->firstsector));
+
+ cxt->label->nparts_max = 4; /* default, unlimited number of logical */
+
+ l->ext_index = 0;
+ l->ext_offset = 0;
+ l->non_pt_changed = 0;
+
+ memset(l->ptes, 0, sizeof(l->ptes));
+
+ for (i = 0; i < 4; i++) {
+ struct pte *pe = self_pte(cxt, i);
+
+ pe->pt_entry = mbr_get_partition(cxt->firstsector, i);
+ pe->ex_entry = NULL;
+ pe->offset = 0;
+ pe->sectorbuffer = cxt->firstsector;
+ pe->private_sectorbuffer = 0;
+ pe->changed = 0;
+ }
+
+ if (fdisk_is_listonly(cxt))
+ return;
+ /*
+ * Various warnings...
+ */
+ if (fdisk_missing_geometry(cxt))
+ fdisk_warnx(cxt, _("You can set geometry from the extra functions menu."));
+
+ if (is_dos_compatible(cxt)) {
+ fdisk_warnx(cxt, _("DOS-compatible mode is deprecated."));
+
+ if (cxt->sector_size != cxt->phy_sector_size)
+ fdisk_info(cxt, _(
+ "The device presents a logical sector size that is smaller than "
+ "the physical sector size. Aligning to a physical sector (or optimal "
+ "I/O) size boundary is recommended, or performance may be impacted."));
+ }
+
+ if (fdisk_use_cylinders(cxt))
+ fdisk_warnx(cxt, _("Cylinders as display units are deprecated."));
+
+ if (cxt->total_sectors > UINT_MAX) {
+ uint64_t bytes = cxt->total_sectors * cxt->sector_size;
+ char *szstr = size_to_human_string(SIZE_SUFFIX_SPACE
+ | SIZE_SUFFIX_3LETTER, bytes);
+ fdisk_warnx(cxt,
+ _("The size of this disk is %s (%ju bytes). DOS "
+ "partition table format can not be used on drives for "
+ "volumes larger than %lu bytes for %lu-byte "
+ "sectors. Use GUID partition table format (GPT)."),
+ szstr, bytes,
+ UINT_MAX * cxt->sector_size,
+ cxt->sector_size);
+ free(szstr);
+ }
+}
+
+/* callback called by libfdisk */
+static void dos_deinit(struct fdisk_label *lb)
+{
+ size_t i;
+ struct fdisk_dos_label *l = (struct fdisk_dos_label *) lb;
+
+ for (i = 0; i < ARRAY_SIZE(l->ptes); i++) {
+ struct pte *pe = &l->ptes[i];
+
+ if (pe->private_sectorbuffer && pe->sectorbuffer) {
+ DBG(LABEL, ul_debug("DOS: freeing pte %zu sector buffer %p",
+ i, pe->sectorbuffer));
+ free(pe->sectorbuffer);
+ }
+ pe->sectorbuffer = NULL;
+ pe->private_sectorbuffer = 0;
+ }
+
+ memset(l->ptes, 0, sizeof(l->ptes));
+}
+
+static void reset_pte(struct pte *pe)
+{
+ assert(pe);
+
+ if (pe->private_sectorbuffer) {
+ DBG(LABEL, ul_debug(" --> freeing pte sector buffer %p",
+ pe->sectorbuffer));
+ free(pe->sectorbuffer);
+ }
+ memset(pe, 0, sizeof(struct pte));
+}
+
+static int dos_delete_partition(struct fdisk_context *cxt, size_t partnum)
+{
+ struct fdisk_dos_label *l;
+ struct pte *pe;
+ struct dos_partition *p;
+ struct dos_partition *q;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ pe = self_pte(cxt, partnum);
+ if (!pe)
+ return -EINVAL;
+
+ DBG(LABEL, ul_debug("DOS: delete partiton %zu (max=%zu)", partnum,
+ cxt->label->nparts_max));
+
+ l = self_label(cxt);
+ p = pe->pt_entry;
+ q = pe->ex_entry;
+
+ /* Note that for the fifth partition (partnum == 4) we don't actually
+ decrement partitions. */
+ if (partnum < 4) {
+ DBG(LABEL, ul_debug("--> delete primary"));
+ if (IS_EXTENDED(p->sys_ind) && partnum == l->ext_index) {
+ cxt->label->nparts_max = 4;
+ l->ptes[l->ext_index].ex_entry = NULL;
+ l->ext_offset = 0;
+ l->ext_index = 0;
+ }
+ partition_set_changed(cxt, partnum, 1);
+ clear_partition(p);
+ } else if (!q->sys_ind && partnum > 4) {
+ DBG(LABEL, ul_debug("--> delete logical [last in the chain]"));
+ reset_pte(&l->ptes[partnum]);
+ --cxt->label->nparts_max;
+ --partnum;
+ /* clear link to deleted partition */
+ clear_partition(l->ptes[partnum].ex_entry);
+ partition_set_changed(cxt, partnum, 1);
+ } else {
+ DBG(LABEL, ul_debug("--> delete logical [move down]"));
+ if (partnum > 4) {
+ DBG(LABEL, ul_debug(" --> delete %zu logical link", partnum));
+ p = l->ptes[partnum - 1].ex_entry;
+ *p = *q;
+ dos_partition_set_start(p, dos_partition_get_start(q));
+ dos_partition_set_size(p, dos_partition_get_size(q));
+ partition_set_changed(cxt, partnum - 1, 1);
+
+ } else if (cxt->label->nparts_max > 5) {
+ DBG(LABEL, ul_debug(" --> delete first logical link"));
+ pe = &l->ptes[5]; /* second logical */
+
+ if (pe->pt_entry) /* prevent SEGFAULT */
+ dos_partition_set_start(pe->pt_entry,
+ get_abs_partition_start(pe) -
+ l->ext_offset);
+ pe->offset = l->ext_offset;
+ partition_set_changed(cxt, 5, 1);
+ }
+
+ if (cxt->label->nparts_max > 5) {
+ DBG(LABEL, ul_debug(" --> move ptes"));
+ cxt->label->nparts_max--;
+ reset_pte(&l->ptes[partnum]);
+ while (partnum < cxt->label->nparts_max) {
+ DBG(LABEL, ul_debug(" --> moving pte %zu <-- %zu", partnum, partnum + 1));
+ l->ptes[partnum] = l->ptes[partnum + 1];
+ partnum++;
+ }
+ memset(&l->ptes[partnum], 0, sizeof(struct pte));
+ } else {
+ DBG(LABEL, ul_debug(" --> the only logical: clear only"));
+ clear_partition(l->ptes[partnum].pt_entry);
+ cxt->label->nparts_max--;
+
+ if (partnum == 4) {
+ DBG(LABEL, ul_debug(" --> clear last logical"));
+ reset_pte(&l->ptes[partnum]);
+ partition_set_changed(cxt, l->ext_index, 1);
+ }
+ }
+ }
+
+ fdisk_label_set_changed(cxt->label, 1);
+ return 0;
+}
+
+static void read_extended(struct fdisk_context *cxt, size_t ext)
+{
+ size_t i;
+ struct pte *pex, *pe;
+ struct dos_partition *p, *q;
+ struct fdisk_dos_label *l = self_label(cxt);
+
+ l->ext_index = ext;
+ pex = self_pte(cxt, ext);
+ pex->ex_entry = pex->pt_entry;
+
+ p = pex->pt_entry;
+ if (!dos_partition_get_start(p)) {
+ fdisk_warnx(cxt, _("Bad offset in primary extended partition."));
+ return;
+ }
+
+ DBG(LABEL, ul_debug("DOS: Reading extended %zu", ext));
+
+ while (IS_EXTENDED (p->sys_ind)) {
+ pe = self_pte(cxt, cxt->label->nparts_max);
+
+ if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
+ /* This is not a Linux restriction, but
+ this program uses arrays of size MAXIMUM_PARTS.
+ Do not try to `improve' this test. */
+ struct pte *pre = self_pte(cxt,
+ cxt->label->nparts_max - 1);
+ fdisk_warnx(cxt,
+ _("Omitting partitions after #%zu. They will be deleted "
+ "if you save this partition table."),
+ cxt->label->nparts_max);
+
+ clear_partition(pre->ex_entry);
+ partition_set_changed(cxt,
+ cxt->label->nparts_max - 1, 1);
+ return;
+ }
+
+ if (read_pte(cxt, cxt->label->nparts_max, l->ext_offset +
+ dos_partition_get_start(p)))
+ return;
+
+ if (!l->ext_offset)
+ l->ext_offset = dos_partition_get_start(p);
+
+ assert(pe->sectorbuffer);
+ q = p = mbr_get_partition(pe->sectorbuffer, 0);
+
+ for (i = 0; i < 4; i++, p++) {
+ if (!dos_partition_get_size(p))
+ continue;
+
+ if (IS_EXTENDED (p->sys_ind)) {
+ if (pe->ex_entry)
+ fdisk_warnx(cxt, _(
+ "Extra link pointer in partition "
+ "table %zu."),
+ cxt->label->nparts_max + 1);
+ else
+ pe->ex_entry = p;
+ } else if (p->sys_ind) {
+ if (pe->pt_entry)
+ fdisk_warnx(cxt, _(
+ "Ignoring extra data in partition "
+ "table %zu."),
+ cxt->label->nparts_max + 1);
+ else
+ pe->pt_entry = p;
+ }
+ }
+
+ /* very strange code here... */
+ if (!pe->pt_entry) {
+ if (q != pe->ex_entry)
+ pe->pt_entry = q;
+ else
+ pe->pt_entry = q + 1;
+ }
+ if (!pe->ex_entry) {
+ if (q != pe->pt_entry)
+ pe->ex_entry = q;
+ else
+ pe->ex_entry = q + 1;
+ }
+
+ p = pe->ex_entry;
+ cxt->label->nparts_cur = ++cxt->label->nparts_max;
+
+ DBG(LABEL, ul_debug("DOS: EBR[offset=%ju]: link: type=%x, start=%u, size=%u; "
+ " data: type=%x, start=%u, size=%u",
+ (uintmax_t) pe->offset,
+ pe->ex_entry->sys_ind,
+ dos_partition_get_start(pe->ex_entry),
+ dos_partition_get_size(pe->ex_entry),
+ pe->pt_entry->sys_ind,
+ dos_partition_get_start(pe->pt_entry),
+ dos_partition_get_size(pe->pt_entry)));
+
+ }
+
+ /* remove last empty EBR */
+ pe = self_pte(cxt, cxt->label->nparts_max - 1);
+ if (is_cleared_partition(pe->ex_entry) &&
+ is_cleared_partition(pe->pt_entry)) {
+ DBG(LABEL, ul_debug("DOS: EBR[offset=%ju]: empty, remove", (uintmax_t) pe->offset));
+ reset_pte(pe);
+ cxt->label->nparts_max--;
+ cxt->label->nparts_cur--;
+ }
+
+ /* remove empty links */
+ remove:
+ q = self_partition(cxt, 4);
+ for (i = 4; i < cxt->label->nparts_max; i++) {
+ p = self_partition(cxt, i);
+
+ if (!dos_partition_get_size(p) &&
+ (cxt->label->nparts_max > 5 || q->sys_ind)) {
+ fdisk_info(cxt, _("omitting empty partition (%zu)"), i+1);
+ dos_delete_partition(cxt, i);
+ goto remove; /* numbering changed */
+ }
+ }
+
+ DBG(LABEL, ul_debug("DOS: nparts_max: %zu", cxt->label->nparts_max));
+}
+
+static int dos_get_disklabel_id(struct fdisk_context *cxt, char **id)
+{
+ unsigned int num;
+
+ assert(cxt);
+ assert(id);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ num = mbr_get_id(cxt->firstsector);
+ if (asprintf(id, "0x%08x", num) > 0)
+ return 0;
+
+ return -ENOMEM;
+}
+
+static int dos_create_disklabel(struct fdisk_context *cxt)
+{
+ unsigned int id = 0;
+ int rc, has_id = 0;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ DBG(LABEL, ul_debug("DOS: creating new disklabel"));
+
+ if (cxt->script) {
+ char *end = NULL;
+ const char *s = fdisk_script_get_header(cxt->script, "label-id");
+
+ if (s) {
+ errno = 0;
+ id = strtol(s, &end, 16);
+ if (!errno && end && s < end)
+ has_id = 1;
+ }
+ }
+
+ /* random disk signature */
+ if (!has_id)
+ random_get_bytes(&id, sizeof(id));
+
+ dos_init(cxt);
+ rc = fdisk_init_firstsector_buffer(cxt);
+ if (rc)
+ return rc;
+ fdisk_label_set_changed(cxt->label, 1);
+
+ /* Generate an MBR ID for this disk */
+ mbr_set_id(cxt->firstsector, id);
+
+ /* Put MBR signature */
+ mbr_set_magic(cxt->firstsector);
+
+ fdisk_info(cxt, _("Created a new DOS disklabel with disk "
+ "identifier 0x%08x."), id);
+ return 0;
+}
+
+static int dos_set_disklabel_id(struct fdisk_context *cxt)
+{
+ char *end = NULL, *str = NULL;
+ unsigned int id, old;
+ struct fdisk_dos_label *l;
+ int rc;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ DBG(LABEL, ul_debug("DOS: setting Id"));
+
+ l = self_label(cxt);
+ old = mbr_get_id(cxt->firstsector);
+ rc = fdisk_ask_string(cxt,
+ _("Enter the new disk identifier"), &str);
+ if (rc)
+ return rc;
+
+ errno = 0;
+ id = strtoul(str, &end, 0);
+ if (errno || str == end || (end && *end)) {
+ fdisk_warnx(cxt, _("Incorrect value."));
+ return -EINVAL;
+ }
+
+
+ mbr_set_id(cxt->firstsector, id);
+ l->non_pt_changed = 1;
+ fdisk_label_set_changed(cxt->label, 1);
+
+ fdisk_info(cxt, _("Disk identifier changed from 0x%08x to 0x%08x."),
+ old, id);
+ return 0;
+}
+
+static void get_partition_table_geometry(struct fdisk_context *cxt,
+ unsigned int *ph, unsigned int *ps)
+{
+ unsigned char *bufp = cxt->firstsector;
+ struct dos_partition *p;
+ int i, h, s, hh, ss;
+ int first = 1;
+ int bad = 0;
+
+ hh = ss = 0;
+ for (i = 0; i < 4; i++) {
+ p = mbr_get_partition(bufp, i);
+ if (p->sys_ind != 0) {
+ h = p->eh + 1;
+ s = (p->es & 077);
+ if (first) {
+ hh = h;
+ ss = s;
+ first = 0;
+ } else if (hh != h || ss != s)
+ bad = 1;
+ }
+ }
+
+ if (!first && !bad) {
+ *ph = hh;
+ *ps = ss;
+ }
+
+ DBG(LABEL, ul_debug("DOS PT geometry: heads=%u, sectors=%u", *ph, *ps));
+}
+
+static int dos_reset_alignment(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ /* overwrite necessary stuff by DOS deprecated stuff */
+ if (is_dos_compatible(cxt)) {
+ DBG(LABEL, ul_debug("DOS: reseting alignemnt for DOS-comaptiblem PT"));
+ if (cxt->geom.sectors)
+ cxt->first_lba = cxt->geom.sectors; /* usually 63 */
+
+ cxt->grain = cxt->sector_size; /* usually 512 */
+ }
+
+ return 0;
+}
+
+/* TODO: move to include/pt-dos.h and share with libblkid */
+#define AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1"
+#define AIX_MAGIC_STRLEN (sizeof(AIX_MAGIC_STRING) - 1)
+
+static int dos_probe_label(struct fdisk_context *cxt)
+{
+ size_t i;
+ unsigned int h = 0, s = 0;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ /* ignore disks with AIX magic number */
+ if (memcmp(cxt->firstsector, AIX_MAGIC_STRING, AIX_MAGIC_STRLEN) == 0)
+ return 0;
+
+ if (!mbr_is_valid_magic(cxt->firstsector))
+ return 0;
+
+ dos_init(cxt);
+
+ get_partition_table_geometry(cxt, &h, &s);
+ if (h && s) {
+ cxt->geom.heads = h;
+ cxt->geom.sectors = s;
+ }
+
+ for (i = 0; i < 4; i++) {
+ struct pte *pe = self_pte(cxt, i);
+
+ if (is_used_partition(pe->pt_entry))
+ cxt->label->nparts_cur++;
+
+ if (IS_EXTENDED (pe->pt_entry->sys_ind)) {
+ if (cxt->label->nparts_max != 4)
+ fdisk_warnx(cxt, _(
+ "Ignoring extra extended partition %zu"),
+ i + 1);
+ else
+ read_extended(cxt, i);
+ }
+ }
+
+ for (i = 3; i < cxt->label->nparts_max; i++) {
+ struct pte *pe = self_pte(cxt, i);
+ struct fdisk_dos_label *l = self_label(cxt);
+
+ if (!mbr_is_valid_magic(pe->sectorbuffer)) {
+ fdisk_info(cxt, _(
+ "Invalid flag 0x%02x%02x of EBR (for partition %zu) will "
+ "be corrected by w(rite)."),
+ pe->sectorbuffer[510],
+ pe->sectorbuffer[511],
+ i + 1);
+ partition_set_changed(cxt, i, 1);
+
+ /* mark also extended as changed to update the first EBR
+ * in situation that there is no logical partitions at all */
+ partition_set_changed(cxt, l->ext_index, 1);
+ }
+ }
+
+ return 1;
+}
+
+static void set_partition(struct fdisk_context *cxt,
+ int i, int doext, fdisk_sector_t start,
+ fdisk_sector_t stop, int sysid, int boot)
+{
+ struct pte *pe = self_pte(cxt, i);
+ struct dos_partition *p;
+ fdisk_sector_t offset;
+
+ assert(!FDISK_IS_UNDEF(start));
+ assert(!FDISK_IS_UNDEF(stop));
+
+ if (doext) {
+ struct fdisk_dos_label *l = self_label(cxt);
+ p = pe->ex_entry;
+ offset = l->ext_offset;
+ } else {
+ p = pe->pt_entry;
+ offset = pe->offset;
+ }
+
+ DBG(LABEL, ul_debug("DOS: setting partition %d%s, offset=%zu, start=%zu, stop=%zu, sysid=%02x",
+ i, doext ? " [extended]" : "",
+ (size_t) offset,
+ (size_t) (start - offset),
+ (size_t) (stop - start + 1),
+ sysid));
+
+ p->boot_ind = boot ? ACTIVE_FLAG : 0;
+ p->sys_ind = sysid;
+ dos_partition_set_start(p, start - offset);
+ dos_partition_set_size(p, stop - start + 1);
+
+ if (is_dos_compatible(cxt) && (start/(cxt->geom.sectors*cxt->geom.heads) > 1023))
+ start = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
+ set_hsc(p->bh, p->bs, p->bc, start);
+ if (is_dos_compatible(cxt) && (stop/(cxt->geom.sectors*cxt->geom.heads) > 1023))
+ stop = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
+ set_hsc(p->eh, p->es, p->ec, stop);
+ partition_set_changed(cxt, i, 1);
+}
+
+static fdisk_sector_t get_unused_start(struct fdisk_context *cxt,
+ int part_n, fdisk_sector_t start,
+ fdisk_sector_t first[], fdisk_sector_t last[])
+{
+ size_t i;
+
+ for (i = 0; i < cxt->label->nparts_max; i++) {
+ fdisk_sector_t lastplusoff;
+ struct pte *pe = self_pte(cxt, i);
+
+ if (start == pe->offset)
+ start += cxt->first_lba;
+ lastplusoff = last[i] + ((part_n < 4) ? 0 : cxt->first_lba);
+ if (start >= first[i] && start <= lastplusoff)
+ start = lastplusoff + 1;
+ }
+
+ return start;
+}
+
+static void fill_bounds(struct fdisk_context *cxt,
+ fdisk_sector_t *first, fdisk_sector_t *last)
+{
+ size_t i;
+ struct pte *pe = self_pte(cxt, 0);
+ struct dos_partition *p;
+
+ for (i = 0; i < cxt->label->nparts_max; pe++,i++) {
+ p = pe->pt_entry;
+ if (is_cleared_partition(p) || IS_EXTENDED (p->sys_ind)) {
+ first[i] = 0xffffffff;
+ last[i] = 0;
+ } else {
+ first[i] = get_abs_partition_start(pe);
+ last[i] = get_abs_partition_end(pe);
+ }
+ }
+}
+
+static int get_start_from_user( struct fdisk_context *cxt,
+ fdisk_sector_t *start,
+ fdisk_sector_t low,
+ fdisk_sector_t dflt,
+ fdisk_sector_t limit,
+ struct fdisk_partition *pa)
+{
+ assert(start);
+
+ /* try to use tepmlate from 'pa' */
+ if (pa && pa->start_follow_default)
+ *start = dflt;
+
+ else if (pa && fdisk_partition_has_start(pa)) {
+ DBG(LABEL, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
+ (uintmax_t) pa->start, (uintmax_t) low, (uintmax_t) limit));
+ *start = pa->start;
+ if (*start < low || *start > limit) {
+ fdisk_warnx(cxt, _("Start sector %ju out of range."),
+ (uintmax_t) *start);
+ return -ERANGE;
+ }
+ } else {
+ /* ask user by dialog */
+ struct fdisk_ask *ask = fdisk_new_ask();
+ int rc;
+
+ if (!ask)
+ return -ENOMEM;
+ fdisk_ask_set_query(ask,
+ fdisk_use_cylinders(cxt) ?
+ _("First cylinder") : _("First sector"));
+ fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+ fdisk_ask_number_set_low(ask, fdisk_cround(cxt, low));
+ fdisk_ask_number_set_default(ask, fdisk_cround(cxt, dflt));
+ fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
+
+ rc = fdisk_do_ask(cxt, ask);
+ *start = fdisk_ask_number_get_result(ask);
+ fdisk_unref_ask(ask);
+ if (rc)
+ return rc;
+ if (fdisk_use_cylinders(cxt)) {
+ *start = (*start - 1)
+ * fdisk_get_units_per_sector(cxt);
+ if (*start < low)
+ *start = low;
+ }
+ }
+
+ DBG(LABEL, ul_debug("DOS: start is %ju", (uintmax_t) *start));
+ return 0;
+}
+
+static fdisk_sector_t get_possible_last(struct fdisk_context *cxt, size_t n)
+{
+ fdisk_sector_t limit;
+
+ if (n >= 4) {
+ /* logical partitions */
+ struct fdisk_dos_label *l = self_label(cxt);
+ struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
+
+ if (!ext_pe)
+ return 0;
+ limit = get_abs_partition_end(ext_pe);
+ } else {
+ /* primary partitions */
+ if (fdisk_use_cylinders(cxt) || !cxt->total_sectors)
+ limit = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
+ else
+ limit = cxt->total_sectors - 1;
+
+ if (limit > UINT_MAX)
+ limit = UINT_MAX;
+ }
+
+ DBG(LABEL, ul_debug("DOS: last possible sector for #%zu is %ju",
+ n, (uintmax_t) limit));
+ return limit;
+}
+
+/* returns last free sector for area addressed by @start, the first[] and
+ * last[] are fill_bounds() results */
+static fdisk_sector_t get_unused_last(struct fdisk_context *cxt, size_t n,
+ fdisk_sector_t start,
+ fdisk_sector_t first[], fdisk_sector_t last[])
+{
+ size_t i;
+ fdisk_sector_t limit = get_possible_last(cxt, n);
+
+ for (i = 0; i < cxt->label->nparts_max; i++) {
+ struct pte *pe = self_pte(cxt, i);
+
+ if (start < pe->offset && limit >= pe->offset)
+ limit = pe->offset - 1;
+ if (start < first[i] && limit >= first[i])
+ limit = first[i] - 1;
+ }
+
+ DBG(LABEL, ul_debug("DOS: unused sector for #%zu is %ju",
+ n, (uintmax_t) limit));
+ return limit;
+}
+
+static int add_partition(struct fdisk_context *cxt, size_t n,
+ struct fdisk_partition *pa)
+{
+ int sys, read = 0, rc, isrel = 0;
+ size_t i;
+ struct fdisk_dos_label *l = self_label(cxt);
+ struct dos_partition *p = self_partition(cxt, n);
+ struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
+
+ fdisk_sector_t start, stop = 0, limit, temp,
+ first[cxt->label->nparts_max],
+ last[cxt->label->nparts_max];
+
+ DBG(LABEL, ul_debug("DOS: adding partition %zu", n));
+
+ sys = pa && pa->type ? pa->type->code : MBR_LINUX_DATA_PARTITION;
+
+ if (is_used_partition(p)) {
+ fdisk_warnx(cxt, _("Partition %zu is already defined. "
+ "Delete it before re-adding it."),
+ n + 1);
+ return -EINVAL;
+ }
+ fill_bounds(cxt, first, last);
+ limit = get_possible_last(cxt, n);
+
+ if (n < 4) {
+ if (cxt->parent && fdisk_is_label(cxt->parent, GPT))
+ start = 1; /* Bad boy modifies hybrid MBR */
+ else {
+ if (cxt->script && pa && fdisk_partition_has_start(pa)
+ && pa->start < cxt->first_lba
+ && pa->start >= 1)
+ fdisk_set_first_lba(cxt, 1);
+
+ start = cxt->first_lba;
+ }
+
+ if (l->ext_offset) {
+ assert(ext_pe);
+ first[l->ext_index] = l->ext_offset;
+ last[l->ext_index] = get_abs_partition_end(ext_pe);
+ }
+ } else {
+ assert(ext_pe);
+
+ if (cxt->script && pa && fdisk_partition_has_start(pa)
+ && pa->start >= l->ext_offset
+ && pa->start < l->ext_offset + cxt->first_lba)
+ fdisk_set_first_lba(cxt, 1);
+
+ start = l->ext_offset + cxt->first_lba;
+ }
+
+ if (fdisk_use_cylinders(cxt))
+ for (i = 0; i < cxt->label->nparts_max; i++) {
+ first[i] = (fdisk_cround(cxt, first[i]) - 1)
+ * fdisk_get_units_per_sector(cxt);
+ }
+
+ /*
+ * Ask for first sector
+ */
+ do {
+ fdisk_sector_t dflt, aligned;
+
+ temp = start;
+ dflt = start = get_unused_start(cxt, n, start, first, last);
+
+ if (n >= 4 && pa && fdisk_partition_has_start(pa) && cxt->script
+ && cxt->first_lba > 1
+ && temp == start - cxt->first_lba) {
+ fdisk_set_first_lba(cxt, 1);
+ start = pa->start;
+ }
+
+ /* the default sector should be aligned and unused */
+ do {
+ aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit);
+ dflt = get_unused_start(cxt, n, aligned, first, last);
+ } while (dflt != aligned && dflt > aligned && dflt < limit);
+
+ if (dflt >= limit)
+ dflt = start;
+ if (start > limit)
+ break;
+ if (start >= temp + fdisk_get_units_per_sector(cxt)
+ && read) {
+ fdisk_info(cxt, _("Sector %llu is already allocated."),
+ temp);
+ temp = start;
+ read = 0;
+ if (pa && (fdisk_partition_has_start(pa) ||
+ pa->start_follow_default))
+ break;
+ }
+
+ if (!read && start == temp) {
+ rc = get_start_from_user(cxt, &start, temp, dflt, limit, pa);
+ if (rc)
+ return rc;
+ read = 1;
+ }
+ } while (start != temp || !read);
+
+ if (n == 4) {
+ /* The first EBR is stored at begin of the extended partition */
+ struct pte *pe = self_pte(cxt, n);
+ pe->offset = l->ext_offset;
+
+ } else if (n > 4) {
+ /* The second (and another) EBR */
+ struct pte *pe = self_pte(cxt, n);
+
+ pe->offset = start - cxt->first_lba;
+ if (pe->offset == l->ext_offset) { /* must be corrected */
+ pe->offset++;
+ if (cxt->first_lba == 1)
+ start++;
+ }
+ }
+
+ limit = get_unused_last(cxt, n, start, first, last);
+
+ if (start > limit) {
+ fdisk_info(cxt, _("No free sectors available."));
+ if (n > 4)
+ cxt->label->nparts_max--;
+ return -ENOSPC;
+ }
+
+ /*
+ * Ask for last sector
+ */
+ if (fdisk_cround(cxt, start) == fdisk_cround(cxt, limit))
+ stop = limit;
+ else if (pa && pa->end_follow_default)
+ stop = limit;
+ else if (pa && fdisk_partition_has_size(pa)) {
+ stop = start + pa->size - 1;
+ isrel = pa->size_explicit ? 0 : 1;
+ } else {
+ /* ask user by dialog */
+ struct fdisk_ask *ask = fdisk_new_ask();
+
+ if (!ask)
+ return -ENOMEM;
+ fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+
+ if (fdisk_use_cylinders(cxt)) {
+ fdisk_ask_set_query(ask, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
+ fdisk_ask_number_set_unit(ask,
+ cxt->sector_size *
+ fdisk_get_units_per_sector(cxt));
+ } else {
+ fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
+ fdisk_ask_number_set_unit(ask,cxt->sector_size);
+ }
+
+ fdisk_ask_number_set_low(ask, fdisk_cround(cxt, start));
+ fdisk_ask_number_set_default(ask, fdisk_cround(cxt, limit));
+ fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
+ fdisk_ask_number_set_base(ask, fdisk_cround(cxt, start)); /* base for relative input */
+
+ rc = fdisk_do_ask(cxt, ask);
+ stop = fdisk_ask_number_get_result(ask);
+ isrel = fdisk_ask_number_is_relative(ask);
+ fdisk_unref_ask(ask);
+ if (rc)
+ return rc;
+ if (fdisk_use_cylinders(cxt)) {
+ stop = stop * fdisk_get_units_per_sector(cxt) - 1;
+ if (stop >limit)
+ stop = limit;
+ }
+ }
+
+ DBG(LABEL, ul_debug("DOS: raw stop: %ju", (uintmax_t) stop));
+
+ if (stop > limit)
+ stop = limit;
+
+ if (stop < limit) {
+ if (isrel && alignment_required(cxt)) {
+ /* the last sector has not been exactly requested (but
+ * defined by +size{K,M,G} convention), so be smart and
+ * align the end of the partition. The next partition
+ * will start at phy.block boundary.
+ */
+ stop = fdisk_align_lba_in_range(cxt, stop, start, limit) - 1;
+ if (stop > limit)
+ stop = limit;
+ }
+ }
+
+ set_partition(cxt, n, 0, start, stop, sys, pa && pa->boot == 1 ? 1 : 0);
+ if (n > 4) {
+ struct pte *pe = self_pte(cxt, n);
+ set_partition(cxt, n - 1, 1, pe->offset, stop,
+ MBR_DOS_EXTENDED_PARTITION, 0);
+ }
+
+ /* report */
+ {
+ struct fdisk_parttype *t =
+ fdisk_label_get_parttype_from_code(cxt->label, sys);
+ fdisk_info_new_partition(cxt, n + 1, start, stop, t);
+ fdisk_unref_parttype(t);
+ }
+
+
+ if (IS_EXTENDED(sys)) {
+ struct pte *pen = self_pte(cxt, n);
+
+ l->ext_index = n;
+ l->ext_offset = start;
+ pen->ex_entry = p;
+ }
+
+ fdisk_label_set_changed(cxt->label, 1);
+ return 0;
+}
+
+static int add_logical(struct fdisk_context *cxt,
+ struct fdisk_partition *pa,
+ size_t *partno)
+{
+ struct pte *pe;
+ int rc;
+
+ assert(cxt);
+ assert(partno);
+ assert(cxt->label);
+ assert(self_label(cxt)->ext_offset);
+
+ DBG(LABEL, ul_debug("DOS: nparts max: %zu", cxt->label->nparts_max));
+ pe = self_pte(cxt, cxt->label->nparts_max);
+
+ if (!pe->sectorbuffer) {
+ pe->sectorbuffer = calloc(1, cxt->sector_size);
+ if (!pe->sectorbuffer)
+ return -ENOMEM;
+ DBG(LABEL, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
+ cxt->label->nparts_max, pe->sectorbuffer));
+ pe->private_sectorbuffer = 1;
+ }
+ pe->pt_entry = mbr_get_partition(pe->sectorbuffer, 0);
+ pe->ex_entry = pe->pt_entry + 1;
+ pe->offset = 0;
+ partition_set_changed(cxt, cxt->label->nparts_max, 1);
+
+ cxt->label->nparts_max++;
+
+ /* this message makes sense only when we use extended/primary/logical
+ * dialog. The dialog is disable for scripts, see dos_add_partition() */
+ if (!cxt->script)
+ fdisk_info(cxt, _("Adding logical partition %zu"),
+ cxt->label->nparts_max);
+ *partno = cxt->label->nparts_max - 1;
+ rc = add_partition(cxt, *partno, pa);
+
+ if (rc) {
+ /* reset on error */
+ cxt->label->nparts_max--;
+ pe->pt_entry = NULL;
+ pe->ex_entry = NULL;
+ pe->offset = 0;
+ pe->changed = 0;
+ }
+
+ return rc;
+}
+
+static void check(struct fdisk_context *cxt, size_t n,
+ unsigned int h, unsigned int s, unsigned int c,
+ unsigned int start)
+{
+ unsigned int total, real_s, real_c;
+
+ if (!is_dos_compatible(cxt))
+ return;
+
+ real_s = sector(s) - 1;
+ real_c = cylinder(s, c);
+ total = (real_c * cxt->geom.heads + h) * cxt->geom.sectors + real_s;
+
+ if (!total)
+ fdisk_warnx(cxt, _("Partition %zu: contains sector 0"), n);
+ if (h >= cxt->geom.heads)
+ fdisk_warnx(cxt, _("Partition %zu: head %d greater than "
+ "maximum %d"), n, h + 1, cxt->geom.heads);
+ if (real_s >= cxt->geom.sectors)
+ fdisk_warnx(cxt, _("Partition %zu: sector %d greater than "
+ "maximum %llu"), n, s, cxt->geom.sectors);
+ if (real_c >= cxt->geom.cylinders)
+ fdisk_warnx(cxt, _("Partition %zu: cylinder %d greater than "
+ "maximum %llu"),
+ n, real_c + 1,
+ cxt->geom.cylinders);
+
+ if (cxt->geom.cylinders <= 1024 && start != total)
+ fdisk_warnx(cxt, _("Partition %zu: previous sectors %u "
+ "disagrees with total %u"), n, start, total);
+}
+
+/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
+ * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
+ * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
+ * Lubkin Oct. 1991). */
+
+static void
+long2chs(struct fdisk_context *cxt, unsigned long ls,
+ unsigned int *c, unsigned int *h, unsigned int *s) {
+ int spc = cxt->geom.heads * cxt->geom.sectors;
+
+ *c = ls / spc;
+ ls = ls % spc;
+ *h = ls / cxt->geom.sectors;
+ *s = ls % cxt->geom.sectors + 1; /* sectors count from 1 */
+}
+
+static void check_consistency(struct fdisk_context *cxt, struct dos_partition *p,
+ size_t partition)
+{
+ unsigned int pbc, pbh, pbs; /* physical beginning c, h, s */
+ unsigned int pec, peh, pes; /* physical ending c, h, s */
+ unsigned int lbc, lbh, lbs; /* logical beginning c, h, s */
+ unsigned int lec, leh, les; /* logical ending c, h, s */
+
+ if (!is_dos_compatible(cxt))
+ return;
+
+ if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
+ return; /* do not check extended partitions */
+
+ /* physical beginning c, h, s */
+ pbc = (p->bc & 0xff) | ((p->bs << 2) & 0x300);
+ pbh = p->bh;
+ pbs = p->bs & 0x3f;
+
+ /* physical ending c, h, s */
+ pec = (p->ec & 0xff) | ((p->es << 2) & 0x300);
+ peh = p->eh;
+ pes = p->es & 0x3f;
+
+ /* compute logical beginning (c, h, s) */
+ long2chs(cxt, dos_partition_get_start(p), &lbc, &lbh, &lbs);
+
+ /* compute logical ending (c, h, s) */
+ long2chs(cxt, dos_partition_get_start(p) + dos_partition_get_size(p) - 1, &lec, &leh, &les);
+
+ /* Same physical / logical beginning? */
+ if (cxt->geom.cylinders <= 1024
+ && (pbc != lbc || pbh != lbh || pbs != lbs)) {
+ fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
+ "beginnings (non-Linux?): "
+ "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
+ partition + 1,
+ pbc, pbh, pbs,
+ lbc, lbh, lbs);
+ }
+
+ /* Same physical / logical ending? */
+ if (cxt->geom.cylinders <= 1024
+ && (pec != lec || peh != leh || pes != les)) {
+ fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
+ "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
+ partition + 1,
+ pec, peh, pes,
+ lec, leh, les);
+ }
+
+ /* Ending on cylinder boundary? */
+ if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
+ fdisk_warnx(cxt, _("Partition %zu: does not end on "
+ "cylinder boundary."),
+ partition + 1);
+ }
+}
+
+static int dos_verify_disklabel(struct fdisk_context *cxt)
+{
+ size_t i, j;
+ fdisk_sector_t total = 1, n_sectors = cxt->total_sectors;
+ fdisk_sector_t first[cxt->label->nparts_max],
+ last[cxt->label->nparts_max];
+ struct dos_partition *p;
+ struct fdisk_dos_label *l = self_label(cxt);
+
+ assert(fdisk_is_label(cxt, DOS));
+
+ fill_bounds(cxt, first, last);
+ for (i = 0; i < cxt->label->nparts_max; i++) {
+ struct pte *pe = self_pte(cxt, i);
+
+ p = self_partition(cxt, i);
+ if (is_used_partition(p) && !IS_EXTENDED(p->sys_ind)) {
+ check_consistency(cxt, p, i);
+ if (get_abs_partition_start(pe) < first[i])
+ fdisk_warnx(cxt, _(
+ "Partition %zu: bad start-of-data."),
+ i + 1);
+
+ check(cxt, i + 1, p->eh, p->es, p->ec, last[i]);
+ total += last[i] + 1 - first[i];
+
+ if (i == 0)
+ total += get_abs_partition_start(pe) - 1;
+
+ for (j = 0; j < i; j++) {
+ if ((first[i] >= first[j] && first[i] <= last[j])
+ || ((last[i] <= last[j] && last[i] >= first[j]))) {
+
+ fdisk_warnx(cxt, _("Partition %zu: "
+ "overlaps partition %zu."),
+ j + 1, i + 1);
+
+ total += first[i] >= first[j] ?
+ first[i] : first[j];
+ total -= last[i] <= last[j] ?
+ last[i] : last[j];
+ }
+ }
+ }
+ }
+
+ if (l->ext_offset) {
+ fdisk_sector_t e_last;
+ struct pte *ext_pe = self_pte(cxt, l->ext_index);
+
+ e_last = get_abs_partition_end(ext_pe);
+
+ for (i = 4; i < cxt->label->nparts_max; i++) {
+ total++;
+ p = self_partition(cxt, i);
+
+ if (!p->sys_ind) {
+ if (i != 4 || i + 1 < cxt->label->nparts_max)
+ fdisk_warnx(cxt,
+ _("Partition %zu: empty."),
+ i + 1);
+ } else if (first[i] < l->ext_offset
+ || last[i] > e_last) {
+
+ fdisk_warnx(cxt, _("Logical partition %zu: "
+ "not entirely in partition %zu."),
+ i + 1, l->ext_index + 1);
+ }
+ }
+ }
+
+ if (total > n_sectors)
+ fdisk_warnx(cxt, _("Total allocated sectors %llu greater "
+ "than the maximum %llu."), total, n_sectors);
+ else if (total < n_sectors)
+ fdisk_warnx(cxt, _("Remaining %lld unallocated %ld-byte "
+ "sectors."), n_sectors - total, cxt->sector_size);
+
+ return 0;
+}
+
+/*
+ * Ask the user for new partition type information (logical, extended).
+ * This function calls the actual partition adding logic - add_partition.
+ *
+ * API callback.
+ */
+static int dos_add_partition(struct fdisk_context *cxt,
+ struct fdisk_partition *pa,
+ size_t *partno)
+{
+ size_t i, free_primary = 0, free_sectors = 0;
+ fdisk_sector_t last = 0, grain;
+ int rc = 0;
+ struct fdisk_dos_label *l;
+ struct pte *ext_pe;
+ size_t res; /* partno */
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ DBG(LABEL, ul_debug("DOS: new partition wanted"));
+
+ l = self_label(cxt);
+ ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
+
+ /*
+ * partition template (@pa) based partitioning
+ */
+
+ /* pa specifies start within extended partition, add logical */
+ if (pa && fdisk_partition_has_start(pa) && ext_pe
+ && pa->start >= l->ext_offset
+ && pa->start <= get_abs_partition_end(ext_pe)) {
+ DBG(LABEL, ul_debug("DOS: pa template %p: add logical", pa));
+ rc = add_logical(cxt, pa, &res);
+ goto done;
+
+ /* pa specifies that extended partition is wanted */
+ } else if (pa && pa->type && pa->type->code == MBR_DOS_EXTENDED_PARTITION) {
+ DBG(LABEL, ul_debug("DOS: pa template %p: add extened", pa));
+ if (l->ext_offset) {
+ fdisk_warnx(cxt, _("Extended partition already exists."));
+ return -EINVAL;
+ }
+ rc = get_partition_unused_primary(cxt, pa, &res);
+ if (rc == 0) {
+ rc = add_partition(cxt, res, pa);
+ goto done;
+ }
+
+ /* pa specifies start, but outside extended partition */
+ } else if (pa && fdisk_partition_has_start(pa) && l->ext_offset) {
+ DBG(LABEL, ul_debug("DOS: pa template %p: add primary", pa));
+ rc = get_partition_unused_primary(cxt, pa, &res);
+ if (rc == 0) {
+ rc = add_partition(cxt, res, pa);
+ goto done;
+ }
+ }
+
+ /*
+ * dialog driven partitioning (it does not mean that @pa template is
+ * completely ignored!)
+ */
+
+ /* check if there is space for primary partition */
+ grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1;
+ last = cxt->first_lba;
+
+ for (i = 0; i < 4; i++) {
+ struct dos_partition *p = self_partition(cxt, i);
+
+ if (is_used_partition(p)) {
+ fdisk_sector_t start = dos_partition_get_start(p);
+ if (last + grain <= start)
+ free_sectors = 1;
+ last = start + dos_partition_get_size(p);
+ } else
+ free_primary++;
+ }
+ if (last + grain < cxt->total_sectors - 1)
+ free_sectors = 1;
+
+ if (!free_primary && cxt->label->nparts_max >= MAXIMUM_PARTS) {
+ fdisk_info(cxt, _("The maximum number of partitions has "
+ "been created."));
+ return -EINVAL;
+ }
+ rc = 1;
+
+ if (!free_primary || !free_sectors) {
+ DBG(LABEL, ul_debug("DOS: primary impossible, add logical"));
+ if (l->ext_offset) {
+ if (!pa || fdisk_partition_has_start(pa)) {
+ if (!free_primary)
+ fdisk_info(cxt, _("All primary partitions are in use."));
+ else if (!free_sectors)
+ fdisk_info(cxt, _("All space for primary partitions is in use."));
+ }
+ rc = add_logical(cxt, pa, &res);
+ } else {
+ fdisk_info(cxt,
+ _( "Impossible to create another primary partition. "
+ "If you want to create more partitions, you must "
+ "replace a primary partition with an extended "
+ "partition first."));
+ return -EINVAL;
+ }
+ } else if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
+ fdisk_info(cxt, _("All logical partitions are in use. "
+ "Adding a primary partition."));
+ rc = get_partition_unused_primary(cxt, pa, &res);
+ if (rc == 0)
+ rc = add_partition(cxt, res, pa);
+ } else {
+ char hint[BUFSIZ];
+ struct fdisk_ask *ask;
+ int c;
+
+ /* the default layout for scripts is to create primary partitions */
+ if (cxt->script) {
+ rc = get_partition_unused_primary(cxt, pa, &res);
+ if (rc == 0)
+ rc = add_partition(cxt, res, pa);
+ goto done;
+ }
+
+ ask = fdisk_new_ask();
+ if (!ask)
+ return -ENOMEM;
+ fdisk_ask_set_type(ask, FDISK_ASKTYPE_MENU);
+ fdisk_ask_set_query(ask, _("Partition type"));
+ fdisk_ask_menu_set_default(ask, free_primary == 1
+ && !l->ext_offset ? 'e' : 'p');
+ snprintf(hint, sizeof(hint),
+ _("%zu primary, %d extended, %zu free"),
+ 4 - (l->ext_offset ? 1 : 0) - free_primary,
+ l->ext_offset ? 1 : 0,
+ free_primary);
+
+ fdisk_ask_menu_add_item(ask, 'p', _("primary"), hint);
+ if (!l->ext_offset)
+ fdisk_ask_menu_add_item(ask, 'e', _("extended"), _("container for logical partitions"));
+ else
+ fdisk_ask_menu_add_item(ask, 'l', _("logical"), _("numbered from 5"));
+
+ rc = fdisk_do_ask(cxt, ask);
+ if (rc)
+ return rc;
+ fdisk_ask_menu_get_result(ask, &c);
+ fdisk_unref_ask(ask);
+
+ if (c == 'p') {
+ rc = get_partition_unused_primary(cxt, pa, &res);
+ if (rc == 0)
+ rc = add_partition(cxt, res, pa);
+ goto done;
+ } else if (c == 'l' && l->ext_offset) {
+ rc = add_logical(cxt, pa, &res);
+ goto done;
+ } else if (c == 'e' && !l->ext_offset) {
+ rc = get_partition_unused_primary(cxt, pa, &res);
+ if (rc == 0) {
+ struct fdisk_partition *xpa = NULL;
+ struct fdisk_parttype *t;
+
+ t = fdisk_label_get_parttype_from_code(cxt->label,
+ MBR_DOS_EXTENDED_PARTITION);
+ if (!pa) {
+ pa = xpa = fdisk_new_partition();
+ if (!xpa)
+ return -ENOMEM;
+ }
+ fdisk_partition_set_type(pa, t);
+ rc = add_partition(cxt, res, pa);
+ if (xpa) {
+ fdisk_unref_partition(xpa);
+ pa = NULL;
+ }
+ }
+ goto done;
+ } else
+ fdisk_warnx(cxt, _("Invalid partition type `%c'."), c);
+ }
+done:
+ if (rc == 0) {
+ cxt->label->nparts_cur++;
+ if (partno)
+ *partno = res;
+ }
+ return rc;
+}
+
+static int write_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
+ unsigned char *buf)
+{
+ int rc;
+
+ rc = seek_sector(cxt, secno);
+ if (rc != 0) {
+ fdisk_warn(cxt, _("Cannot write sector %jd: seek failed"),
+ (uintmax_t) secno);
+ return rc;
+ }
+
+ DBG(LABEL, ul_debug("DOS: writting to sector %ju", (uintmax_t) secno));
+
+ if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
+ return -errno;
+ return 0;
+}
+
+static int dos_write_disklabel(struct fdisk_context *cxt)
+{
+ struct fdisk_dos_label *l = self_label(cxt);
+ size_t i;
+ int rc = 0, mbr_changed = 0;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ mbr_changed = l->non_pt_changed;
+
+ /* MBR (primary partitions) */
+ if (!mbr_changed) {
+ for (i = 0; i < 4; i++) {
+ struct pte *pe = self_pte(cxt, i);
+ if (pe->changed)
+ mbr_changed = 1;
+ }
+ }
+ if (mbr_changed) {
+ mbr_set_magic(cxt->firstsector);
+ rc = write_sector(cxt, 0, cxt->firstsector);
+ if (rc)
+ goto done;
+ }
+
+ if (cxt->label->nparts_max <= 4 && l->ext_offset) {
+ /* we have empty extended partition, check if the partition has
+ * been modified and then cleanup possible remaining EBR */
+ struct pte *pe = self_pte(cxt, l->ext_index);
+ unsigned char empty[512] = { 0 };
+ fdisk_sector_t off = pe ? get_abs_partition_start(pe) : 0;
+
+ if (off && pe->changed) {
+ mbr_set_magic(empty);
+ write_sector(cxt, off, empty);
+ }
+ }
+
+ /* EBR (logical partitions) */
+ for (i = 4; i < cxt->label->nparts_max; i++) {
+ struct pte *pe = self_pte(cxt, i);
+
+ if (!pe->changed || !pe->offset || !pe->sectorbuffer)
+ continue;
+
+ mbr_set_magic(pe->sectorbuffer);
+ rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
+ if (rc)
+ goto done;
+ }
+
+done:
+ return rc;
+}
+
+static int dos_locate_disklabel(struct fdisk_context *cxt, int n,
+ const char **name, off_t *offset, size_t *size)
+{
+ assert(cxt);
+
+ *name = NULL;
+ *offset = 0;
+ *size = 0;
+
+ switch (n) {
+ case 0:
+ *name = "MBR";
+ *offset = 0;
+ *size = 512;
+ break;
+ default:
+ /* extended partitions */
+ if (n - 1 + 4 < cxt->label->nparts_max) {
+ struct pte *pe = self_pte(cxt, n - 1 + 4);
+
+ assert(pe->private_sectorbuffer);
+
+ *name = "EBR";
+ *offset = pe->offset * cxt->sector_size;
+ *size = 512;
+ } else
+ return 1;
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Check whether partition entries are ordered by their starting positions.
+ * Return 0 if OK. Return i if partition i should have been earlier.
+ * Two separate checks: primary and logical partitions.
+ */
+static int wrong_p_order(struct fdisk_context *cxt, size_t *prev)
+{
+ size_t last_p_start_pos = 0, p_start_pos;
+ size_t i, last_i = 0;
+
+ for (i = 0 ; i < cxt->label->nparts_max; i++) {
+
+ struct pte *pe = self_pte(cxt, i);
+ struct dos_partition *p = pe->pt_entry;
+
+ if (i == 4) {
+ last_i = 4;
+ last_p_start_pos = 0;
+ }
+ if (is_used_partition(p)) {
+ p_start_pos = get_abs_partition_start(pe);
+
+ if (last_p_start_pos > p_start_pos) {
+ if (prev)
+ *prev = last_i;
+ return i;
+ }
+
+ last_p_start_pos = p_start_pos;
+ last_i = i;
+ }
+ }
+ return 0;
+}
+
+static int dos_list_disklabel(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ return 0;
+}
+
+static int dos_get_partition(struct fdisk_context *cxt, size_t n,
+ struct fdisk_partition *pa)
+{
+ struct dos_partition *p;
+ struct pte *pe;
+ struct fdisk_dos_label *lb;
+
+ assert(cxt);
+ assert(pa);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ lb = self_label(cxt);
+ pe = self_pte(cxt, n);
+ p = pe->pt_entry;
+ pa->used = !is_cleared_partition(p);
+ if (!pa->used)
+ return 0;
+
+ pa->type = dos_partition_parttype(cxt, p);
+ pa->boot = p->boot_ind == ACTIVE_FLAG ? 1 : 0;
+ pa->start = get_abs_partition_start(pe);
+ pa->size = dos_partition_get_size(p);
+ pa->container = lb->ext_offset && n == lb->ext_index;
+
+ if (n >= 4)
+ pa->parent_partno = lb->ext_index;
+
+ if (p->boot_ind && asprintf(&pa->attrs, "%02x", p->boot_ind) < 0)
+ return -ENOMEM;
+
+ /* start C/H/S */
+ if (asprintf(&pa->start_chs, "%d/%d/%d",
+ cylinder(p->bs, p->bc),
+ sector(p->bs),
+ p->bh) < 0)
+ return -ENOMEM;
+
+ /* end C/H/S */
+ if (asprintf(&pa->end_chs, "%d/%d/%d",
+ cylinder(p->es, p->ec),
+ sector(p->es),
+ p->eh) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int dos_set_partition(struct fdisk_context *cxt, size_t n,
+ struct fdisk_partition *pa)
+{
+ struct fdisk_dos_label *l;
+ struct dos_partition *p;
+ struct pte *pe;
+ fdisk_sector_t start, size;
+
+ assert(cxt);
+ assert(pa);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ if (n >= cxt->label->nparts_max)
+ return -EINVAL;
+
+ if (pa->type && IS_EXTENDED(pa->type->code)) {
+ fdisk_warnx(cxt, _("You cannot change a partition into an "
+ "extended one or vice versa. Delete it first."));
+ return -EINVAL;
+ }
+
+ if (pa->type && !pa->type->code)
+ fdisk_warnx(cxt, _("Type 0 means free space to many systems. "
+ "Having partitions of type 0 is probably unwise."));
+ l = self_label(cxt);
+ p = self_partition(cxt, n);
+ pe = self_pte(cxt, n);
+
+ FDISK_INIT_UNDEF(start);
+ FDISK_INIT_UNDEF(size);
+
+ if (fdisk_partition_has_start(pa))
+ start = pa->start;
+ if (fdisk_partition_has_size(pa))
+ size = pa->size;
+
+ if (pa->end_follow_default) {
+ fdisk_sector_t first[cxt->label->nparts_max],
+ last[cxt->label->nparts_max],
+ xlast;
+ struct pte *ext = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
+
+ fill_bounds(cxt, first, last);
+
+ if (ext && l->ext_offset) {
+ first[l->ext_index] = l->ext_offset;
+ last[l->ext_index] = get_abs_partition_end(ext);
+ }
+ if (FDISK_IS_UNDEF(start))
+ start = get_abs_partition_start(pe);
+
+ DBG(LABEL, ul_debug("DOS: #%zu now %ju +%ju sectors",
+ n, (uintmax_t) start, (uintmax_t) dos_partition_get_size(p)));
+
+ xlast = get_unused_last(cxt, n, start, first, last);
+ size = xlast ? xlast - start + 1: dos_partition_get_size(p);
+
+ DBG(LABEL, ul_debug("DOS: #%zu wanted %ju +%ju sectors",
+ n, (uintmax_t) start, (uintmax_t) size));
+ }
+
+ if (!FDISK_IS_UNDEF(start) || !FDISK_IS_UNDEF(size)) {
+ DBG(LABEL, ul_debug("DOS: resize partition"));
+
+ if (FDISK_IS_UNDEF(start))
+ start = get_abs_partition_start(pe);
+ if (FDISK_IS_UNDEF(size))
+ size = dos_partition_get_size(p);
+
+ set_partition(cxt, n, 0, start, start + size - 1,
+ pa->type ? pa->type->code : p->sys_ind,
+ pa->boot == 1);
+ } else {
+ DBG(LABEL, ul_debug("DOS: keep size, modify properties"));
+ if (pa->type)
+ p->sys_ind = pa->type->code;
+ if (!FDISK_IS_UNDEF(pa->boot))
+ p->boot_ind = pa->boot == 1 ? ACTIVE_FLAG : 0;
+ }
+
+ partition_set_changed(cxt, n, 1);
+ return 0;
+}
+
+static void print_chain_of_logicals(struct fdisk_context *cxt)
+{
+ size_t i;
+ struct fdisk_dos_label *l = self_label(cxt);
+
+ fputc('\n', stdout);
+
+ for (i = 4; i < cxt->label->nparts_max; i++) {
+ struct pte *pe = self_pte(cxt, i);
+
+ fprintf(stderr, "#%02zu EBR [%10ju], "
+ "data[start=%10ju (%10ju), size=%10ju], "
+ "link[start=%10ju (%10ju), size=%10ju]\n",
+ i, (uintmax_t) pe->offset,
+ /* data */
+ (uintmax_t) dos_partition_get_start(pe->pt_entry),
+ (uintmax_t) get_abs_partition_start(pe),
+ (uintmax_t) dos_partition_get_size(pe->pt_entry),
+ /* link */
+ (uintmax_t) dos_partition_get_start(pe->ex_entry),
+ (uintmax_t) l->ext_offset + dos_partition_get_start(pe->ex_entry),
+ (uintmax_t) dos_partition_get_size(pe->ex_entry));
+ }
+}
+
+static int cmp_ebr_offsets(const void *a, const void *b)
+{
+ struct pte *ae = (struct pte *) a,
+ *be = (struct pte *) b;
+
+ if (ae->offset == 0 && be->offset == 0)
+ return 0;
+ if (ae->offset == 0)
+ return 1;
+ if (be->offset == 0)
+ return -1;
+
+ return cmp_numbers(ae->offset, be->offset);
+}
+
+/*
+ * Fix the chain of logicals.
+ *
+ * The function does not modify data partitions within EBR tables
+ * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
+ * (pte->ex_entry) between EBR tables.
+ *
+ */
+static void fix_chain_of_logicals(struct fdisk_context *cxt)
+{
+ struct fdisk_dos_label *l = self_label(cxt);
+ struct pte *last;
+ size_t i;
+
+ DBG(LABEL, print_chain_of_logicals(cxt));
+
+ /* Sort chain by EBR offsets */
+ qsort(&l->ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte),
+ cmp_ebr_offsets);
+
+again:
+ /* Sort data partitions by start */
+ for (i = 4; i < cxt->label->nparts_max - 1; i++) {
+ struct pte *cur = self_pte(cxt, i),
+ *nxt = self_pte(cxt, i + 1);
+
+ if (get_abs_partition_start(cur) >
+ get_abs_partition_start(nxt)) {
+
+ struct dos_partition tmp = *cur->pt_entry;
+ fdisk_sector_t cur_start = get_abs_partition_start(cur),
+ nxt_start = get_abs_partition_start(nxt);
+
+ /* swap data partitions */
+ *cur->pt_entry = *nxt->pt_entry;
+ *nxt->pt_entry = tmp;
+
+ /* Recount starts according to EBR offsets, the absolute
+ * address tas to be still the same! */
+ dos_partition_set_start(cur->pt_entry, nxt_start - cur->offset);
+ dos_partition_set_start(nxt->pt_entry, cur_start - nxt->offset);
+
+ partition_set_changed(cxt, i, 1);
+ partition_set_changed(cxt, i + 1, 1);
+ goto again;
+ }
+ }
+
+ /* Update EBR links */
+ for (i = 4; i < cxt->label->nparts_max - 1; i++) {
+ struct pte *cur = self_pte(cxt, i),
+ *nxt = self_pte(cxt, i + 1);
+
+ fdisk_sector_t noff = nxt->offset - l->ext_offset,
+ ooff = dos_partition_get_start(cur->ex_entry);
+
+ if (noff == ooff)
+ continue;
+
+ DBG(LABEL, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
+ (uintmax_t) cur->offset,
+ (uintmax_t) ooff, (uintmax_t) noff));
+
+ set_partition(cxt, i, 1, nxt->offset,
+ get_abs_partition_end(nxt),
+ MBR_DOS_EXTENDED_PARTITION, 0);
+ }
+
+ /* always terminate the chain ! */
+ last = self_pte(cxt, cxt->label->nparts_max - 1);
+ if (last) {
+ clear_partition(last->ex_entry);
+ partition_set_changed(cxt, cxt->label->nparts_max - 1, 1);
+ }
+
+ DBG(LABEL, print_chain_of_logicals(cxt));
+}
+
+static int dos_reorder(struct fdisk_context *cxt)
+{
+ struct pte *pei, *pek;
+ size_t i,k;
+
+ if (!wrong_p_order(cxt, NULL)) {
+ fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
+ return 0;
+ }
+
+ while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) {
+ /* partition i should have come earlier, move it */
+ /* We have to move data in the MBR */
+ struct dos_partition *pi, *pk, *pe, pbuf;
+ pei = self_pte(cxt, i);
+ pek = self_pte(cxt, k);
+
+ pe = pei->ex_entry;
+ pei->ex_entry = pek->ex_entry;
+ pek->ex_entry = pe;
+
+ pi = pei->pt_entry;
+ pk = pek->pt_entry;
+
+ memmove(&pbuf, pi, sizeof(struct dos_partition));
+ memmove(pi, pk, sizeof(struct dos_partition));
+ memmove(pk, &pbuf, sizeof(struct dos_partition));
+
+ partition_set_changed(cxt, i, 1);
+ partition_set_changed(cxt, k, 1);
+ }
+
+ if (i)
+ fix_chain_of_logicals(cxt);
+
+ fdisk_info(cxt, _("Done."));
+ return 0;
+}
+
+/* TODO: use fdisk_set_partition() API */
+int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i)
+{
+ struct pte *pe;
+ struct dos_partition *p;
+ unsigned int new, free_start, curr_start, last;
+ uintmax_t res = 0;
+ size_t x;
+ int rc;
+
+ assert(cxt);
+ assert(fdisk_is_label(cxt, DOS));
+
+ pe = self_pte(cxt, i);
+ p = pe->pt_entry;
+
+ if (!is_used_partition(p) || IS_EXTENDED (p->sys_ind)) {
+ fdisk_warnx(cxt, _("Partition %zu: no data area."), i + 1);
+ return 0;
+ }
+
+ /* the default start is at the second sector of the disk or at the
+ * second sector of the extended partition
+ */
+ free_start = pe->offset ? pe->offset + 1 : 1;
+
+ curr_start = get_abs_partition_start(pe);
+
+ /* look for a free space before the current start of the partition */
+ for (x = 0; x < cxt->label->nparts_max; x++) {
+ unsigned int end;
+ struct pte *prev_pe = self_pte(cxt, x);
+ struct dos_partition *prev_p = prev_pe->pt_entry;
+
+ if (!prev_p)
+ continue;
+ end = get_abs_partition_start(prev_pe)
+ + dos_partition_get_size(prev_p);
+
+ if (is_used_partition(prev_p) &&
+ end > free_start && end <= curr_start)
+ free_start = end;
+ }
+
+ last = get_abs_partition_end(pe);
+
+ rc = fdisk_ask_number(cxt, free_start, curr_start, last,
+ _("New beginning of data"), &res);
+ if (rc)
+ return rc;
+
+ new = res - pe->offset;
+
+ if (new != dos_partition_get_size(p)) {
+ unsigned int sects = dos_partition_get_size(p)
+ + dos_partition_get_start(p) - new;
+
+ dos_partition_set_size(p, sects);
+ dos_partition_set_start(p, new);
+
+ partition_set_changed(cxt, i, 1);
+ }
+
+ return rc;
+}
+
+static int dos_partition_is_used(
+ struct fdisk_context *cxt,
+ size_t i)
+{
+ struct dos_partition *p;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ if (i >= cxt->label->nparts_max)
+ return 0;
+
+ p = self_partition(cxt, i);
+
+ return p && !is_cleared_partition(p);
+}
+
+static int dos_toggle_partition_flag(
+ struct fdisk_context *cxt,
+ size_t i,
+ unsigned long flag)
+{
+ struct dos_partition *p;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, DOS));
+
+ if (i >= cxt->label->nparts_max)
+ return -EINVAL;
+
+ p = self_partition(cxt, i);
+
+ switch (flag) {
+ case DOS_FLAG_ACTIVE:
+ if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
+ fdisk_warnx(cxt, _("Partition %zu: is an extended "
+ "partition."), i + 1);
+
+ p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
+ partition_set_changed(cxt, i, 1);
+ fdisk_info(cxt, p->boot_ind ?
+ _("The bootable flag on partition %zu is enabled now.") :
+ _("The bootable flag on partition %zu is disabled now."),
+ i + 1);
+ break;
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct fdisk_field dos_fields[] =
+{
+ /* basic */
+ { FDISK_FIELD_DEVICE, N_("Device"), 10, 0 },
+ { FDISK_FIELD_BOOT, N_("Boot"), 1, 0 },
+ { FDISK_FIELD_START, N_("Start"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_END, N_("End"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_SECTORS, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_CYLINDERS,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_SIZE, N_("Size"), 5, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
+ { FDISK_FIELD_TYPEID, N_("Id"), 2, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_TYPE, N_("Type"), 0.1, 0 },
+
+ /* expert mode */
+ { FDISK_FIELD_SADDR, N_("Start-C/H/S"), 1, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
+ { FDISK_FIELD_EADDR, N_("End-C/H/S"), 1, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
+ { FDISK_FIELD_ATTR, N_("Attrs"), 2, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL }
+
+};
+
+static const struct fdisk_label_operations dos_operations =
+{
+ .probe = dos_probe_label,
+ .write = dos_write_disklabel,
+ .verify = dos_verify_disklabel,
+ .create = dos_create_disklabel,
+ .locate = dos_locate_disklabel,
+ .list = dos_list_disklabel,
+ .reorder = dos_reorder,
+ .get_id = dos_get_disklabel_id,
+ .set_id = dos_set_disklabel_id,
+
+ .get_part = dos_get_partition,
+ .set_part = dos_set_partition,
+ .add_part = dos_add_partition,
+ .del_part = dos_delete_partition,
+
+ .part_toggle_flag = dos_toggle_partition_flag,
+ .part_is_used = dos_partition_is_used,
+
+ .reset_alignment = dos_reset_alignment,
+
+ .deinit = dos_deinit,
+};
+
+/*
+ * allocates DOS in-memory stuff
+ */
+struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt)
+{
+ struct fdisk_label *lb;
+ struct fdisk_dos_label *dos;
+
+ assert(cxt);
+
+ dos = calloc(1, sizeof(*dos));
+ if (!dos)
+ return NULL;
+
+ /* initialize generic part of the driver */
+ lb = (struct fdisk_label *) dos;
+ lb->name = "dos";
+ lb->id = FDISK_DISKLABEL_DOS;
+ lb->op = &dos_operations;
+ lb->parttypes = dos_parttypes;
+ lb->nparttypes = ARRAY_SIZE(dos_parttypes) - 1;
+ lb->fields = dos_fields;
+ lb->nfields = ARRAY_SIZE(dos_fields);
+
+ return lb;
+}
+
+/**
+ * fdisk_dos_enable_compatible:
+ * @lb: DOS label (see fdisk_get_label())
+ * @enable: 0 or 1
+ *
+ * Enables deprecated DOS compatible mode, in this mode library checks for
+ * cylinders boundary, cases about CHS addressing and another obscure things.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable)
+{
+ struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb;
+
+ if (!lb)
+ return -EINVAL;
+
+ dos->compatible = enable;
+ if (enable)
+ lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
+ return 0;
+}
+
+/**
+ * fdisk_dos_is_compatible:
+ * @lb: DOS label
+ *
+ * Returns: 0 if DOS compatibility disabled, 1 if enabled
+ */
+int fdisk_dos_is_compatible(struct fdisk_label *lb)
+{
+ return ((struct fdisk_dos_label *) lb)->compatible;
+}
diff --git a/libblkid/libfdisk/src/fdiskP.h b/libblkid/libfdisk/src/fdiskP.h
new file mode 100644
index 000000000..b169a9ffb
--- /dev/null
+++ b/libblkid/libfdisk/src/fdiskP.h
@@ -0,0 +1,438 @@
+/*
+ * fdiskP.h - private library header file
+ *
+ * Copyright (C) 2012 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#ifndef _LIBFDISK_PRIVATE_H
+#define _LIBFDISK_PRIVATE_H
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+
+#include "c.h"
+#include "libfdisk.h"
+
+#include "nls.h" /* temporary before dialog API will be implamented */
+#include "list.h"
+#include "debug.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+/* features */
+#define CONFIG_LIBFDISK_ASSERT
+
+#ifdef CONFIG_LIBFDISK_ASSERT
+#include <assert.h>
+#endif
+
+/*
+ * Debug
+ */
+#define LIBFDISK_DEBUG_HELP (1 << 0)
+#define LIBFDISK_DEBUG_INIT (1 << 1)
+#define LIBFDISK_DEBUG_CXT (1 << 2)
+#define LIBFDISK_DEBUG_LABEL (1 << 3)
+#define LIBFDISK_DEBUG_ASK (1 << 4)
+#define LIBFDISK_DEBUG_PART (1 << 6)
+#define LIBFDISK_DEBUG_PARTTYPE (1 << 7)
+#define LIBFDISK_DEBUG_TAB (1 << 8)
+#define LIBFDISK_DEBUG_SCRIPT (1 << 9)
+#define LIBFDISK_DEBUG_ALL 0xFFFF
+
+UL_DEBUG_DECLARE_MASK(libfdisk);
+#define DBG(m, x) __UL_DBG(libfdisk, LIBFDISK_DEBUG_, m, x)
+#define ON_DBG(m, x) __UL_DBG_CALL(libfdisk, LIBFDISK_DEBUG_, m, x)
+#define DBG_FLUSH __UL_DBG_FLUSH(libfdisk, LIBFDISK_DEBUG_)
+
+#ifdef TEST_PROGRAM
+struct fdisk_test {
+ const char *name;
+ int (*body)(struct fdisk_test *ts, int argc, char *argv[]);
+ const char *usage;
+};
+
+/* test.c */
+extern int fdisk_run_test(struct fdisk_test *tests, int argc, char *argv[]);
+#endif
+
+
+/*
+ * Generic iterator
+ */
+struct fdisk_iter {
+ struct list_head *p; /* current position */
+ struct list_head *head; /* start position */
+ int direction; /* FDISK_ITER_{FOR,BACK}WARD */
+};
+
+#define IS_ITER_FORWARD(_i) ((_i)->direction == FDISK_ITER_FORWARD)
+#define IS_ITER_BACKWARD(_i) ((_i)->direction == FDISK_ITER_BACKWARD)
+
+#define FDISK_ITER_INIT(itr, list) \
+ do { \
+ (itr)->p = IS_ITER_FORWARD(itr) ? \
+ (list)->next : (list)->prev; \
+ (itr)->head = (list); \
+ } while(0)
+
+#define FDISK_ITER_ITERATE(itr, res, restype, member) \
+ do { \
+ res = list_entry((itr)->p, restype, member); \
+ (itr)->p = IS_ITER_FORWARD(itr) ? \
+ (itr)->p->next : (itr)->p->prev; \
+ } while(0)
+
+/*
+ * Partition types
+ */
+struct fdisk_parttype {
+ unsigned int code; /* type as number or zero */
+ char *name; /* description */
+ char *typestr; /* type as string or NULL */
+
+ unsigned int flags; /* FDISK_PARTTYPE_* flags */
+ int refcount; /* reference counter for allocated types */
+};
+
+enum {
+ FDISK_PARTTYPE_UNKNOWN = (1 << 1),
+ FDISK_PARTTYPE_INVISIBLE = (1 << 2),
+ FDISK_PARTTYPE_ALLOCATED = (1 << 3)
+};
+
+#define fdisk_parttype_is_invisible(_x) ((_x) && ((_x)->flags & FDISK_PARTTYPE_INVISIBLE))
+#define fdisk_parttype_is_allocated(_x) ((_x) && ((_x)->flags & FDISK_PARTTYPE_ALLOCATED))
+
+struct fdisk_partition {
+ int refcount; /* reference counter */
+
+ size_t partno; /* partition number */
+ size_t parent_partno; /* for logical partitions */
+
+ fdisk_sector_t start; /* first sectors */
+ fdisk_sector_t size; /* size in sectors */
+
+ char *name; /* partition name */
+ char *uuid; /* partition UUID */
+ char *attrs; /* partition flags/attributes converted to string */
+ struct fdisk_parttype *type; /* partition type */
+
+ struct list_head parts; /* list of partitions */
+
+ /* extra fields for partition_to_string() */
+ char start_post; /* start postfix (e.g. '+') */
+ char end_post; /* end postfix */
+ char size_post; /* size postfix */
+
+ uint64_t fsize; /* bsd junk */
+ uint64_t bsize;
+ uint64_t cpg;
+
+ char *start_chs; /* start C/H/S in string */
+ char *end_chs; /* end C/H/S in string */
+
+ unsigned int boot; /* MBR: bootable */
+
+ unsigned int container : 1, /* container partition (e.g. extended partition) */
+ end_follow_default : 1, /* use default end */
+ freespace : 1, /* this is free space */
+ partno_follow_default : 1, /* use default partno */
+ size_explicit : 1, /* don't align the size */
+ start_follow_default : 1, /* use default start */
+ used : 1, /* partition already used */
+ wholedisk : 1; /* special system partition */
+};
+
+#define FDISK_INIT_UNDEF(_x) ((_x) = (__typeof__(_x)) -1)
+#define FDISK_IS_UNDEF(_x) ((_x) == (__typeof__(_x)) -1)
+
+struct fdisk_table {
+ struct list_head parts; /* partitions */
+ int refcount;
+ size_t nents; /* number of partitions */
+};
+
+/*
+ * Legacy CHS based geometry
+ */
+struct fdisk_geometry {
+ unsigned int heads;
+ fdisk_sector_t sectors;
+ fdisk_sector_t cylinders;
+};
+
+/*
+ * Label specific operations
+ */
+struct fdisk_label_operations {
+ /* probe disk label */
+ int (*probe)(struct fdisk_context *cxt);
+ /* write in-memory changes to disk */
+ int (*write)(struct fdisk_context *cxt);
+ /* verify the partition table */
+ int (*verify)(struct fdisk_context *cxt);
+ /* create new disk label */
+ int (*create)(struct fdisk_context *cxt);
+ /* list disklabel details */
+ int (*list)(struct fdisk_context *cxt);
+ /* returns offset and size of the 'n' part of the PT */
+ int (*locate)(struct fdisk_context *cxt, int n, const char **name, off_t *offset, size_t *size);
+ /* reorder partitions */
+ int (*reorder)(struct fdisk_context *cxt);
+
+ /* get disk label ID */
+ int (*get_id)(struct fdisk_context *cxt, char **id);
+ /* set disk label ID */
+ int (*set_id)(struct fdisk_context *cxt);
+
+
+ /* new partition */
+ int (*add_part)(struct fdisk_context *cxt, struct fdisk_partition *pa,
+ size_t *partno);
+ /* delete partition */
+ int (*del_part)(struct fdisk_context *cxt, size_t partnum);
+
+ /* fill in partition struct */
+ int (*get_part)(struct fdisk_context *cxt, size_t n,
+ struct fdisk_partition *pa);
+ /* modify partition */
+ int (*set_part)(struct fdisk_context *cxt, size_t n,
+ struct fdisk_partition *pa);
+
+ /* return state of the partition */
+ int (*part_is_used)(struct fdisk_context *cxt, size_t partnum);
+
+ int (*part_toggle_flag)(struct fdisk_context *cxt, size_t i, unsigned long flag);
+
+ /* refresh alignment setting */
+ int (*reset_alignment)(struct fdisk_context *cxt);
+
+ /* free in-memory label stuff */
+ void (*free)(struct fdisk_label *lb);
+
+ /* deinit in-memory label stuff */
+ void (*deinit)(struct fdisk_label *lb);
+};
+
+/*
+ * The fields describes how to display libfdisk_partition
+ */
+struct fdisk_field {
+ int id; /* FDISK_FIELD_* */
+ const char *name; /* field name */
+ double width; /* field width (compatible with libsmartcols whint) */
+ int flags; /* FDISK_FIELDFL_* */
+};
+
+/* note that the defauls is to display a column always */
+enum {
+ FDISK_FIELDFL_DETAIL = (1 << 1), /* only display if fdisk_is_details() */
+ FDISK_FIELDFL_EYECANDY = (1 << 2), /* don't display if fdisk_is_details() */
+ FDISK_FIELDFL_NUMBER = (1 << 3), /* column display numbers */
+};
+
+/*
+ * Generic label
+ */
+struct fdisk_label {
+ const char *name; /* label name */
+ enum fdisk_labeltype id; /* FDISK_DISKLABEL_* */
+ struct fdisk_parttype *parttypes; /* supported partitions types */
+ size_t nparttypes; /* number of items in parttypes[] */
+
+ size_t nparts_max; /* maximal number of partitions */
+ size_t nparts_cur; /* number of currently used partitions */
+
+ int flags; /* FDISK_LABEL_FL_* flags */
+
+ unsigned int changed:1, /* label has been modified */
+ disabled:1; /* this driver is disabled at all */
+
+ const struct fdisk_field *fields; /* all possible fields */
+ size_t nfields;
+
+ const struct fdisk_label_operations *op;
+};
+
+
+/* label driver flags */
+enum {
+ FDISK_LABEL_FL_REQUIRE_GEOMETRY = (1 << 2),
+ FDISK_LABEL_FL_INCHARS_PARTNO = (1 << 3)
+};
+
+/* label allocators */
+extern struct fdisk_label *fdisk_new_gpt_label(struct fdisk_context *cxt);
+extern struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt);
+extern struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt);
+extern struct fdisk_label *fdisk_new_sgi_label(struct fdisk_context *cxt);
+extern struct fdisk_label *fdisk_new_sun_label(struct fdisk_context *cxt);
+
+
+struct ask_menuitem {
+ char key;
+ const char *name;
+ const char *desc;
+
+ struct ask_menuitem *next;
+};
+
+/* fdisk dialog -- note that nothing from this stuff will be directly exported,
+ * we will have get/set() function for everything.
+ */
+struct fdisk_ask {
+ int type; /* FDISK_ASKTYPE_* */
+ char *query;
+
+ int refcount;
+
+ union {
+ /* FDISK_ASKTYPE_{NUMBER,OFFSET} */
+ struct ask_number {
+ uint64_t hig; /* high limit */
+ uint64_t low; /* low limit */
+ uint64_t dfl; /* default */
+ uint64_t result;
+ uint64_t base; /* for relative results */
+ uint64_t unit; /* unit for offsets */
+ const char *range; /* by library generated list */
+ unsigned int relative :1,
+ inchars :1;
+ } num;
+ /* FDISK_ASKTYPE_{WARN,WARNX,..} */
+ struct ask_print {
+ const char *mesg;
+ int errnum; /* errno */
+ } print;
+ /* FDISK_ASKTYPE_YESNO */
+ struct ask_yesno {
+ int result; /* TRUE or FALSE */
+ } yesno;
+ /* FDISK_ASKTYPE_STRING */
+ struct ask_string {
+ char *result; /* allocated */
+ } str;
+ /* FDISK_ASKTYPE_MENU */
+ struct ask_menu {
+ int dfl; /* default meni item */
+ int result;
+ struct ask_menuitem *first;
+ } menu;
+ } data;
+};
+
+struct fdisk_context {
+ int dev_fd; /* device descriptor */
+ char *dev_path; /* device path */
+ int refcount;
+
+ unsigned char *firstsector; /* buffer with master boot record */
+ unsigned long firstsector_bufsz;
+
+ /* topology */
+ unsigned long io_size; /* I/O size used by fdisk */
+ unsigned long optimal_io_size; /* optional I/O returned by device */
+ unsigned long min_io_size; /* minimal I/O size */
+ unsigned long phy_sector_size; /* physical size */
+ unsigned long sector_size; /* logical size */
+ unsigned long alignment_offset;
+
+ unsigned int readonly : 1, /* don't write to the device */
+ display_in_cyl_units : 1, /* for obscure labels */
+ display_details : 1, /* expert display mode */
+ listonly : 1; /* list partition, nothing else */
+
+ /* alignment */
+ unsigned long grain; /* alignment unit */
+ fdisk_sector_t first_lba; /* recommended begin of the first partition */
+ fdisk_sector_t last_lba; /* recomennded end of last partition */
+
+ /* geometry */
+ fdisk_sector_t total_sectors; /* in logical sectors */
+ struct fdisk_geometry geom;
+
+ /* user setting to overwrite device default */
+ struct fdisk_geometry user_geom;
+ unsigned long user_pyh_sector;
+ unsigned long user_log_sector;
+
+ struct fdisk_label *label; /* current label, pointer to labels[] */
+
+ size_t nlabels; /* number of initialized label drivers */
+ struct fdisk_label *labels[8]; /* all supported labels,
+ * FIXME: use any enum rather than hardcoded number */
+
+ int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *); /* fdisk dialogs callback */
+ void *ask_data; /* ask_cb() data */
+
+ struct fdisk_context *parent; /* for nested PT */
+ struct fdisk_script *script; /* what we want to follow */
+};
+
+/* partition.c */
+int fdisk_partition_next_partno(struct fdisk_partition *pa,
+ struct fdisk_context *cxt,
+ size_t *n);
+
+/* context.c */
+extern int __fdisk_switch_label(struct fdisk_context *cxt,
+ struct fdisk_label *lb);
+extern int fdisk_missing_geometry(struct fdisk_context *cxt);
+
+/* alignment.c */
+fdisk_sector_t fdisk_scround(struct fdisk_context *cxt, fdisk_sector_t num);
+fdisk_sector_t fdisk_cround(struct fdisk_context *cxt, fdisk_sector_t num);
+
+extern int fdisk_discover_geometry(struct fdisk_context *cxt);
+extern int fdisk_discover_topology(struct fdisk_context *cxt);
+
+extern int fdisk_apply_user_device_properties(struct fdisk_context *cxt);
+extern void fdisk_zeroize_device_properties(struct fdisk_context *cxt);
+
+/* utils.c */
+extern int fdisk_init_firstsector_buffer(struct fdisk_context *cxt);
+extern int fdisk_read_firstsector(struct fdisk_context *cxt);
+extern char *fdisk_partname(const char *dev, size_t partno);
+
+/* label.c */
+extern int fdisk_probe_labels(struct fdisk_context *cxt);
+extern void fdisk_deinit_label(struct fdisk_label *lb);
+
+/* ask.c */
+struct fdisk_ask *fdisk_new_ask(void);
+void fdisk_reset_ask(struct fdisk_ask *ask);
+int fdisk_ask_set_query(struct fdisk_ask *ask, const char *str);
+int fdisk_ask_set_type(struct fdisk_ask *ask, int type);
+int fdisk_do_ask(struct fdisk_context *cxt, struct fdisk_ask *ask);
+int fdisk_ask_number_set_range(struct fdisk_ask *ask, const char *range);
+int fdisk_ask_number_set_default(struct fdisk_ask *ask, uint64_t dflt);
+int fdisk_ask_number_set_low(struct fdisk_ask *ask, uint64_t low);
+int fdisk_ask_number_set_high(struct fdisk_ask *ask, uint64_t high);
+int fdisk_ask_number_set_base(struct fdisk_ask *ask, uint64_t base);
+int fdisk_ask_number_set_unit(struct fdisk_ask *ask, uint64_t unit);
+int fdisk_ask_number_is_relative(struct fdisk_ask *ask);
+int fdisk_ask_menu_set_default(struct fdisk_ask *ask, int dfl);
+int fdisk_ask_menu_add_item(struct fdisk_ask *ask, int key,
+ const char *name, const char *desc);
+int fdisk_ask_print_set_errno(struct fdisk_ask *ask, int errnum);
+int fdisk_ask_print_set_mesg(struct fdisk_ask *ask, const char *mesg);
+int fdisk_info_new_partition(
+ struct fdisk_context *cxt,
+ int num, fdisk_sector_t start, fdisk_sector_t stop,
+ struct fdisk_parttype *t);
+
+/* dos.c */
+extern struct dos_partition *fdisk_dos_get_partition(
+ struct fdisk_context *cxt,
+ size_t i);
+
+#endif /* _LIBFDISK_PRIVATE_H */
diff --git a/libblkid/libfdisk/src/gpt.c b/libblkid/libfdisk/src/gpt.c
new file mode 100644
index 000000000..8c1c96c37
--- /dev/null
+++ b/libblkid/libfdisk/src/gpt.c
@@ -0,0 +1,2565 @@
+/*
+ * Copyright (C) 2007 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
+ *
+ * GUID Partition Table (GPT) support. Based on UEFI Specs 2.3.1
+ * Chapter 5: GUID Partition Table (GPT) Disk Layout (Jun 27th, 2012).
+ * Some ideas and inspiration from GNU parted and gptfdisk.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <uuid.h>
+
+#include "fdiskP.h"
+
+#include "nls.h"
+#include "crc32.h"
+#include "blkdev.h"
+#include "bitops.h"
+#include "strutils.h"
+#include "all-io.h"
+
+/**
+ * SECTION: gpt
+ * @title: UEFI GPT
+ * @short_description: specific functionality
+ */
+
+#define GPT_HEADER_SIGNATURE 0x5452415020494645LL /* EFI PART */
+#define GPT_HEADER_REVISION_V1_02 0x00010200
+#define GPT_HEADER_REVISION_V1_00 0x00010000
+#define GPT_HEADER_REVISION_V0_99 0x00009900
+#define GPT_HEADER_MINSZ 92 /* bytes */
+
+#define GPT_PMBR_LBA 0
+#define GPT_MBR_PROTECTIVE 1
+#define GPT_MBR_HYBRID 2
+
+#define GPT_PRIMARY_PARTITION_TABLE_LBA 0x00000001
+
+#define EFI_PMBR_OSTYPE 0xEE
+#define MSDOS_MBR_SIGNATURE 0xAA55
+#define GPT_PART_NAME_LEN (72 / sizeof(uint16_t))
+#define GPT_NPARTITIONS 128
+
+/* Globally unique identifier */
+struct gpt_guid {
+ 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];
+};
+
+
+/* only checking that the GUID is 0 is enough to verify an empty partition. */
+#define GPT_UNUSED_ENTRY_GUID \
+ ((struct gpt_guid) { 0x00000000, 0x0000, 0x0000, 0x00, 0x00, \
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }})
+
+/* Linux native partition type */
+#define GPT_DEFAULT_ENTRY_TYPE "0FC63DAF-8483-4772-8E79-3D69D8477DE4"
+
+/*
+ * Attribute bits
+ */
+enum {
+ /* UEFI specific */
+ GPT_ATTRBIT_REQ = 0,
+ GPT_ATTRBIT_NOBLOCK = 1,
+ GPT_ATTRBIT_LEGACY = 2,
+
+ /* GUID specific (range 48..64)*/
+ GPT_ATTRBIT_GUID_FIRST = 48,
+ GPT_ATTRBIT_GUID_COUNT = 16
+};
+
+#define GPT_ATTRSTR_REQ "RequiredPartiton"
+#define GPT_ATTRSTR_NOBLOCK "NoBlockIOProtocol"
+#define GPT_ATTRSTR_LEGACY "LegacyBIOSBootable"
+
+/* The GPT Partition entry array contains an array of GPT entries. */
+struct gpt_entry {
+ struct gpt_guid type; /* purpose and type of the partition */
+ struct gpt_guid partition_guid;
+ uint64_t lba_start;
+ uint64_t lba_end;
+ uint64_t attrs;
+ uint16_t name[GPT_PART_NAME_LEN];
+} __attribute__ ((packed));
+
+/* GPT header */
+struct gpt_header {
+ uint64_t signature; /* header identification */
+ uint32_t revision; /* header version */
+ uint32_t size; /* in bytes */
+ uint32_t crc32; /* header CRC checksum */
+ uint32_t reserved1; /* must be 0 */
+ uint64_t my_lba; /* LBA of block that contains this struct (LBA 1) */
+ uint64_t alternative_lba; /* backup GPT header */
+ uint64_t first_usable_lba; /* first usable logical block for partitions */
+ uint64_t last_usable_lba; /* last usable logical block for partitions */
+ struct gpt_guid disk_guid; /* unique disk identifier */
+ uint64_t partition_entry_lba; /* LBA of start of partition entries array */
+ uint32_t npartition_entries; /* total partition entries - normally 128 */
+ uint32_t sizeof_partition_entry; /* bytes for each GUID pt */
+ uint32_t partition_entry_array_crc32; /* partition CRC checksum */
+ uint8_t reserved2[512 - 92]; /* must all be 0 */
+} __attribute__ ((packed));
+
+struct gpt_record {
+ uint8_t boot_indicator; /* unused by EFI, set to 0x80 for bootable */
+ uint8_t start_head; /* unused by EFI, pt start in CHS */
+ uint8_t start_sector; /* unused by EFI, pt start in CHS */
+ uint8_t start_track;
+ uint8_t os_type; /* EFI and legacy non-EFI OS types */
+ uint8_t end_head; /* unused by EFI, pt end in CHS */
+ uint8_t end_sector; /* unused by EFI, pt end in CHS */
+ uint8_t end_track; /* unused by EFI, pt end in CHS */
+ uint32_t starting_lba; /* used by EFI - start addr of the on disk pt */
+ uint32_t size_in_lba; /* used by EFI - size of pt in LBA */
+} __attribute__ ((packed));
+
+/* Protected MBR and legacy MBR share same structure */
+struct gpt_legacy_mbr {
+ uint8_t boot_code[440];
+ uint32_t unique_mbr_signature;
+ uint16_t unknown;
+ struct gpt_record partition_record[4];
+ uint16_t signature;
+} __attribute__ ((packed));
+
+/*
+ * Here be dragons!
+ * See: http://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs
+ */
+#define DEF_GUID(_u, _n) \
+ { \
+ .typestr = (_u), \
+ .name = (_n), \
+ }
+
+static struct fdisk_parttype gpt_parttypes[] =
+{
+ /* Generic OS */
+ DEF_GUID("C12A7328-F81F-11D2-BA4B-00A0C93EC93B", N_("EFI System")),
+
+ DEF_GUID("024DEE41-33E7-11D3-9D69-0008C781F39F", N_("MBR partition scheme")),
+ DEF_GUID("D3BFE2DE-3DAF-11DF-BA40-E3A556D89593", N_("Intel Fast Flash")),
+
+ /* Hah!IdontneedEFI */
+ DEF_GUID("21686148-6449-6E6F-744E-656564454649", N_("BIOS boot")),
+
+ /* Windows */
+ DEF_GUID("E3C9E316-0B5C-4DB8-817D-F92DF00215AE", N_("Microsoft reserved")),
+ DEF_GUID("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", N_("Microsoft basic data")),
+ DEF_GUID("5808C8AA-7E8F-42E0-85D2-E1E90434CFB3", N_("Microsoft LDM metadata")),
+ DEF_GUID("AF9B60A0-1431-4F62-BC68-3311714A69AD", N_("Microsoft LDM data")),
+ DEF_GUID("DE94BBA4-06D1-4D40-A16A-BFD50179D6AC", N_("Windows recovery environment")),
+ DEF_GUID("37AFFC90-EF7D-4E96-91C3-2D7AE055B174", N_("IBM General Parallel Fs")),
+ DEF_GUID("E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D", N_("Microsoft Storage Spaces")),
+
+ /* HP-UX */
+ DEF_GUID("75894C1E-3AEB-11D3-B7C1-7B03A0000000", N_("HP-UX data")),
+ DEF_GUID("E2A1E728-32E3-11D6-A682-7B03A0000000", N_("HP-UX service")),
+
+ /* Linux (http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec) */
+ DEF_GUID("0657FD6D-A4AB-43C4-84E5-0933C84B4F4F", N_("Linux swap")),
+ DEF_GUID("0FC63DAF-8483-4772-8E79-3D69D8477DE4", N_("Linux filesystem")),
+ DEF_GUID("3B8F8425-20E0-4F3B-907F-1A25A76F98E8", N_("Linux server data")),
+ DEF_GUID("44479540-F297-41B2-9AF7-D131D5F0458A", N_("Linux root (x86)")),
+ DEF_GUID("4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709", N_("Linux root (x86-64)")),
+ DEF_GUID("8DA63339-0007-60C0-C436-083AC8230908", N_("Linux reserved")),
+ DEF_GUID("933AC7E1-2EB4-4F13-B844-0E14E2AEF915", N_("Linux home")),
+ DEF_GUID("A19D880F-05FC-4D3B-A006-743F0F84911E", N_("Linux RAID")),
+ DEF_GUID("BC13C2FF-59E6-4262-A352-B275FD6F7172", N_("Linux extended boot")),
+ DEF_GUID("E6D6D379-F507-44C2-A23C-238F2A3DF928", N_("Linux LVM")),
+
+ /* FreeBSD */
+ DEF_GUID("516E7CB4-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD data")),
+ DEF_GUID("83BD6B9D-7F41-11DC-BE0B-001560B84F0F", N_("FreeBSD boot")),
+ DEF_GUID("516E7CB5-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD swap")),
+ DEF_GUID("516E7CB6-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD UFS")),
+ DEF_GUID("516E7CBA-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD ZFS")),
+ DEF_GUID("516E7CB8-6ECF-11D6-8FF8-00022D09712B", N_("FreeBSD Vinum")),
+
+ /* Apple OSX */
+ DEF_GUID("48465300-0000-11AA-AA11-00306543ECAC", N_("Apple HFS/HFS+")),
+ DEF_GUID("55465300-0000-11AA-AA11-00306543ECAC", N_("Apple UFS")),
+ DEF_GUID("52414944-0000-11AA-AA11-00306543ECAC", N_("Apple RAID")),
+ DEF_GUID("52414944-5F4F-11AA-AA11-00306543ECAC", N_("Apple RAID offline")),
+ DEF_GUID("426F6F74-0000-11AA-AA11-00306543ECAC", N_("Apple boot")),
+ DEF_GUID("4C616265-6C00-11AA-AA11-00306543ECAC", N_("Apple label")),
+ DEF_GUID("5265636F-7665-11AA-AA11-00306543ECAC", N_("Apple TV recovery")),
+ DEF_GUID("53746F72-6167-11AA-AA11-00306543ECAC", N_("Apple Core storage")),
+
+ /* Solaris */
+ DEF_GUID("6A82CB45-1DD2-11B2-99A6-080020736631", N_("Solaris boot")),
+ DEF_GUID("6A85CF4D-1DD2-11B2-99A6-080020736631", N_("Solaris root")),
+ /* same as Apple ZFS */
+ DEF_GUID("6A898CC3-1DD2-11B2-99A6-080020736631", N_("Solaris /usr & Apple ZFS")),
+ DEF_GUID("6A87C46F-1DD2-11B2-99A6-080020736631", N_("Solaris swap")),
+ DEF_GUID("6A8B642B-1DD2-11B2-99A6-080020736631", N_("Solaris backup")),
+ DEF_GUID("6A8EF2E9-1DD2-11B2-99A6-080020736631", N_("Solaris /var")),
+ DEF_GUID("6A90BA39-1DD2-11B2-99A6-080020736631", N_("Solaris /home")),
+ DEF_GUID("6A9283A5-1DD2-11B2-99A6-080020736631", N_("Solaris alternate sector")),
+ DEF_GUID("6A945A3B-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 1")),
+ DEF_GUID("6A9630D1-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 2")),
+ DEF_GUID("6A980767-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 3")),
+ DEF_GUID("6A96237F-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 4")),
+ DEF_GUID("6A8D2AC7-1DD2-11B2-99A6-080020736631", N_("Solaris reserved 5")),
+
+ /* NetBSD */
+ DEF_GUID("49F48D32-B10E-11DC-B99B-0019D1879648", N_("NetBSD swap")),
+ DEF_GUID("49F48D5A-B10E-11DC-B99B-0019D1879648", N_("NetBSD FFS")),
+ DEF_GUID("49F48D82-B10E-11DC-B99B-0019D1879648", N_("NetBSD LFS")),
+ DEF_GUID("2DB519C4-B10E-11DC-B99B-0019D1879648", N_("NetBSD concatenated")),
+ DEF_GUID("2DB519EC-B10E-11DC-B99B-0019D1879648", N_("NetBSD encrypted")),
+ DEF_GUID("49F48DAA-B10E-11DC-B99B-0019D1879648", N_("NetBSD RAID")),
+
+ /* ChromeOS */
+ DEF_GUID("FE3A2A5D-4F32-41A7-B725-ACCC3285A309", N_("ChromeOS kernel")),
+ DEF_GUID("3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC", N_("ChromeOS root fs")),
+ DEF_GUID("2E0A753D-9E48-43B0-8337-B15192CB1B5E", N_("ChromeOS reserved")),
+
+ /* MidnightBSD */
+ DEF_GUID("85D5E45A-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD data")),
+ DEF_GUID("85D5E45E-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD boot")),
+ DEF_GUID("85D5E45B-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD swap")),
+ DEF_GUID("0394Ef8B-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD UFS")),
+ DEF_GUID("85D5E45D-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD ZFS")),
+ DEF_GUID("85D5E45C-237C-11E1-B4B3-E89A8F7FC3A7", N_("MidnightBSD Vinum")),
+};
+
+/* gpt_entry macros */
+#define gpt_partition_start(_e) le64_to_cpu((_e)->lba_start)
+#define gpt_partition_end(_e) le64_to_cpu((_e)->lba_end)
+
+/*
+ * in-memory fdisk GPT stuff
+ */
+struct fdisk_gpt_label {
+ struct fdisk_label head; /* generic part */
+
+ /* gpt specific part */
+ struct gpt_header *pheader; /* primary header */
+ struct gpt_header *bheader; /* backup header */
+ struct gpt_entry *ents; /* entries (partitions) */
+};
+
+static void gpt_deinit(struct fdisk_label *lb);
+
+static inline struct fdisk_gpt_label *self_label(struct fdisk_context *cxt)
+{
+ return (struct fdisk_gpt_label *) cxt->label;
+}
+
+/*
+ * Returns the partition length, or 0 if end is before beginning.
+ */
+static uint64_t gpt_partition_size(const struct gpt_entry *e)
+{
+ uint64_t start = gpt_partition_start(e);
+ uint64_t end = gpt_partition_end(e);
+
+ return start > end ? 0 : end - start + 1ULL;
+}
+
+/* prints UUID in the real byte order! */
+static void gpt_debug_uuid(const char *mesg, struct gpt_guid *guid)
+{
+ const unsigned char *uuid = (unsigned char *) guid;
+
+ fprintf(stderr, "%s: "
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ mesg,
+ 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]);
+}
+
+/*
+ * 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(struct gpt_guid *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 string_to_guid(const char *in, struct gpt_guid *guid)
+{
+ if (uuid_parse(in, (unsigned char *) guid)) /* BE */
+ return -1;
+ swap_efi_guid(guid); /* LE */
+ return 0;
+}
+
+static char *guid_to_string(const struct gpt_guid *guid, char *out)
+{
+ struct gpt_guid u = *guid; /* LE */
+
+ swap_efi_guid(&u); /* BE */
+ uuid_unparse_upper((unsigned char *) &u, out);
+
+ return out;
+}
+
+static struct fdisk_parttype *gpt_partition_parttype(
+ struct fdisk_context *cxt,
+ const struct gpt_entry *e)
+{
+ struct fdisk_parttype *t;
+ char str[37];
+
+ guid_to_string(&e->type, str);
+ t = fdisk_label_get_parttype_from_string(cxt->label, str);
+ return t ? : fdisk_new_unknown_parttype(0, str);
+}
+
+static void gpt_entry_set_type(struct gpt_entry *e, struct gpt_guid *uuid)
+{
+ e->type = *uuid;
+ DBG(LABEL, gpt_debug_uuid("new type", &(e->type)));
+}
+
+static void gpt_entry_set_name(struct gpt_entry *e, char *str)
+{
+ char name[GPT_PART_NAME_LEN] = { 0 };
+ size_t i, sz = strlen(str);
+
+ if (sz) {
+ if (sz > GPT_PART_NAME_LEN)
+ sz = GPT_PART_NAME_LEN;
+ memcpy(name, str, sz);
+ }
+
+ for (i = 0; i < GPT_PART_NAME_LEN; i++)
+ e->name[i] = cpu_to_le16((uint16_t) name[i]);
+}
+
+static int gpt_entry_set_uuid(struct gpt_entry *e, char *str)
+{
+ struct gpt_guid uuid;
+ int rc;
+
+ rc = string_to_guid(str, &uuid);
+ if (rc)
+ return rc;
+
+ e->partition_guid = uuid;
+ return 0;
+}
+
+
+static const char *gpt_get_header_revstr(struct gpt_header *header)
+{
+ if (!header)
+ goto unknown;
+
+ switch (header->revision) {
+ case GPT_HEADER_REVISION_V1_02:
+ return "1.2";
+ case GPT_HEADER_REVISION_V1_00:
+ return "1.0";
+ case GPT_HEADER_REVISION_V0_99:
+ return "0.99";
+ default:
+ goto unknown;
+ }
+
+unknown:
+ return "unknown";
+}
+
+static inline int partition_unused(const struct gpt_entry *e)
+{
+ return !memcmp(&e->type, &GPT_UNUSED_ENTRY_GUID,
+ sizeof(struct gpt_guid));
+}
+
+/*
+ * Builds a clean new valid protective MBR - will wipe out any existing data.
+ * Returns 0 on success, otherwise < 0 on error.
+ */
+static int gpt_mknew_pmbr(struct fdisk_context *cxt)
+{
+ struct gpt_legacy_mbr *pmbr = NULL;
+ int rc;
+
+ if (!cxt || !cxt->firstsector)
+ return -ENOSYS;
+
+ rc = fdisk_init_firstsector_buffer(cxt);
+ if (rc)
+ return rc;
+
+ pmbr = (struct gpt_legacy_mbr *) cxt->firstsector;
+
+ pmbr->signature = cpu_to_le16(MSDOS_MBR_SIGNATURE);
+ pmbr->partition_record[0].os_type = EFI_PMBR_OSTYPE;
+ pmbr->partition_record[0].start_sector = 1;
+ pmbr->partition_record[0].end_head = 0xFE;
+ pmbr->partition_record[0].end_sector = 0xFF;
+ pmbr->partition_record[0].end_track = 0xFF;
+ pmbr->partition_record[0].starting_lba = cpu_to_le32(1);
+ pmbr->partition_record[0].size_in_lba =
+ cpu_to_le32(min((uint32_t) cxt->total_sectors - 1, 0xFFFFFFFF));
+
+ return 0;
+}
+
+/* some universal differences between the headers */
+static void gpt_mknew_header_common(struct fdisk_context *cxt,
+ struct gpt_header *header, uint64_t lba)
+{
+ if (!cxt || !header)
+ return;
+
+ header->my_lba = cpu_to_le64(lba);
+
+ if (lba == GPT_PRIMARY_PARTITION_TABLE_LBA) { /* primary */
+ header->alternative_lba = cpu_to_le64(cxt->total_sectors - 1);
+ header->partition_entry_lba = cpu_to_le64(2);
+ } else { /* backup */
+ uint64_t esz = le32_to_cpu(header->npartition_entries) * sizeof(struct gpt_entry);
+ uint64_t esects = (esz + cxt->sector_size - 1) / cxt->sector_size;
+
+ header->alternative_lba = cpu_to_le64(GPT_PRIMARY_PARTITION_TABLE_LBA);
+ header->partition_entry_lba = cpu_to_le64(cxt->total_sectors - 1 - esects);
+ }
+}
+
+/*
+ * Builds a new GPT header (at sector lba) from a backup header2.
+ * If building a primary header, then backup is the secondary, and vice versa.
+ *
+ * Always pass a new (zeroized) header to build upon as we don't
+ * explicitly zero-set some values such as CRCs and reserved.
+ *
+ * Returns 0 on success, otherwise < 0 on error.
+ */
+static int gpt_mknew_header_from_bkp(struct fdisk_context *cxt,
+ struct gpt_header *header,
+ uint64_t lba,
+ struct gpt_header *header2)
+{
+ if (!cxt || !header || !header2)
+ return -ENOSYS;
+
+ header->signature = header2->signature;
+ header->revision = header2->revision;
+ header->size = header2->size;
+ header->npartition_entries = header2->npartition_entries;
+ header->sizeof_partition_entry = header2->sizeof_partition_entry;
+ header->first_usable_lba = header2->first_usable_lba;
+ header->last_usable_lba = header2->last_usable_lba;
+
+ memcpy(&header->disk_guid,
+ &header2->disk_guid, sizeof(header2->disk_guid));
+ gpt_mknew_header_common(cxt, header, lba);
+
+ return 0;
+}
+
+static struct gpt_header *gpt_copy_header(struct fdisk_context *cxt,
+ struct gpt_header *src)
+{
+ struct gpt_header *res;
+
+ if (!cxt || !src)
+ return NULL;
+
+ res = calloc(1, sizeof(*res));
+ if (!res) {
+ fdisk_warn(cxt, _("failed to allocate GPT header"));
+ return NULL;
+ }
+
+ res->my_lba = src->alternative_lba;
+ res->alternative_lba = src->my_lba;
+
+ res->signature = src->signature;
+ res->revision = src->revision;
+ res->size = src->size;
+ res->npartition_entries = src->npartition_entries;
+ res->sizeof_partition_entry = src->sizeof_partition_entry;
+ res->first_usable_lba = src->first_usable_lba;
+ res->last_usable_lba = src->last_usable_lba;
+
+ memcpy(&res->disk_guid, &src->disk_guid, sizeof(src->disk_guid));
+
+
+ if (res->my_lba == GPT_PRIMARY_PARTITION_TABLE_LBA)
+ res->partition_entry_lba = cpu_to_le64(2);
+ else {
+ uint64_t esz = le32_to_cpu(src->npartition_entries) * sizeof(struct gpt_entry);
+ uint64_t esects = (esz + cxt->sector_size - 1) / cxt->sector_size;
+
+ res->partition_entry_lba = cpu_to_le64(cxt->total_sectors - 1 - esects);
+ }
+
+ return res;
+}
+
+static void count_first_last_lba(struct fdisk_context *cxt,
+ uint64_t *first, uint64_t *last)
+{
+ uint64_t esz = 0;
+
+ assert(cxt);
+
+ esz = sizeof(struct gpt_entry) * GPT_NPARTITIONS / cxt->sector_size;
+ *last = cxt->total_sectors - 2 - esz;
+ *first = esz + 2;
+
+ if (*first < cxt->first_lba && cxt->first_lba < *last)
+ /* Align according to topology */
+ *first = cxt->first_lba;
+}
+
+/*
+ * Builds a clean new GPT header (currently under revision 1.0).
+ *
+ * Always pass a new (zeroized) header to build upon as we don't
+ * explicitly zero-set some values such as CRCs and reserved.
+ *
+ * Returns 0 on success, otherwise < 0 on error.
+ */
+static int gpt_mknew_header(struct fdisk_context *cxt,
+ struct gpt_header *header, uint64_t lba)
+{
+ uint64_t first, last;
+ int has_id = 0;
+
+ if (!cxt || !header)
+ return -ENOSYS;
+
+ header->signature = cpu_to_le64(GPT_HEADER_SIGNATURE);
+ header->revision = cpu_to_le32(GPT_HEADER_REVISION_V1_00);
+ header->size = cpu_to_le32(sizeof(struct gpt_header));
+
+ /*
+ * 128 partitions are the default. It can go beyond that, but
+ * we're creating a de facto header here, so no funny business.
+ */
+ header->npartition_entries = cpu_to_le32(GPT_NPARTITIONS);
+ header->sizeof_partition_entry = cpu_to_le32(sizeof(struct gpt_entry));
+
+ count_first_last_lba(cxt, &first, &last);
+ header->first_usable_lba = cpu_to_le64(first);
+ header->last_usable_lba = cpu_to_le64(last);
+
+ gpt_mknew_header_common(cxt, header, lba);
+
+ if (cxt->script) {
+ const char *id = fdisk_script_get_header(cxt->script, "label-id");
+ if (id && string_to_guid(id, &header->disk_guid) == 0)
+ has_id = 1;
+ }
+
+ if (!has_id) {
+ uuid_generate_random((unsigned char *) &header->disk_guid);
+ swap_efi_guid(&header->disk_guid);
+ }
+ return 0;
+}
+
+/*
+ * Checks if there is a valid protective MBR partition table.
+ * Returns 0 if it is invalid or failure. Otherwise, return
+ * GPT_MBR_PROTECTIVE or GPT_MBR_HYBRID, depeding on the detection.
+ */
+static int valid_pmbr(struct fdisk_context *cxt)
+{
+ int i, part = 0, ret = 0; /* invalid by default */
+ struct gpt_legacy_mbr *pmbr = NULL;
+ uint32_t sz_lba = 0;
+
+ if (!cxt->firstsector)
+ goto done;
+
+ pmbr = (struct gpt_legacy_mbr *) cxt->firstsector;
+
+ if (le16_to_cpu(pmbr->signature) != MSDOS_MBR_SIGNATURE)
+ goto done;
+
+ /* LBA of the GPT partition header */
+ if (pmbr->partition_record[0].starting_lba !=
+ cpu_to_le32(GPT_PRIMARY_PARTITION_TABLE_LBA))
+ goto done;
+
+ /* seems like a valid MBR was found, check DOS primary partitions */
+ for (i = 0; i < 4; i++) {
+ if (pmbr->partition_record[i].os_type == EFI_PMBR_OSTYPE) {
+ /*
+ * Ok, we at least know that there's a protective MBR,
+ * now check if there are other partition types for
+ * hybrid MBR.
+ */
+ part = i;
+ ret = GPT_MBR_PROTECTIVE;
+ goto check_hybrid;
+ }
+ }
+
+ if (ret != GPT_MBR_PROTECTIVE)
+ goto done;
+check_hybrid:
+ for (i = 0 ; i < 4; i++) {
+ if ((pmbr->partition_record[i].os_type != EFI_PMBR_OSTYPE) &&
+ (pmbr->partition_record[i].os_type != 0x00))
+ ret = GPT_MBR_HYBRID;
+ }
+
+ /*
+ * Protective MBRs take up the lesser of the whole disk
+ * or 2 TiB (32bit LBA), ignoring the rest of the disk.
+ * Some partitioning programs, nonetheless, choose to set
+ * the size to the maximum 32-bit limitation, disregarding
+ * the disk size.
+ *
+ * Hybrid MBRs do not necessarily comply with this.
+ *
+ * Consider a bad value here to be a warning to support dd-ing
+ * an image from a smaller disk to a bigger disk.
+ */
+ if (ret == GPT_MBR_PROTECTIVE) {
+ sz_lba = le32_to_cpu(pmbr->partition_record[part].size_in_lba);
+ if (sz_lba != (uint32_t) cxt->total_sectors - 1 && sz_lba != 0xFFFFFFFF) {
+ fdisk_warnx(cxt, _("GPT PMBR size mismatch (%u != %u) "
+ "will be corrected by w(rite)."),
+ sz_lba,
+ (uint32_t) cxt->total_sectors - 1);
+ fdisk_label_set_changed(cxt->label, 1);
+ }
+ }
+done:
+ return ret;
+}
+
+static uint64_t last_lba(struct fdisk_context *cxt)
+{
+ struct stat s;
+ uint64_t sectors = 0;
+
+ memset(&s, 0, sizeof(s));
+ if (fstat(cxt->dev_fd, &s) == -1) {
+ fdisk_warn(cxt, _("gpt: stat() failed"));
+ return 0;
+ }
+
+ if (S_ISBLK(s.st_mode))
+ sectors = cxt->total_sectors - 1;
+ else if (S_ISREG(s.st_mode))
+ sectors = ((uint64_t) s.st_size /
+ (uint64_t) cxt->sector_size) - 1ULL;
+ else
+ fdisk_warnx(cxt, _("gpt: cannot handle files with mode %o"), s.st_mode);
+
+ DBG(LABEL, ul_debug("GPT last LBA: %ju", sectors));
+ return sectors;
+}
+
+static ssize_t read_lba(struct fdisk_context *cxt, uint64_t lba,
+ void *buffer, const size_t bytes)
+{
+ off_t offset = lba * cxt->sector_size;
+
+ if (lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1)
+ return -1;
+ return read(cxt->dev_fd, buffer, bytes) != bytes;
+}
+
+
+/* Returns the GPT entry array */
+static struct gpt_entry *gpt_read_entries(struct fdisk_context *cxt,
+ struct gpt_header *header)
+{
+ ssize_t sz;
+ struct gpt_entry *ret = NULL;
+ off_t offset;
+
+ assert(cxt);
+ assert(header);
+
+ sz = le32_to_cpu(header->npartition_entries) *
+ le32_to_cpu(header->sizeof_partition_entry);
+
+ ret = calloc(1, sz);
+ if (!ret)
+ return NULL;
+ offset = le64_to_cpu(header->partition_entry_lba) *
+ cxt->sector_size;
+
+ if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+ goto fail;
+ if (sz != read(cxt->dev_fd, ret, sz))
+ goto fail;
+
+ return ret;
+
+fail:
+ free(ret);
+ return NULL;
+}
+
+static inline uint32_t count_crc32(const unsigned char *buf, size_t len)
+{
+ return (crc32(~0L, buf, len) ^ ~0L);
+}
+
+/*
+ * Recompute header and partition array 32bit CRC checksums.
+ * This function does not fail - if there's corruption, then it
+ * will be reported when checksuming it again (ie: probing or verify).
+ */
+static void gpt_recompute_crc(struct gpt_header *header, struct gpt_entry *ents)
+{
+ uint32_t crc = 0;
+ size_t entry_sz = 0;
+
+ if (!header)
+ return;
+
+ /* header CRC */
+ header->crc32 = 0;
+ crc = count_crc32((unsigned char *) header, le32_to_cpu(header->size));
+ header->crc32 = cpu_to_le32(crc);
+
+ /* partition entry array CRC */
+ header->partition_entry_array_crc32 = 0;
+ entry_sz = le32_to_cpu(header->npartition_entries) *
+ le32_to_cpu(header->sizeof_partition_entry);
+
+ crc = count_crc32((unsigned char *) ents, entry_sz);
+ header->partition_entry_array_crc32 = cpu_to_le32(crc);
+}
+
+/*
+ * Compute the 32bit CRC checksum of the partition table header.
+ * Returns 1 if it is valid, otherwise 0.
+ */
+static int gpt_check_header_crc(struct gpt_header *header, struct gpt_entry *ents)
+{
+ uint32_t crc, orgcrc = le32_to_cpu(header->crc32);
+
+ header->crc32 = 0;
+ crc = count_crc32((unsigned char *) header, le32_to_cpu(header->size));
+ header->crc32 = cpu_to_le32(orgcrc);
+
+ if (crc == le32_to_cpu(header->crc32))
+ return 1;
+
+ /*
+ * If we have checksum mismatch it may be due to stale data,
+ * like a partition being added or deleted. Recompute the CRC again
+ * and make sure this is not the case.
+ */
+ if (ents) {
+ gpt_recompute_crc(header, ents);
+ orgcrc = le32_to_cpu(header->crc32);
+ header->crc32 = 0;
+ crc = count_crc32((unsigned char *) header, le32_to_cpu(header->size));
+ header->crc32 = cpu_to_le32(orgcrc);
+
+ return crc == le32_to_cpu(header->crc32);
+ }
+
+ return 0;
+}
+
+/*
+ * It initializes the partition entry array.
+ * Returns 1 if the checksum is valid, otherwise 0.
+ */
+static int gpt_check_entryarr_crc(struct gpt_header *header,
+ struct gpt_entry *ents)
+{
+ int ret = 0;
+ ssize_t entry_sz;
+ uint32_t crc;
+
+ if (!header || !ents)
+ goto done;
+
+ entry_sz = le32_to_cpu(header->npartition_entries) *
+ le32_to_cpu(header->sizeof_partition_entry);
+
+ if (!entry_sz)
+ goto done;
+
+ crc = count_crc32((unsigned char *) ents, entry_sz);
+ ret = (crc == le32_to_cpu(header->partition_entry_array_crc32));
+done:
+ return ret;
+}
+
+static int gpt_check_lba_sanity(struct fdisk_context *cxt, struct gpt_header *header)
+{
+ int ret = 0;
+ uint64_t lu, fu, lastlba = last_lba(cxt);
+
+ fu = le64_to_cpu(header->first_usable_lba);
+ lu = le64_to_cpu(header->last_usable_lba);
+
+ /* check if first and last usable LBA make sense */
+ if (lu < fu) {
+ DBG(LABEL, ul_debug("error: header last LBA is before first LBA"));
+ goto done;
+ }
+
+ /* check if first and last usable LBAs with the disk's last LBA */
+ if (fu > lastlba || lu > lastlba) {
+ DBG(LABEL, ul_debug("error: header LBAs are after the disk's last LBA"));
+ goto done;
+ }
+
+ /* the header has to be outside usable range */
+ if (fu < GPT_PRIMARY_PARTITION_TABLE_LBA &&
+ GPT_PRIMARY_PARTITION_TABLE_LBA < lu) {
+ DBG(LABEL, ul_debug("error: header outside of usable range"));
+ goto done;
+ }
+
+ ret = 1; /* sane */
+done:
+ return ret;
+}
+
+/* Check if there is a valid header signature */
+static int gpt_check_signature(struct gpt_header *header)
+{
+ return header->signature == cpu_to_le64(GPT_HEADER_SIGNATURE);
+}
+
+/*
+ * Return the specified GPT Header, or NULL upon failure/invalid.
+ * Note that all tests must pass to ensure a valid header,
+ * we do not rely on only testing the signature for a valid probe.
+ */
+static struct gpt_header *gpt_read_header(struct fdisk_context *cxt,
+ uint64_t lba,
+ struct gpt_entry **_ents)
+{
+ struct gpt_header *header = NULL;
+ struct gpt_entry *ents = NULL;
+ uint32_t hsz;
+
+ if (!cxt)
+ return NULL;
+
+ header = calloc(1, sizeof(*header));
+ if (!header)
+ return NULL;
+
+ /* read and verify header */
+ if (read_lba(cxt, lba, header, sizeof(struct gpt_header)) != 0)
+ goto invalid;
+
+ if (!gpt_check_signature(header))
+ goto invalid;
+
+ if (!gpt_check_header_crc(header, NULL))
+ goto invalid;
+
+ /* read and verify entries */
+ ents = gpt_read_entries(cxt, header);
+ if (!ents)
+ goto invalid;
+
+ if (!gpt_check_entryarr_crc(header, ents))
+ goto invalid;
+
+ if (!gpt_check_lba_sanity(cxt, header))
+ goto invalid;
+
+ /* valid header must be at MyLBA */
+ if (le64_to_cpu(header->my_lba) != lba)
+ goto invalid;
+
+ /* make sure header size is between 92 and sector size bytes */
+ hsz = le32_to_cpu(header->size);
+ if (hsz < GPT_HEADER_MINSZ || hsz > cxt->sector_size)
+ goto invalid;
+
+ if (_ents)
+ *_ents = ents;
+ else
+ free(ents);
+
+ DBG(LABEL, ul_debug("found valid GPT Header on LBA %ju", lba));
+ return header;
+invalid:
+ free(header);
+ free(ents);
+
+ DBG(LABEL, ul_debug("read GPT Header on LBA %ju failed", lba));
+ return NULL;
+}
+
+
+static int gpt_locate_disklabel(struct fdisk_context *cxt, int n,
+ const char **name, off_t *offset, size_t *size)
+{
+ struct fdisk_gpt_label *gpt;
+
+ assert(cxt);
+
+ *name = NULL;
+ *offset = 0;
+ *size = 0;
+
+ switch (n) {
+ case 0:
+ *name = "PMBR";
+ *offset = 0;
+ *size = 512;
+ break;
+ case 1:
+ *name = _("GPT Header");
+ *offset = GPT_PRIMARY_PARTITION_TABLE_LBA * cxt->sector_size;
+ *size = sizeof(struct gpt_header);
+ break;
+ case 2:
+ *name = _("GPT Entries");
+ gpt = self_label(cxt);
+ *offset = le64_to_cpu(gpt->pheader->partition_entry_lba) * cxt->sector_size;
+ *size = le32_to_cpu(gpt->pheader->npartition_entries) *
+ le32_to_cpu(gpt->pheader->sizeof_partition_entry);
+ break;
+ default:
+ return 1; /* no more chunks */
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Returns the number of partitions that are in use.
+ */
+static unsigned partitions_in_use(struct gpt_header *header,
+ struct gpt_entry *ents)
+{
+ uint32_t i, used = 0;
+
+ if (!header || ! ents)
+ return 0;
+
+ for (i = 0; i < le32_to_cpu(header->npartition_entries); i++)
+ if (!partition_unused(&ents[i]))
+ used++;
+ return used;
+}
+
+
+/*
+ * Check if a partition is too big for the disk (sectors).
+ * Returns the faulting partition number, otherwise 0.
+ */
+static uint32_t check_too_big_partitions(struct gpt_header *header,
+ struct gpt_entry *ents, uint64_t sectors)
+{
+ uint32_t i;
+
+ for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+ if (partition_unused(&ents[i]))
+ continue;
+ if (gpt_partition_end(&ents[i]) >= sectors)
+ return i + 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Check if a partition ends before it begins
+ * Returns the faulting partition number, otherwise 0.
+ */
+static uint32_t check_start_after_end_paritions(struct gpt_header *header,
+ struct gpt_entry *ents)
+{
+ uint32_t i;
+
+ for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+ if (partition_unused(&ents[i]))
+ continue;
+ if (gpt_partition_start(&ents[i]) > gpt_partition_end(&ents[i]))
+ return i + 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Check if partition e1 overlaps with partition e2.
+ */
+static inline int partition_overlap(struct gpt_entry *e1, struct gpt_entry *e2)
+{
+ uint64_t start1 = gpt_partition_start(e1);
+ uint64_t end1 = gpt_partition_end(e1);
+ uint64_t start2 = gpt_partition_start(e2);
+ uint64_t end2 = gpt_partition_end(e2);
+
+ return (start1 && start2 && (start1 <= end2) != (end1 < start2));
+}
+
+/*
+ * Find any partitions that overlap.
+ */
+static uint32_t check_overlap_partitions(struct gpt_header *header,
+ struct gpt_entry *ents)
+{
+ uint32_t i, j;
+
+ for (i = 0; i < le32_to_cpu(header->npartition_entries); i++)
+ for (j = 0; j < i; j++) {
+ if (partition_unused(&ents[i]) ||
+ partition_unused(&ents[j]))
+ continue;
+ if (partition_overlap(&ents[i], &ents[j])) {
+ DBG(LABEL, ul_debug("GPT partitions overlap detected [%u vs. %u]", i, j));
+ return i + 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Find the first available block after the starting point; returns 0 if
+ * there are no available blocks left, or error. From gdisk.
+ */
+static uint64_t find_first_available(struct gpt_header *header,
+ struct gpt_entry *ents, uint64_t start)
+{
+ uint64_t first;
+ uint32_t i, first_moved = 0;
+
+ uint64_t fu, lu;
+
+ if (!header || !ents)
+ return 0;
+
+ fu = le64_to_cpu(header->first_usable_lba);
+ lu = le64_to_cpu(header->last_usable_lba);
+
+ /*
+ * Begin from the specified starting point or from the first usable
+ * LBA, whichever is greater...
+ */
+ first = start < fu ? fu : start;
+
+ /*
+ * Now search through all partitions; if first is within an
+ * existing partition, move it to the next sector after that
+ * partition and repeat. If first was moved, set firstMoved
+ * flag; repeat until firstMoved is not set, so as to catch
+ * cases where partitions are out of sequential order....
+ */
+ do {
+ first_moved = 0;
+ for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+ if (partition_unused(&ents[i]))
+ continue;
+ if (first < gpt_partition_start(&ents[i]))
+ continue;
+ if (first <= gpt_partition_end(&ents[i])) {
+ first = gpt_partition_end(&ents[i]) + 1;
+ first_moved = 1;
+ }
+ }
+ } while (first_moved == 1);
+
+ if (first > lu)
+ first = 0;
+
+ return first;
+}
+
+
+/* Returns last available sector in the free space pointed to by start. From gdisk. */
+static uint64_t find_last_free(struct gpt_header *header,
+ struct gpt_entry *ents, uint64_t start)
+{
+ uint32_t i;
+ uint64_t nearest_start;
+
+ if (!header || !ents)
+ return 0;
+
+ nearest_start = le64_to_cpu(header->last_usable_lba);
+
+ for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+ uint64_t ps = gpt_partition_start(&ents[i]);
+
+ if (nearest_start > ps && ps > start)
+ nearest_start = ps - 1;
+ }
+
+ return nearest_start;
+}
+
+/* Returns the last free sector on the disk. From gdisk. */
+static uint64_t find_last_free_sector(struct gpt_header *header,
+ struct gpt_entry *ents)
+{
+ uint32_t i, last_moved;
+ uint64_t last = 0;
+
+ if (!header || !ents)
+ goto done;
+
+ /* start by assuming the last usable LBA is available */
+ last = le64_to_cpu(header->last_usable_lba);
+ do {
+ last_moved = 0;
+ for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+ if ((last >= gpt_partition_start(&ents[i])) &&
+ (last <= gpt_partition_end(&ents[i]))) {
+ last = gpt_partition_start(&ents[i]) - 1;
+ last_moved = 1;
+ }
+ }
+ } while (last_moved == 1);
+done:
+ return last;
+}
+
+/*
+ * Finds the first available sector in the largest block of unallocated
+ * space on the disk. Returns 0 if there are no available blocks left.
+ * From gdisk.
+ */
+static uint64_t find_first_in_largest(struct gpt_header *header,
+ struct gpt_entry *ents)
+{
+ uint64_t start = 0, first_sect, last_sect;
+ uint64_t segment_size, selected_size = 0, selected_segment = 0;
+
+ if (!header || !ents)
+ goto done;
+
+ do {
+ first_sect = find_first_available(header, ents, start);
+ if (first_sect != 0) {
+ last_sect = find_last_free(header, ents, first_sect);
+ segment_size = last_sect - first_sect + 1;
+
+ if (segment_size > selected_size) {
+ selected_size = segment_size;
+ selected_segment = first_sect;
+ }
+ start = last_sect + 1;
+ }
+ } while (first_sect != 0);
+
+done:
+ return selected_segment;
+}
+
+/*
+ * Find the total number of free sectors, the number of segments in which
+ * they reside, and the size of the largest of those segments. From gdisk.
+ */
+static uint64_t get_free_sectors(struct fdisk_context *cxt, struct gpt_header *header,
+ struct gpt_entry *ents, uint32_t *nsegments,
+ uint64_t *largest_segment)
+{
+ uint32_t num = 0;
+ uint64_t first_sect, last_sect;
+ uint64_t largest_seg = 0, segment_sz;
+ uint64_t totfound = 0, start = 0; /* starting point for each search */
+
+ if (!cxt->total_sectors)
+ goto done;
+
+ do {
+ first_sect = find_first_available(header, ents, start);
+ if (first_sect) {
+ last_sect = find_last_free(header, ents, first_sect);
+ segment_sz = last_sect - first_sect + 1;
+
+ if (segment_sz > largest_seg)
+ largest_seg = segment_sz;
+ totfound += segment_sz;
+ num++;
+ start = last_sect + 1;
+ }
+ } while (first_sect);
+
+done:
+ if (nsegments)
+ *nsegments = num;
+ if (largest_segment)
+ *largest_segment = largest_seg;
+
+ return totfound;
+}
+
+static int gpt_probe_label(struct fdisk_context *cxt)
+{
+ int mbr_type;
+ struct fdisk_gpt_label *gpt;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ gpt = self_label(cxt);
+
+ /* TODO: it would be nice to support scenario when GPT headers are OK,
+ * but PMBR is corrupt */
+ mbr_type = valid_pmbr(cxt);
+ if (!mbr_type)
+ goto failed;
+
+ DBG(LABEL, ul_debug("found a %s MBR", mbr_type == GPT_MBR_PROTECTIVE ?
+ "protective" : "hybrid"));
+
+ /* primary header */
+ gpt->pheader = gpt_read_header(cxt, GPT_PRIMARY_PARTITION_TABLE_LBA,
+ &gpt->ents);
+
+ if (gpt->pheader)
+ /* primary OK, try backup from alternative LBA */
+ gpt->bheader = gpt_read_header(cxt,
+ le64_to_cpu(gpt->pheader->alternative_lba),
+ NULL);
+ else
+ /* primary corrupted -- try last LBA */
+ gpt->bheader = gpt_read_header(cxt, last_lba(cxt), &gpt->ents);
+
+ if (!gpt->pheader && !gpt->bheader)
+ goto failed;
+
+ /* primary OK, backup corrupted -- recovery */
+ if (gpt->pheader && !gpt->bheader) {
+ fdisk_warnx(cxt, _("The backup GPT table is corrupt, but the "
+ "primary appears OK, so that will be used."));
+ gpt->bheader = gpt_copy_header(cxt, gpt->pheader);
+ if (!gpt->bheader)
+ goto failed;
+ gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+ /* primary corrupted, backup OK -- recovery */
+ } else if (!gpt->pheader && gpt->bheader) {
+ fdisk_warnx(cxt, _("The primary GPT table is corrupt, but the "
+ "backup appears OK, so that will be used."));
+ gpt->pheader = gpt_copy_header(cxt, gpt->bheader);
+ if (!gpt->pheader)
+ goto failed;
+ gpt_recompute_crc(gpt->pheader, gpt->ents);
+ }
+
+ cxt->label->nparts_max = le32_to_cpu(gpt->pheader->npartition_entries);
+ cxt->label->nparts_cur = partitions_in_use(gpt->pheader, gpt->ents);
+ return 1;
+failed:
+ DBG(LABEL, ul_debug("GPT probe failed"));
+ gpt_deinit(cxt->label);
+ return 0;
+}
+
+/*
+ * Stolen from libblkid - can be removed once partition semantics
+ * are added to the fdisk API.
+ */
+static char *encode_to_utf8(unsigned char *src, size_t count)
+{
+ uint16_t c;
+ char *dest;
+ size_t i, j, len = count;
+
+ dest = calloc(1, count);
+ if (!dest)
+ return NULL;
+
+ for (j = i = 0; i + 2 <= count; i += 2) {
+ /* always little endian */
+ c = (src[i+1] << 8) | src[i];
+ if (c == 0) {
+ dest[j] = '\0';
+ break;
+ } else if (c < 0x80) {
+ if (j+1 >= len)
+ break;
+ dest[j++] = (uint8_t) c;
+ } else if (c < 0x800) {
+ if (j+2 >= len)
+ break;
+ dest[j++] = (uint8_t) (0xc0 | (c >> 6));
+ dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
+ } else {
+ if (j+3 >= len)
+ break;
+ dest[j++] = (uint8_t) (0xe0 | (c >> 12));
+ dest[j++] = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
+ dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
+ }
+ }
+ dest[j] = '\0';
+
+ return dest;
+}
+
+static int gpt_entry_attrs_to_string(struct gpt_entry *e, char **res)
+{
+ unsigned int n, count = 0;
+ size_t l;
+ char *bits, *p;
+ uint64_t attrs;
+
+ assert(e);
+ assert(res);
+
+ *res = NULL;
+ attrs = le64_to_cpu(e->attrs);
+ if (!attrs)
+ return 0; /* no attributes at all */
+
+ bits = (char *) &attrs;
+
+ /* Note that sizeof() is correct here, we need separators between
+ * the strings so also count \0 is correct */
+ *res = calloc(1, sizeof(GPT_ATTRSTR_NOBLOCK) +
+ sizeof(GPT_ATTRSTR_REQ) +
+ sizeof(GPT_ATTRSTR_LEGACY) +
+ sizeof("GUID:") + (GPT_ATTRBIT_GUID_COUNT * 3));
+ if (!*res)
+ return -errno;
+
+ p = *res;
+ if (isset(bits, GPT_ATTRBIT_REQ)) {
+ memcpy(p, GPT_ATTRSTR_REQ, (l = sizeof(GPT_ATTRSTR_REQ)));
+ p += l - 1;
+ }
+ if (isset(bits, GPT_ATTRBIT_NOBLOCK)) {
+ if (p > *res)
+ *p++ = ' ';
+ memcpy(p, GPT_ATTRSTR_NOBLOCK, (l = sizeof(GPT_ATTRSTR_NOBLOCK)));
+ p += l - 1;
+ }
+ if (isset(bits, GPT_ATTRBIT_LEGACY)) {
+ if (p > *res)
+ *p++ = ' ';
+ memcpy(p, GPT_ATTRSTR_LEGACY, (l = sizeof(GPT_ATTRSTR_LEGACY)));
+ p += l - 1;
+ }
+
+ for (n = GPT_ATTRBIT_GUID_FIRST;
+ n < GPT_ATTRBIT_GUID_FIRST + GPT_ATTRBIT_GUID_COUNT; n++) {
+
+ if (!isset(bits, n))
+ continue;
+ if (!count) {
+ if (p > *res)
+ *p++ = ' ';
+ p += sprintf(p, "GUID:%u", n);
+ } else
+ p += sprintf(p, ",%u", n);
+ count++;
+ }
+
+ return 0;
+}
+
+static int gpt_entry_attrs_from_string(
+ struct fdisk_context *cxt,
+ struct gpt_entry *e,
+ const char *str)
+{
+ const char *p = str;
+ uint64_t attrs = 0;
+ char *bits;
+
+ assert(e);
+ assert(p);
+
+ DBG(LABEL, ul_debug("GPT: parsing string attributes '%s'", p));
+
+ bits = (char *) &attrs;
+
+ while (p && *p) {
+ int bit = -1;
+
+ while (isblank(*p)) p++;
+ if (!*p)
+ break;
+
+ DBG(LABEL, ul_debug(" parsing item '%s'", p));
+
+ if (strncmp(p, "GUID:", 5) == 0) {
+ p += 5;
+ continue;
+ } else if (strncmp(p, GPT_ATTRSTR_REQ,
+ sizeof(GPT_ATTRSTR_REQ) - 1) == 0) {
+ bit = GPT_ATTRBIT_REQ;
+ p += sizeof(GPT_ATTRSTR_REQ) - 1;
+ } else if (strncmp(p, GPT_ATTRSTR_LEGACY,
+ sizeof(GPT_ATTRSTR_LEGACY) - 1) == 0) {
+ bit = GPT_ATTRBIT_LEGACY;
+ p += sizeof(GPT_ATTRSTR_LEGACY) - 1;
+ } else if (strncmp(p, GPT_ATTRSTR_NOBLOCK,
+ sizeof(GPT_ATTRSTR_NOBLOCK) - 1) == 0) {
+ bit = GPT_ATTRBIT_NOBLOCK;
+ p += sizeof(GPT_ATTRSTR_NOBLOCK) - 1;
+ } else if (isdigit((unsigned int) *p)) {
+ char *end = NULL;
+
+ errno = 0;
+ bit = strtol(p, &end, 0);
+ if (errno || !end || end == str
+ || bit < GPT_ATTRBIT_GUID_FIRST
+ || bit >= GPT_ATTRBIT_GUID_FIRST + GPT_ATTRBIT_GUID_COUNT)
+ bit = -1;
+ else
+ p = end;
+ }
+
+ if (bit < 0) {
+ fdisk_warnx(cxt, _("unssuported GPT attribute bit '%s'"), p);
+ return -EINVAL;
+ }
+
+ setbit(bits, bit);
+
+ while (isblank(*p)) p++;
+ if (*p == ',')
+ p++;
+ }
+
+ e->attrs = cpu_to_le64(attrs);
+ return 0;
+}
+
+static int gpt_get_partition(struct fdisk_context *cxt, size_t n,
+ struct fdisk_partition *pa)
+{
+ struct fdisk_gpt_label *gpt;
+ struct gpt_entry *e;
+ char u_str[37];
+ int rc = 0;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ gpt = self_label(cxt);
+
+ if ((uint32_t) n >= le32_to_cpu(gpt->pheader->npartition_entries))
+ return -EINVAL;
+
+ gpt = self_label(cxt);
+ e = &gpt->ents[n];
+
+ pa->used = !partition_unused(e) || gpt_partition_start(e);
+ if (!pa->used)
+ return 0;
+
+ pa->start = gpt_partition_start(e);
+ pa->size = gpt_partition_size(e);
+ pa->type = gpt_partition_parttype(cxt, e);
+
+ if (guid_to_string(&e->partition_guid, u_str)) {
+ pa->uuid = strdup(u_str);
+ if (!pa->uuid) {
+ rc = -errno;
+ goto done;
+ }
+ } else
+ pa->uuid = NULL;
+
+ rc = gpt_entry_attrs_to_string(e, &pa->attrs);
+ if (rc)
+ goto done;
+
+ pa->name = encode_to_utf8((unsigned char *)e->name, sizeof(e->name));
+ return 0;
+done:
+ fdisk_reset_partition(pa);
+ return rc;
+}
+
+
+static int gpt_set_partition(struct fdisk_context *cxt, size_t n,
+ struct fdisk_partition *pa)
+{
+ struct fdisk_gpt_label *gpt;
+ struct gpt_entry *e;
+ int rc = 0;
+ uint64_t start, end;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ gpt = self_label(cxt);
+
+ if ((uint32_t) n >= le32_to_cpu(gpt->pheader->npartition_entries))
+ return -EINVAL;
+
+ FDISK_INIT_UNDEF(start);
+ FDISK_INIT_UNDEF(end);
+
+ gpt = self_label(cxt);
+ e = &gpt->ents[n];
+
+ if (pa->uuid) {
+ char new_u[37], old_u[37];
+
+ guid_to_string(&e->partition_guid, old_u);
+ rc = gpt_entry_set_uuid(e, pa->uuid);
+ if (rc)
+ return rc;
+ guid_to_string(&e->partition_guid, new_u);
+ fdisk_info(cxt, _("Partition UUID changed from %s to %s."),
+ old_u, new_u);
+ }
+
+ if (pa->name) {
+ char *old = encode_to_utf8((unsigned char *)e->name, sizeof(e->name));
+ gpt_entry_set_name(e, pa->name);
+
+ fdisk_info(cxt, _("Partition name changed from '%s' to '%.*s'."),
+ old, (int) GPT_PART_NAME_LEN, pa->name);
+ free(old);
+ }
+
+ if (pa->type && pa->type->typestr) {
+ struct gpt_guid typeid;
+
+ rc = string_to_guid(pa->type->typestr, &typeid);
+ if (rc)
+ return rc;
+ gpt_entry_set_type(e, &typeid);
+ }
+ if (pa->attrs) {
+ rc = gpt_entry_attrs_from_string(cxt, e, pa->attrs);
+ if (rc)
+ return rc;
+ }
+
+ if (fdisk_partition_has_start(pa))
+ start = pa->start;
+ if (fdisk_partition_has_size(pa))
+ end = gpt_partition_start(e) + pa->size - 1ULL;
+
+ if (pa->end_follow_default) {
+ /* enlarge */
+ if (!FDISK_IS_UNDEF(start))
+ start = gpt_partition_start(e);
+ end = find_last_free(gpt->bheader, gpt->ents, start);
+ if (!end)
+ FDISK_INIT_UNDEF(end);
+ }
+
+ if (!FDISK_IS_UNDEF(start))
+ e->lba_start = cpu_to_le64(start);
+ if (!FDISK_IS_UNDEF(end))
+ e->lba_end = cpu_to_le64(end);
+
+ gpt_recompute_crc(gpt->pheader, gpt->ents);
+ gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+ fdisk_label_set_changed(cxt->label, 1);
+ return rc;
+}
+
+
+/*
+ * List label partitions.
+ */
+static int gpt_list_disklabel(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ if (fdisk_is_details(cxt)) {
+ struct gpt_header *h = self_label(cxt)->pheader;
+
+ fdisk_info(cxt, _("First LBA: %ju"), h->first_usable_lba);
+ fdisk_info(cxt, _("Last LBA: %ju"), h->last_usable_lba);
+ /* TRANSLATORS: The LBA (Logical Block Address) of the backup GPT header. */
+ fdisk_info(cxt, _("Alternative LBA: %ju"), h->alternative_lba);
+ /* TRANSLATORS: The start of the array of partition entries. */
+ fdisk_info(cxt, _("Partition entries LBA: %ju"), h->partition_entry_lba);
+ fdisk_info(cxt, _("Allocated partition entries: %u"), h->npartition_entries);
+ }
+
+ return 0;
+}
+
+/*
+ * Write partitions.
+ * Returns 0 on success, or corresponding error otherwise.
+ */
+static int gpt_write_partitions(struct fdisk_context *cxt,
+ struct gpt_header *header, struct gpt_entry *ents)
+{
+ off_t offset = le64_to_cpu(header->partition_entry_lba) * cxt->sector_size;
+ uint32_t nparts = le32_to_cpu(header->npartition_entries);
+ uint32_t totwrite = nparts * le32_to_cpu(header->sizeof_partition_entry);
+ ssize_t rc;
+
+ if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+ goto fail;
+
+ rc = write(cxt->dev_fd, ents, totwrite);
+ if (rc > 0 && totwrite == (uint32_t) rc)
+ return 0;
+fail:
+ return -errno;
+}
+
+/*
+ * Write a GPT header to a specified LBA
+ * Returns 0 on success, or corresponding error otherwise.
+ */
+static int gpt_write_header(struct fdisk_context *cxt,
+ struct gpt_header *header, uint64_t lba)
+{
+ off_t offset = lba * cxt->sector_size;
+
+ if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+ goto fail;
+ if (cxt->sector_size ==
+ (size_t) write(cxt->dev_fd, header, cxt->sector_size))
+ return 0;
+fail:
+ return -errno;
+}
+
+/*
+ * Write the protective MBR.
+ * Returns 0 on success, or corresponding error otherwise.
+ */
+static int gpt_write_pmbr(struct fdisk_context *cxt)
+{
+ off_t offset;
+ struct gpt_legacy_mbr *pmbr = NULL;
+
+ assert(cxt);
+ assert(cxt->firstsector);
+
+ pmbr = (struct gpt_legacy_mbr *) cxt->firstsector;
+
+ /* zero out the legacy partitions */
+ memset(pmbr->partition_record, 0, sizeof(pmbr->partition_record));
+
+ pmbr->signature = cpu_to_le16(MSDOS_MBR_SIGNATURE);
+ pmbr->partition_record[0].os_type = EFI_PMBR_OSTYPE;
+ pmbr->partition_record[0].start_sector = 1;
+ pmbr->partition_record[0].end_head = 0xFE;
+ pmbr->partition_record[0].end_sector = 0xFF;
+ pmbr->partition_record[0].end_track = 0xFF;
+ pmbr->partition_record[0].starting_lba = cpu_to_le32(1);
+
+ /*
+ * Set size_in_lba to the size of the disk minus one. If the size of the disk
+ * is too large to be represented by a 32bit LBA (2Tb), set it to 0xFFFFFFFF.
+ */
+ if (cxt->total_sectors - 1 > 0xFFFFFFFFULL)
+ pmbr->partition_record[0].size_in_lba = cpu_to_le32(0xFFFFFFFF);
+ else
+ pmbr->partition_record[0].size_in_lba =
+ cpu_to_le32(cxt->total_sectors - 1UL);
+
+ offset = GPT_PMBR_LBA * cxt->sector_size;
+ if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+ goto fail;
+
+ /* pMBR covers the first sector (LBA) of the disk */
+ if (write_all(cxt->dev_fd, pmbr, cxt->sector_size))
+ goto fail;
+ return 0;
+fail:
+ return -errno;
+}
+
+/*
+ * Writes in-memory GPT and pMBR data to disk.
+ * Returns 0 if successful write, otherwise, a corresponding error.
+ * Any indication of error will abort the operation.
+ */
+static int gpt_write_disklabel(struct fdisk_context *cxt)
+{
+ struct fdisk_gpt_label *gpt;
+ int mbr_type;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ gpt = self_label(cxt);
+ mbr_type = valid_pmbr(cxt);
+
+ /* check that disk is big enough to handle the backup header */
+ if (le64_to_cpu(gpt->pheader->alternative_lba) > cxt->total_sectors)
+ goto err0;
+
+ /* check that the backup header is properly placed */
+ if (le64_to_cpu(gpt->pheader->alternative_lba) < cxt->total_sectors - 1)
+ /* TODO: correct this (with user authorization) and write */
+ goto err0;
+
+ if (check_overlap_partitions(gpt->pheader, gpt->ents))
+ goto err0;
+
+ /* recompute CRCs for both headers */
+ gpt_recompute_crc(gpt->pheader, gpt->ents);
+ gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+ /*
+ * UEFI requires writing in this specific order:
+ * 1) backup partition tables
+ * 2) backup GPT header
+ * 3) primary partition tables
+ * 4) primary GPT header
+ * 5) protective MBR
+ *
+ * If any write fails, we abort the rest.
+ */
+ if (gpt_write_partitions(cxt, gpt->bheader, gpt->ents) != 0)
+ goto err1;
+ if (gpt_write_header(cxt, gpt->bheader,
+ le64_to_cpu(gpt->pheader->alternative_lba)) != 0)
+ goto err1;
+ if (gpt_write_partitions(cxt, gpt->pheader, gpt->ents) != 0)
+ goto err1;
+ if (gpt_write_header(cxt, gpt->pheader, GPT_PRIMARY_PARTITION_TABLE_LBA) != 0)
+ goto err1;
+
+ if (mbr_type == GPT_MBR_HYBRID)
+ fdisk_warnx(cxt, _("The device contains hybrid MBR -- writing GPT only. "
+ "You have to sync the MBR manually."));
+ else if (gpt_write_pmbr(cxt) != 0)
+ goto err1;
+
+ DBG(LABEL, ul_debug("GPT write success"));
+ return 0;
+err0:
+ DBG(LABEL, ul_debug("GPT write failed: incorrect input"));
+ errno = EINVAL;
+ return -EINVAL;
+err1:
+ DBG(LABEL, ul_debug("GPT write failed: %m"));
+ return -errno;
+}
+
+/*
+ * Verify data integrity and report any found problems for:
+ * - primary and backup header validations
+ * - paritition validations
+ */
+static int gpt_verify_disklabel(struct fdisk_context *cxt)
+{
+ int nerror = 0;
+ unsigned int ptnum;
+ struct fdisk_gpt_label *gpt;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ gpt = self_label(cxt);
+
+ if (!gpt || !gpt->bheader) {
+ nerror++;
+ fdisk_warnx(cxt, _("Disk does not contain a valid backup header."));
+ }
+
+ if (!gpt_check_header_crc(gpt->pheader, gpt->ents)) {
+ nerror++;
+ fdisk_warnx(cxt, _("Invalid primary header CRC checksum."));
+ }
+ if (gpt->bheader && !gpt_check_header_crc(gpt->bheader, gpt->ents)) {
+ nerror++;
+ fdisk_warnx(cxt, _("Invalid backup header CRC checksum."));
+ }
+
+ if (!gpt_check_entryarr_crc(gpt->pheader, gpt->ents)) {
+ nerror++;
+ fdisk_warnx(cxt, _("Invalid partition entry checksum."));
+ }
+
+ if (!gpt_check_lba_sanity(cxt, gpt->pheader)) {
+ nerror++;
+ fdisk_warnx(cxt, _("Invalid primary header LBA sanity checks."));
+ }
+ if (gpt->bheader && !gpt_check_lba_sanity(cxt, gpt->bheader)) {
+ nerror++;
+ fdisk_warnx(cxt, _("Invalid backup header LBA sanity checks."));
+ }
+
+ if (le64_to_cpu(gpt->pheader->my_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA) {
+ nerror++;
+ fdisk_warnx(cxt, _("MyLBA mismatch with real position at primary header."));
+ }
+ if (gpt->bheader && le64_to_cpu(gpt->bheader->my_lba) != last_lba(cxt)) {
+ nerror++;
+ fdisk_warnx(cxt, _("MyLBA mismatch with real position at backup header."));
+
+ }
+ if (le64_to_cpu(gpt->pheader->alternative_lba) >= cxt->total_sectors) {
+ nerror++;
+ fdisk_warnx(cxt, _("Disk is too small to hold all data."));
+ }
+
+ /*
+ * if the GPT is the primary table, check the alternateLBA
+ * to see if it is a valid GPT
+ */
+ if (gpt->bheader && (le64_to_cpu(gpt->pheader->my_lba) !=
+ le64_to_cpu(gpt->bheader->alternative_lba))) {
+ nerror++;
+ fdisk_warnx(cxt, _("Primary and backup header mismatch."));
+ }
+
+ ptnum = check_overlap_partitions(gpt->pheader, gpt->ents);
+ if (ptnum) {
+ nerror++;
+ fdisk_warnx(cxt, _("Partition %u overlaps with partition %u."),
+ ptnum, ptnum+1);
+ }
+
+ ptnum = check_too_big_partitions(gpt->pheader, gpt->ents, cxt->total_sectors);
+ if (ptnum) {
+ nerror++;
+ fdisk_warnx(cxt, _("Partition %u is too big for the disk."),
+ ptnum);
+ }
+
+ ptnum = check_start_after_end_paritions(gpt->pheader, gpt->ents);
+ if (ptnum) {
+ nerror++;
+ fdisk_warnx(cxt, _("Partition %u ends before it starts."),
+ ptnum);
+ }
+
+ if (!nerror) { /* yay :-) */
+ uint32_t nsegments = 0;
+ uint64_t free_sectors = 0, largest_segment = 0;
+ char *strsz = NULL;
+
+ fdisk_info(cxt, _("No errors detected."));
+ fdisk_info(cxt, _("Header version: %s"), gpt_get_header_revstr(gpt->pheader));
+ fdisk_info(cxt, _("Using %u out of %d partitions."),
+ partitions_in_use(gpt->pheader, gpt->ents),
+ le32_to_cpu(gpt->pheader->npartition_entries));
+
+ free_sectors = get_free_sectors(cxt, gpt->pheader, gpt->ents,
+ &nsegments, &largest_segment);
+ if (largest_segment)
+ strsz = size_to_human_string(SIZE_SUFFIX_SPACE | SIZE_SUFFIX_3LETTER,
+ largest_segment * cxt->sector_size);
+
+ fdisk_info(cxt,
+ P_("A total of %ju free sectors is available in %u segment.",
+ "A total of %ju free sectors is available in %u segments "
+ "(the largest is %s).", nsegments),
+ free_sectors, nsegments, strsz);
+ free(strsz);
+
+ } else
+ fdisk_warnx(cxt,
+ P_("%d error detected.", "%d errors detected.", nerror),
+ nerror);
+
+ return 0;
+}
+
+/* Delete a single GPT partition, specified by partnum. */
+static int gpt_delete_partition(struct fdisk_context *cxt,
+ size_t partnum)
+{
+ struct fdisk_gpt_label *gpt;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ gpt = self_label(cxt);
+
+ if (partnum >= cxt->label->nparts_max
+ || partition_unused(&gpt->ents[partnum]))
+ return -EINVAL;
+
+ /* hasta la vista, baby! */
+ memset(&gpt->ents[partnum], 0, sizeof(struct gpt_entry));
+ if (!partition_unused(&gpt->ents[partnum]))
+ return -EINVAL;
+ else {
+ gpt_recompute_crc(gpt->pheader, gpt->ents);
+ gpt_recompute_crc(gpt->bheader, gpt->ents);
+ cxt->label->nparts_cur--;
+ fdisk_label_set_changed(cxt->label, 1);
+ }
+
+ return 0;
+}
+
+
+/* Performs logical checks to add a new partition entry */
+static int gpt_add_partition(
+ struct fdisk_context *cxt,
+ struct fdisk_partition *pa,
+ size_t *partno)
+{
+ uint64_t user_f, user_l; /* user input ranges for first and last sectors */
+ uint64_t disk_f, disk_l; /* first and last available sector ranges on device*/
+ uint64_t dflt_f, dflt_l; /* largest segment (default) */
+ struct gpt_guid typeid;
+ struct fdisk_gpt_label *gpt;
+ struct gpt_header *pheader;
+ struct gpt_entry *e, *ents;
+ struct fdisk_ask *ask = NULL;
+ size_t partnum;
+ int rc;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ gpt = self_label(cxt);
+ pheader = gpt->pheader;
+ ents = gpt->ents;
+
+ rc = fdisk_partition_next_partno(pa, cxt, &partnum);
+ if (rc) {
+ DBG(LABEL, ul_debug("GPT failed to get next partno"));
+ return rc;
+ }
+ if (!partition_unused(&ents[partnum])) {
+ fdisk_warnx(cxt, _("Partition %zu is already defined. "
+ "Delete it before re-adding it."), partnum +1);
+ return -ERANGE;
+ }
+ if (le32_to_cpu(pheader->npartition_entries) ==
+ partitions_in_use(pheader, ents)) {
+ fdisk_warnx(cxt, _("All partitions are already in use."));
+ return -ENOSPC;
+ }
+ if (!get_free_sectors(cxt, pheader, ents, NULL, NULL)) {
+ fdisk_warnx(cxt, _("No free sectors available."));
+ return -ENOSPC;
+ }
+
+ string_to_guid(pa && pa->type && pa->type->typestr ?
+ pa->type->typestr:
+ GPT_DEFAULT_ENTRY_TYPE, &typeid);
+
+ disk_f = find_first_available(pheader, ents, pheader->first_usable_lba);
+
+ /* if first sector no explicitly defined then ignore small gaps before
+ * the first partition */
+ if ((!pa || !fdisk_partition_has_start(pa))
+ && !partition_unused(&ents[0])
+ && disk_f < gpt_partition_start(&ents[0])) {
+
+ do {
+ uint64_t x;
+ DBG(LABEL, ul_debug("testing first sector %ju", disk_f));
+ disk_f = find_first_available(pheader, ents, disk_f);
+ if (!disk_f)
+ break;
+ x = find_last_free(pheader, ents, disk_f);
+ if (x - disk_f >= cxt->grain / cxt->sector_size)
+ break;
+ DBG(LABEL, ul_debug("first sector %ju addresses to small space, continue...", disk_f));
+ disk_f = x + 1;
+ } while(1);
+
+ if (disk_f == 0)
+ disk_f = find_first_available(pheader, ents, pheader->first_usable_lba);
+ }
+
+ disk_l = find_last_free_sector(pheader, ents);
+
+ /* the default is the largest free space */
+ dflt_f = find_first_in_largest(pheader, ents);
+ dflt_l = find_last_free(pheader, ents, dflt_f);
+
+ /* align the default in range <dflt_f,dflt_l>*/
+ dflt_f = fdisk_align_lba_in_range(cxt, dflt_f, dflt_f, dflt_l);
+
+ /* first sector */
+ if (pa && pa->start_follow_default) {
+ user_f = dflt_f;
+
+ } else if (pa && fdisk_partition_has_start(pa)) {
+ DBG(LABEL, ul_debug("first sector defined: %ju", pa->start));
+ if (pa->start != find_first_available(pheader, ents, pa->start)) {
+ fdisk_warnx(cxt, _("Sector %ju already used."), pa->start);
+ return -ERANGE;
+ }
+ user_f = pa->start;
+ } else {
+ /* ask by dialog */
+ for (;;) {
+ if (!ask)
+ ask = fdisk_new_ask();
+ else
+ fdisk_reset_ask(ask);
+
+ /* First sector */
+ fdisk_ask_set_query(ask, _("First sector"));
+ fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+ fdisk_ask_number_set_low(ask, disk_f); /* minimal */
+ fdisk_ask_number_set_default(ask, dflt_f); /* default */
+ fdisk_ask_number_set_high(ask, disk_l); /* maximal */
+
+ rc = fdisk_do_ask(cxt, ask);
+ if (rc)
+ goto done;
+
+ user_f = fdisk_ask_number_get_result(ask);
+ if (user_f != find_first_available(pheader, ents, user_f)) {
+ fdisk_warnx(cxt, _("Sector %ju already used."), user_f);
+ continue;
+ }
+ break;
+ }
+ }
+
+
+ /* Last sector */
+ dflt_l = find_last_free(pheader, ents, user_f);
+
+ if (pa && pa->end_follow_default) {
+ user_l = dflt_l;
+
+ } else if (pa && fdisk_partition_has_size(pa)) {
+ user_l = user_f + pa->size - 1;
+ DBG(LABEL, ul_debug("size defined: %ju, end: %ju (last possible: %ju)",
+ pa->size, user_l, dflt_l));
+ if (user_l != dflt_l && !pa->size_explicit)
+ user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l) - 1;
+
+ } else {
+ for (;;) {
+ if (!ask)
+ ask = fdisk_new_ask();
+ else
+ fdisk_reset_ask(ask);
+
+ fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
+ fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+ fdisk_ask_number_set_low(ask, user_f); /* minimal */
+ fdisk_ask_number_set_default(ask, dflt_l); /* default */
+ fdisk_ask_number_set_high(ask, dflt_l); /* maximal */
+ fdisk_ask_number_set_base(ask, user_f); /* base for relative input */
+ fdisk_ask_number_set_unit(ask, cxt->sector_size);
+
+ rc = fdisk_do_ask(cxt, ask);
+ if (rc)
+ goto done;
+
+ user_l = fdisk_ask_number_get_result(ask);
+ if (fdisk_ask_number_is_relative(ask)) {
+ user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l) - 1;
+
+ /* no space for anything useful, use all space
+ if (user_l + (cxt->grain / cxt->sector_size) > dflt_l)
+ user_l = dflt_l;
+ */
+ }
+
+ if (user_l > user_f && user_l <= disk_l)
+ break;
+ }
+ }
+
+
+ if (user_f > user_l || partnum >= cxt->label->nparts_max) {
+ fdisk_warnx(cxt, _("Could not create partition %zu"), partnum + 1);
+ rc = -EINVAL;
+ goto done;
+ }
+
+ assert(!FDISK_IS_UNDEF(user_l));
+ assert(!FDISK_IS_UNDEF(user_f));
+
+ e = &ents[partnum];
+ e->lba_end = cpu_to_le64(user_l);
+ e->lba_start = cpu_to_le64(user_f);
+
+ gpt_entry_set_type(e, &typeid);
+
+ if (pa && pa->uuid) {
+ /* Sometimes it's necessary to create a copy of the PT and
+ * reuse already defined UUID
+ */
+ rc = gpt_entry_set_uuid(e, pa->uuid);
+ if (rc)
+ goto done;
+ } else {
+ /* Any time a new partition entry is created a new GUID must be
+ * generated for that partition, and every partition is guaranteed
+ * to have a unique GUID.
+ */
+ uuid_generate_random((unsigned char *) &e->partition_guid);
+ swap_efi_guid(&e->partition_guid);
+ }
+
+ if (pa && pa->name && *pa->name)
+ gpt_entry_set_name(e, pa->name);
+ if (pa && pa->attrs)
+ gpt_entry_attrs_from_string(cxt, e, pa->attrs);
+
+ DBG(LABEL, ul_debug("GPT new partition: partno=%zu, start=%ju, end=%ju, size=%ju",
+ partnum,
+ gpt_partition_start(e),
+ gpt_partition_end(e),
+ gpt_partition_size(e)));
+
+ gpt_recompute_crc(gpt->pheader, ents);
+ gpt_recompute_crc(gpt->bheader, ents);
+
+ /* report result */
+ {
+ struct fdisk_parttype *t;
+
+ cxt->label->nparts_cur++;
+ fdisk_label_set_changed(cxt->label, 1);
+
+ t = gpt_partition_parttype(cxt, &ents[partnum]);
+ fdisk_info_new_partition(cxt, partnum + 1, user_f, user_l, t);
+ fdisk_unref_parttype(t);
+ }
+
+ rc = 0;
+ if (partno)
+ *partno = partnum;
+done:
+ fdisk_unref_ask(ask);
+ return rc;
+}
+
+/*
+ * Create a new GPT disklabel - destroys any previous data.
+ */
+static int gpt_create_disklabel(struct fdisk_context *cxt)
+{
+ int rc = 0;
+ ssize_t esz = 0;
+ char str[37];
+ struct fdisk_gpt_label *gpt;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ gpt = self_label(cxt);
+
+ /* label private stuff has to be empty, see gpt_deinit() */
+ assert(gpt->pheader == NULL);
+ assert(gpt->bheader == NULL);
+
+ /*
+ * When no header, entries or pmbr is set, we're probably
+ * dealing with a new, empty disk - so always allocate memory
+ * to deal with the data structures whatever the case is.
+ */
+ rc = gpt_mknew_pmbr(cxt);
+ if (rc < 0)
+ goto done;
+
+ /* primary */
+ gpt->pheader = calloc(1, sizeof(*gpt->pheader));
+ if (!gpt->pheader) {
+ rc = -ENOMEM;
+ goto done;
+ }
+ rc = gpt_mknew_header(cxt, gpt->pheader, GPT_PRIMARY_PARTITION_TABLE_LBA);
+ if (rc < 0)
+ goto done;
+
+ /* backup ("copy" primary) */
+ gpt->bheader = calloc(1, sizeof(*gpt->bheader));
+ if (!gpt->bheader) {
+ rc = -ENOMEM;
+ goto done;
+ }
+ rc = gpt_mknew_header_from_bkp(cxt, gpt->bheader,
+ last_lba(cxt), gpt->pheader);
+ if (rc < 0)
+ goto done;
+
+ esz = le32_to_cpu(gpt->pheader->npartition_entries) *
+ le32_to_cpu(gpt->pheader->sizeof_partition_entry);
+ gpt->ents = calloc(1, esz);
+ if (!gpt->ents) {
+ rc = -ENOMEM;
+ goto done;
+ }
+ gpt_recompute_crc(gpt->pheader, gpt->ents);
+ gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+ cxt->label->nparts_max = le32_to_cpu(gpt->pheader->npartition_entries);
+ cxt->label->nparts_cur = 0;
+
+ guid_to_string(&gpt->pheader->disk_guid, str);
+ fdisk_label_set_changed(cxt->label, 1);
+ fdisk_info(cxt, _("Created a new GPT disklabel (GUID: %s)."), str);
+done:
+ return rc;
+}
+
+static int gpt_get_disklabel_id(struct fdisk_context *cxt, char **id)
+{
+ struct fdisk_gpt_label *gpt;
+ char str[37];
+
+ assert(cxt);
+ assert(id);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ gpt = self_label(cxt);
+ guid_to_string(&gpt->pheader->disk_guid, str);
+
+ *id = strdup(str);
+ if (!*id)
+ return -ENOMEM;
+ return 0;
+}
+
+static int gpt_set_disklabel_id(struct fdisk_context *cxt)
+{
+ struct fdisk_gpt_label *gpt;
+ struct gpt_guid uuid;
+ char *str, *old, *new;
+ int rc;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ gpt = self_label(cxt);
+ if (fdisk_ask_string(cxt,
+ _("Enter new disk UUID (in 8-4-4-4-12 format)"), &str))
+ return -EINVAL;
+
+ rc = string_to_guid(str, &uuid);
+ free(str);
+
+ if (rc) {
+ fdisk_warnx(cxt, _("Failed to parse your UUID."));
+ return rc;
+ }
+
+ gpt_get_disklabel_id(cxt, &old);
+
+ gpt->pheader->disk_guid = uuid;
+ gpt->bheader->disk_guid = uuid;
+
+ gpt_recompute_crc(gpt->pheader, gpt->ents);
+ gpt_recompute_crc(gpt->bheader, gpt->ents);
+
+ gpt_get_disklabel_id(cxt, &new);
+
+ fdisk_info(cxt, _("Disk identifier changed from %s to %s."), old, new);
+
+ free(old);
+ free(new);
+ fdisk_label_set_changed(cxt->label, 1);
+ return 0;
+}
+
+static int gpt_part_is_used(struct fdisk_context *cxt, size_t i)
+{
+ struct fdisk_gpt_label *gpt;
+ struct gpt_entry *e;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ gpt = self_label(cxt);
+
+ if ((uint32_t) i >= le32_to_cpu(gpt->pheader->npartition_entries))
+ return 0;
+ e = &gpt->ents[i];
+
+ return !partition_unused(e) || gpt_partition_start(e);
+}
+
+/**
+ * fdisk_gpt_is_hybrid:
+ * @cxt: context
+ *
+ * The regular GPT contains PMBR (dummy protective MBR) where the protective
+ * MBR does not address any partitions.
+ *
+ * Hybrid GPT contains regular MBR where this partition table addresses the
+ * same partitions as GPT. It's recommended to not use hybrid GPT due to MBR
+ * limits.
+ *
+ * The libfdisk does not provide functionality to sync GPT and MBR, you have to
+ * directly access and modify (P)MBR (see fdisk_new_nested_context()).
+ *
+ * Returns: 1 if partition table detected as hybrid otherwise return 0
+ */
+int fdisk_gpt_is_hybrid(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return valid_pmbr(cxt) == GPT_MBR_HYBRID;
+}
+
+static int gpt_toggle_partition_flag(
+ struct fdisk_context *cxt,
+ size_t i,
+ unsigned long flag)
+{
+ struct fdisk_gpt_label *gpt;
+ uint64_t attrs, tmp;
+ char *bits;
+ const char *name = NULL;
+ int bit = -1, rc;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ DBG(LABEL, ul_debug("GPT entry attribute change requested partno=%zu", i));
+ gpt = self_label(cxt);
+
+ if ((uint32_t) i >= le32_to_cpu(gpt->pheader->npartition_entries))
+ return -EINVAL;
+
+ attrs = le64_to_cpu(gpt->ents[i].attrs);
+ bits = (char *) &attrs;
+
+ switch (flag) {
+ case GPT_FLAG_REQUIRED:
+ bit = GPT_ATTRBIT_REQ;
+ name = GPT_ATTRSTR_REQ;
+ break;
+ case GPT_FLAG_NOBLOCK:
+ bit = GPT_ATTRBIT_NOBLOCK;
+ name = GPT_ATTRSTR_NOBLOCK;
+ break;
+ case GPT_FLAG_LEGACYBOOT:
+ bit = GPT_ATTRBIT_LEGACY;
+ name = GPT_ATTRSTR_LEGACY;
+ break;
+ case GPT_FLAG_GUIDSPECIFIC:
+ rc = fdisk_ask_number(cxt, 48, 48, 63, _("Enter GUID specific bit"), &tmp);
+ if (rc)
+ return rc;
+ bit = tmp;
+ break;
+ default:
+ /* already specified PT_FLAG_GUIDSPECIFIC bit */
+ if (flag >= 48 && flag <= 63) {
+ bit = flag;
+ flag = GPT_FLAG_GUIDSPECIFIC;
+ }
+ break;
+ }
+
+ if (bit < 0) {
+ fdisk_warnx(cxt, _("failed to toggle unsupported bit %lu"), flag);
+ return -EINVAL;
+ }
+
+ if (!isset(bits, bit))
+ setbit(bits, bit);
+ else
+ clrbit(bits, bit);
+
+ gpt->ents[i].attrs = cpu_to_le64(attrs);
+
+ if (flag == GPT_FLAG_GUIDSPECIFIC)
+ fdisk_info(cxt, isset(bits, bit) ?
+ _("The GUID specific bit %d on partition %zu is enabled now.") :
+ _("The GUID specific bit %d on partition %zu is disabled now."),
+ bit, i + 1);
+ else
+ fdisk_info(cxt, isset(bits, bit) ?
+ _("The %s flag on partition %zu is enabled now.") :
+ _("The %s flag on partition %zu is disabled now."),
+ name, i + 1);
+
+ gpt_recompute_crc(gpt->pheader, gpt->ents);
+ gpt_recompute_crc(gpt->bheader, gpt->ents);
+ fdisk_label_set_changed(cxt->label, 1);
+ return 0;
+}
+
+static int gpt_entry_cmp_start(const void *a, const void *b)
+{
+ struct gpt_entry *ae = (struct gpt_entry *) a,
+ *be = (struct gpt_entry *) b;
+ int au = partition_unused(ae),
+ bu = partition_unused(be);
+
+ if (au && bu)
+ return 0;
+ if (au)
+ return 1;
+ if (bu)
+ return -1;
+
+ return cmp_numbers(gpt_partition_start(ae), gpt_partition_start(be));
+}
+
+/* sort partition by start sector */
+static int gpt_reorder(struct fdisk_context *cxt)
+{
+ struct fdisk_gpt_label *gpt;
+ size_t nparts;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ gpt = self_label(cxt);
+ nparts = le32_to_cpu(gpt->pheader->npartition_entries);
+
+ qsort(gpt->ents, nparts, sizeof(struct gpt_entry),
+ gpt_entry_cmp_start);
+
+ gpt_recompute_crc(gpt->pheader, gpt->ents);
+ gpt_recompute_crc(gpt->bheader, gpt->ents);
+ fdisk_label_set_changed(cxt->label, 1);
+
+ fdisk_info(cxt, _("Done."));
+ return 0;
+}
+
+static int gpt_reset_alignment(struct fdisk_context *cxt)
+{
+ struct fdisk_gpt_label *gpt;
+ struct gpt_header *h;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, GPT));
+
+ gpt = self_label(cxt);
+ h = gpt ? gpt->pheader : NULL;
+
+ if (h) {
+ /* always follow existing table */
+ cxt->first_lba = h->first_usable_lba;
+ cxt->last_lba = h->last_usable_lba;
+ } else {
+ /* estimate ranges for GPT */
+ uint64_t first, last;
+
+ count_first_last_lba(cxt, &first, &last);
+
+ if (cxt->first_lba < first)
+ cxt->first_lba = first;
+ if (cxt->last_lba > last)
+ cxt->last_lba = last;
+ }
+
+ return 0;
+}
+/*
+ * Deinitialize fdisk-specific variables
+ */
+static void gpt_deinit(struct fdisk_label *lb)
+{
+ struct fdisk_gpt_label *gpt = (struct fdisk_gpt_label *) lb;
+
+ if (!gpt)
+ return;
+
+ free(gpt->ents);
+ free(gpt->pheader);
+ free(gpt->bheader);
+
+ gpt->ents = NULL;
+ gpt->pheader = NULL;
+ gpt->bheader = NULL;
+}
+
+static const struct fdisk_label_operations gpt_operations =
+{
+ .probe = gpt_probe_label,
+ .write = gpt_write_disklabel,
+ .verify = gpt_verify_disklabel,
+ .create = gpt_create_disklabel,
+ .list = gpt_list_disklabel,
+ .locate = gpt_locate_disklabel,
+ .reorder = gpt_reorder,
+ .get_id = gpt_get_disklabel_id,
+ .set_id = gpt_set_disklabel_id,
+
+ .get_part = gpt_get_partition,
+ .set_part = gpt_set_partition,
+ .add_part = gpt_add_partition,
+ .del_part = gpt_delete_partition,
+
+ .part_is_used = gpt_part_is_used,
+ .part_toggle_flag = gpt_toggle_partition_flag,
+
+ .deinit = gpt_deinit,
+
+ .reset_alignment = gpt_reset_alignment
+};
+
+static const struct fdisk_field gpt_fields[] =
+{
+ /* basic */
+ { FDISK_FIELD_DEVICE, N_("Device"), 10, 0 },
+ { FDISK_FIELD_START, N_("Start"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_END, N_("End"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_SECTORS, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_SIZE, N_("Size"), 5, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
+ { FDISK_FIELD_TYPE, N_("Type"), 0.1, FDISK_FIELDFL_EYECANDY },
+ /* expert */
+ { FDISK_FIELD_TYPEID, N_("Type-UUID"), 36, FDISK_FIELDFL_DETAIL },
+ { FDISK_FIELD_UUID, N_("UUID"), 36, FDISK_FIELDFL_DETAIL },
+ { FDISK_FIELD_NAME, N_("Name"), 0.2, FDISK_FIELDFL_DETAIL },
+ { FDISK_FIELD_ATTR, N_("Attrs"), 0, FDISK_FIELDFL_DETAIL }
+};
+
+/*
+ * allocates GPT in-memory stuff
+ */
+struct fdisk_label *fdisk_new_gpt_label(struct fdisk_context *cxt)
+{
+ struct fdisk_label *lb;
+ struct fdisk_gpt_label *gpt;
+
+ assert(cxt);
+
+ gpt = calloc(1, sizeof(*gpt));
+ if (!gpt)
+ return NULL;
+
+ /* initialize generic part of the driver */
+ lb = (struct fdisk_label *) gpt;
+ lb->name = "gpt";
+ lb->id = FDISK_DISKLABEL_GPT;
+ lb->op = &gpt_operations;
+ lb->parttypes = gpt_parttypes;
+ lb->nparttypes = ARRAY_SIZE(gpt_parttypes);
+
+ lb->fields = gpt_fields;
+ lb->nfields = ARRAY_SIZE(gpt_fields);
+
+ return lb;
+}
diff --git a/libblkid/libfdisk/src/init.c b/libblkid/libfdisk/src/init.c
new file mode 100644
index 000000000..61acb0a4f
--- /dev/null
+++ b/libblkid/libfdisk/src/init.c
@@ -0,0 +1,55 @@
+
+#include "fdiskP.h"
+
+
+/**
+ * SECTION: init
+ * @title: Library initialization
+ * @short_description: initialize debug stuff
+ *
+ */
+
+UL_DEBUG_DEFINE_MASK(libfdisk);
+UL_DEBUG_DEFINE_MASKNAMES(libfdisk) =
+{
+ { "all", LIBFDISK_DEBUG_ALL, "info about all subsystems" },
+ { "ask", LIBFDISK_DEBUG_ASK, "fdisk dialogs" },
+ { "help", LIBFDISK_DEBUG_HELP, "this help" },
+ { "cxt", LIBFDISK_DEBUG_CXT, "library context (handler)" },
+ { "label", LIBFDISK_DEBUG_LABEL, "disk label utils" },
+ { "part", LIBFDISK_DEBUG_PART, "partition utils" },
+ { "parttype", LIBFDISK_DEBUG_PARTTYPE,"partition type utils" },
+ { "script", LIBFDISK_DEBUG_SCRIPT, "sfdisk-like scripts" },
+ { "tab", LIBFDISK_DEBUG_TAB, "table utils"},
+ { NULL, 0 }
+};
+
+/**
+ * fdisk_init_debug:
+ * @mask: debug mask (0xffff to enable full debuging)
+ *
+ * If the @mask is not specified then this function reads
+ * LIBFDISK_DEBUG environment variable to get the mask.
+ *
+ * Already initialized debugging stuff cannot be changed. It does not
+ * have effect to call this function twice.
+ *
+ * It's strongly recommended to use fdisk_init_debug(0) in your code.
+ */
+void fdisk_init_debug(int mask)
+{
+ if (libfdisk_debug_mask)
+ return;
+
+ __UL_INIT_DEBUG(libfdisk, LIBFDISK_DEBUG_, mask, LIBFDISK_DEBUG);
+
+
+ if (libfdisk_debug_mask != LIBFDISK_DEBUG_INIT
+ && libfdisk_debug_mask != (LIBFDISK_DEBUG_HELP|LIBFDISK_DEBUG_INIT)) {
+
+ DBG(INIT, ul_debug("library debug mask: 0x%04x", libfdisk_debug_mask));
+ }
+
+ ON_DBG(HELP, ul_debug_print_masks("LIBFDISK_DEBUG",
+ UL_DEBUG_MASKNAMES(libfdisk)));
+}
diff --git a/libblkid/libfdisk/src/iter.c b/libblkid/libfdisk/src/iter.c
new file mode 100644
index 000000000..9a0b0801c
--- /dev/null
+++ b/libblkid/libfdisk/src/iter.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: iter
+ * @title: Iterator
+ * @short_description: unified iterator
+ *
+ * The iterator keeps the direction and the last position for access to the
+ * internal library tables/lists.
+ *
+ * It's very unusual to use the same iterator on multiple places in your
+ * application or share the same iterator, for this purpose libfdisk does not
+ * provide reference counting for this object. It's recommended to initialize
+ * the iterator by fdisk_new_iter() at begin of your function and then
+ * fdisk_free_iter() before you return from the function.
+ *
+ * Don't forget to call fdisk_reset_iter() if you want to use the iterator more
+ * than once.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "fdiskP.h"
+
+/**
+ * fdisk_new_iter:
+ * @direction: FDISK_INTER_{FOR,BACK}WARD direction
+ *
+ * Returns: newly allocated generic libmount iterator.
+ */
+struct fdisk_iter *fdisk_new_iter(int direction)
+{
+ struct fdisk_iter *itr = calloc(1, sizeof(*itr));
+ if (!itr)
+ return NULL;
+ itr->direction = direction;
+ return itr;
+}
+
+/**
+ * fdisk_free_iter:
+ * @itr: iterator pointer
+ *
+ * Deallocates the iterator.
+ */
+void fdisk_free_iter(struct fdisk_iter *itr)
+{
+ free(itr);
+}
+
+/**
+ * fdisk_reset_iter:
+ * @itr: iterator pointer
+ * @direction: FDISK_INTER_{FOR,BACK}WARD or -1 to keep the direction unchanged
+ *
+ * Resets the iterator.
+ */
+void fdisk_reset_iter(struct fdisk_iter *itr, int direction)
+{
+ if (direction == -1)
+ direction = itr->direction;
+
+ memset(itr, 0, sizeof(*itr));
+ itr->direction = direction;
+}
+
+/**
+ * fdisk_iter_get_direction:
+ * @itr: iterator pointer
+ *
+ * Returns: FDISK_INTER_{FOR,BACK}WARD
+ */
+int fdisk_iter_get_direction(struct fdisk_iter *itr)
+{
+ return itr->direction;
+}
diff --git a/libblkid/libfdisk/src/label.c b/libblkid/libfdisk/src/label.c
new file mode 100644
index 000000000..750cfca55
--- /dev/null
+++ b/libblkid/libfdisk/src/label.c
@@ -0,0 +1,569 @@
+
+#include "fdiskP.h"
+
+
+/**
+ * SECTION: label
+ * @title: Label
+ * @short_description: disk label (PT) specific data and functions
+ *
+ * The fdisk_new_context() initializes all label drivers, and allocate
+ * per-label specific data struct. This concept allows to store label specific
+ * settings to the label driver independently on the currently active label
+ * driver. Note that label struct cannot be deallocated, so there is no
+ * reference counting for fdisk_label objects. All is destroyed by
+ * fdisk_unref_context() only.
+ *
+ * Anyway, all label drives share in-memory first sector. The function
+ * fdisk_create_disklabel() overwrites the sector. But it's possible that
+ * label driver also uses another buffers, for example GPT uses more than only
+ * the first sector.
+ *
+ * All label operations are in-memory only, except fdisk_write_disklabel().
+ *
+ * All functions that use "struct fdisk_context" rather than "struct
+ * fdisk_label" use the currently active label driver.
+ */
+
+
+int fdisk_probe_labels(struct fdisk_context *cxt)
+{
+ size_t i;
+
+ cxt->label = NULL;
+
+ for (i = 0; i < cxt->nlabels; i++) {
+ struct fdisk_label *lb = cxt->labels[i];
+ struct fdisk_label *org = fdisk_get_label(cxt, NULL);
+ int rc;
+
+ if (!lb->op->probe)
+ continue;
+ if (lb->disabled) {
+ DBG(CXT, ul_debugobj(cxt, "%s: disabled -- ignore", lb->name));
+ continue;
+ }
+ DBG(CXT, ul_debugobj(cxt, "probing for %s", lb->name));
+
+ cxt->label = lb;
+ rc = lb->op->probe(cxt);
+ cxt->label = org;
+
+ if (rc != 1) {
+ if (lb->op->deinit)
+ lb->op->deinit(lb); /* for sure */
+ continue;
+ }
+
+ __fdisk_switch_label(cxt, lb);
+ return 0;
+ }
+
+ DBG(CXT, ul_debugobj(cxt, "no label found"));
+ return 1; /* not found */
+}
+
+/**
+ * fdisk_label_get_name:
+ * @lb: label
+ *
+ * Returns: label name
+ */
+const char *fdisk_label_get_name(const struct fdisk_label *lb)
+{
+ return lb ? lb->name : NULL;
+}
+
+/**
+ * fdisk_label_is_labeltype:
+ * @lb: label
+ *
+ * Returns: FDISK_DISKLABEL_*.
+ */
+int fdisk_label_get_type(const struct fdisk_label *lb)
+{
+ return lb->id;
+}
+
+/**
+ * fdisk_label_require_geometry:
+ * @lb: label
+ *
+ * Returns: 1 if label requires CHS geometry
+ */
+int fdisk_label_require_geometry(const struct fdisk_label *lb)
+{
+ assert(lb);
+
+ return lb->flags & FDISK_LABEL_FL_REQUIRE_GEOMETRY ? 1 : 0;
+}
+
+/**
+ * fdisk_label_get_fields_ids
+ * @lb: label (or NULL for the current label)
+ * @cxt: context
+ * @ids: returns allocated array with FDISK_FIELD_* IDs
+ * @nids: returns number of items in fields
+ *
+ * This function returns the default fields for the label.
+ *
+ * Note that the set of the default fields depends on fdisk_enable_details()
+ * function. If the details are enabled then this function usually returns more
+ * fields.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_label_get_fields_ids(
+ const struct fdisk_label *lb,
+ struct fdisk_context *cxt,
+ int **ids, size_t *nids)
+{
+ size_t i, n;
+ int *c;
+
+ assert(cxt);
+
+ if (!lb)
+ lb = cxt->label;
+ if (!lb)
+ return -EINVAL;
+ if (!lb->fields || !lb->nfields)
+ return -ENOSYS;
+ c = calloc(lb->nfields, sizeof(int));
+ if (!c)
+ return -ENOMEM;
+ for (n = 0, i = 0; i < lb->nfields; i++) {
+ int id = lb->fields[i].id;
+
+ if ((fdisk_is_details(cxt) &&
+ (lb->fields[i].flags & FDISK_FIELDFL_EYECANDY))
+ || (!fdisk_is_details(cxt) &&
+ (lb->fields[i].flags & FDISK_FIELDFL_DETAIL))
+ || (id == FDISK_FIELD_SECTORS &&
+ fdisk_use_cylinders(cxt))
+ || (id == FDISK_FIELD_CYLINDERS &&
+ !fdisk_use_cylinders(cxt)))
+ continue;
+
+ c[n++] = id;
+ }
+ if (ids)
+ *ids = c;
+ else
+ free(c);
+ if (nids)
+ *nids = n;
+ return 0;
+}
+
+/**
+ * fdisk_label_get_field:
+ * @lb: label
+ * @id: FDISK_FIELD_*
+ *
+ * The field struct describes data stored in struct fdisk_partition. The info
+ * about data is usable for example to generate human readable output (e.g.
+ * fdisk 'p'rint command). See fdisk_partition_to_stirng() and fdisk code.
+ *
+ * Returns: pointer to static instance of the field.
+ */
+const struct fdisk_field *fdisk_label_get_field(const struct fdisk_label *lb, int id)
+{
+ size_t i;
+
+ assert(lb);
+ assert(id > 0);
+
+ for (i = 0; i < lb->nfields; i++) {
+ if (lb->fields[i].id == id)
+ return &lb->fields[i];
+ }
+
+ return NULL;
+}
+
+/**
+ * fdisk_label_get_field_by_name
+ * @lb: label
+ * @name: field name
+ *
+ * Returns: pointer to static instance of the field.
+ */
+const struct fdisk_field *fdisk_label_get_field_by_name(
+ const struct fdisk_label *lb,
+ const char *name)
+{
+ size_t i;
+
+ assert(lb);
+ assert(name);
+
+ for (i = 0; i < lb->nfields; i++) {
+ if (lb->fields[i].name && strcasecmp(lb->fields[i].name, name) == 0)
+ return &lb->fields[i];
+ }
+
+ return NULL;
+}
+
+
+/**
+ * fdisk_field_get_id:
+ * @field: field instance
+ *
+ * Returns: field Id (FDISK_FIELD_*)
+ */
+int fdisk_field_get_id(const struct fdisk_field *field)
+{
+ return field ? field->id : -EINVAL;
+}
+
+/**
+ * fdisk_field_get_name:
+ * @field: field instance
+ *
+ * Returns: field name
+ */
+const char *fdisk_field_get_name(const struct fdisk_field *field)
+{
+ return field ? field->name : NULL;
+}
+
+/**
+ * fdisk_field_get_width:
+ * @field: field instance
+ *
+ * Returns: libsmartcols compatible width.
+ */
+double fdisk_field_get_width(const struct fdisk_field *field)
+{
+ return field ? field->width : -EINVAL;
+}
+
+/**
+ * fdisk_field_is_number:
+ * @field: field instance
+ *
+ * Returns: 1 if field represent number
+ */
+int fdisk_field_is_number(const struct fdisk_field *field)
+{
+ return field->flags ? field->flags & FDISK_FIELDFL_NUMBER : 0;
+}
+
+
+/**
+ * fdisk_write_disklabel:
+ * @cxt: fdisk context
+ *
+ * Write in-memory changes to disk. Be careful!
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_write_disklabel(struct fdisk_context *cxt)
+{
+ if (!cxt || !cxt->label || cxt->readonly)
+ return -EINVAL;
+ if (!cxt->label->op->write)
+ return -ENOSYS;
+ return cxt->label->op->write(cxt);
+}
+
+/**
+ * fdisk_verify_disklabel:
+ * @cxt: fdisk context
+ *
+ * Verifies the partition table.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_verify_disklabel(struct fdisk_context *cxt)
+{
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+ if (!cxt->label->op->verify)
+ return -ENOSYS;
+ if (fdisk_missing_geometry(cxt))
+ return -EINVAL;
+
+ return cxt->label->op->verify(cxt);
+}
+
+/**
+ * fdisk_list_disklabel:
+ * @cxt: fdisk context
+ *
+ * Lists details about disklabel, but no partitions.
+ *
+ * This function uses libfdisk ASK interface to print data. The details about
+ * partitions table are printed by FDISK_ASKTYPE_INFO.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_list_disklabel(struct fdisk_context *cxt)
+{
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+ if (!cxt->label->op->list)
+ return -ENOSYS;
+
+ return cxt->label->op->list(cxt);
+}
+
+/**
+ * fdisk_create_disklabel:
+ * @cxt: fdisk context
+ * @name: label name
+ *
+ * Creates a new disk label of type @name. If @name is NULL, then it will
+ * create a default system label type, either SUN or DOS. The function
+ * automaticaly switches the current label driver to @name. The function
+ * fdisk_get_label() returns the current label driver.
+ *
+ * The function modifies in-memory data only.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name)
+{
+ int haslabel = 0;
+ struct fdisk_label *lb;
+
+ if (!cxt)
+ return -EINVAL;
+
+ if (!name) { /* use default label creation */
+#ifdef __sparc__
+ name = "sun";
+#else
+ name = "dos";
+#endif
+ }
+
+ if (cxt->label) {
+ fdisk_deinit_label(cxt->label);
+ haslabel = 1;
+ }
+
+ lb = fdisk_get_label(cxt, name);
+ if (!lb || lb->disabled)
+ return -EINVAL;
+ if (!lb->op->create)
+ return -ENOSYS;
+
+ __fdisk_switch_label(cxt, lb);
+
+ if (haslabel && !cxt->parent)
+ fdisk_reset_device_properties(cxt);
+
+ DBG(CXT, ul_debugobj(cxt, "create a new %s label", lb->name));
+ return cxt->label->op->create(cxt);
+}
+
+/**
+ * fdisk_locate_disklabel:
+ * @cxt: context
+ * @n: N item
+ * @name: return item name
+ * @offset: return offset where is item
+ * @size: of the item
+ *
+ * Locate disklabel and returns info about @n item of the label. For example
+ * GPT is composed from two items, PMBR and GPT, n=0 return offset to PMBR and n=1
+ * return offset to GPT. For more details see 'D' expect fdisk command.
+ *
+ * Returns: 0 on succes, <0 on error, 1 no more items.
+ */
+int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name,
+ off_t *offset, size_t *size)
+{
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+ if (!cxt->label->op->locate)
+ return -ENOSYS;
+
+ DBG(CXT, ul_debugobj(cxt, "locating %d chunk of %s.", n, cxt->label->name));
+ return cxt->label->op->locate(cxt, n, name, offset, size);
+}
+
+
+/**
+ * fdisk_get_disklabel_id:
+ * @cxt: fdisk context
+ * @id: returns pointer to allocated string (MBR Id or GPT dirk UUID)
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id)
+{
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+ if (!cxt->label->op->get_id)
+ return -ENOSYS;
+
+ DBG(CXT, ul_debugobj(cxt, "asking for disk %s ID", cxt->label->name));
+ return cxt->label->op->get_id(cxt, id);
+}
+
+/**
+ * fdisk_set_disklabel_id:
+ * @cxt: fdisk context
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_set_disklabel_id(struct fdisk_context *cxt)
+{
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+ if (!cxt->label->op->set_id)
+ return -ENOSYS;
+
+ DBG(CXT, ul_debugobj(cxt, "setting %s disk ID", cxt->label->name));
+ return cxt->label->op->set_id(cxt);
+}
+
+/**
+ * fdisk_set_partition_type:
+ * @cxt: fdisk context
+ * @partnum: partition number
+ * @t: new type
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int fdisk_set_partition_type(struct fdisk_context *cxt,
+ size_t partnum,
+ struct fdisk_parttype *t)
+{
+ if (!cxt || !cxt->label || !t)
+ return -EINVAL;
+
+
+ if (cxt->label->op->set_part) {
+ struct fdisk_partition *pa = fdisk_new_partition();
+ int rc;
+
+ if (!pa)
+ return -ENOMEM;
+ fdisk_partition_set_type(pa, t);
+
+ DBG(CXT, ul_debugobj(cxt, "partition: %zd: set type", partnum));
+ rc = cxt->label->op->set_part(cxt, partnum, pa);
+ fdisk_unref_partition(pa);
+ return rc;
+ }
+
+ return -ENOSYS;
+}
+
+
+/**
+ * fdisk_toggle_partition_flag:
+ * @cxt: fdisk context
+ * @partnum: partition number
+ * @flag: flag ID
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_toggle_partition_flag(struct fdisk_context *cxt,
+ size_t partnum,
+ unsigned long flag)
+{
+ int rc;
+
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+ if (!cxt->label->op->part_toggle_flag)
+ return -ENOSYS;
+
+ rc = cxt->label->op->part_toggle_flag(cxt, partnum, flag);
+
+ DBG(CXT, ul_debugobj(cxt, "partition: %zd: toggle: 0x%04lx [rc=%d]", partnum, flag, rc));
+ return rc;
+}
+
+/**
+ * fdisk_reorder_partitions
+ * @cxt: fdisk context
+ *
+ * Sort partitions according to the partition start sector.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_reorder_partitions(struct fdisk_context *cxt)
+{
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+ if (!cxt->label->op->reorder)
+ return -ENOSYS;
+
+ return cxt->label->op->reorder(cxt);
+}
+
+/*
+ * Resets the current used label driver to initial state
+ */
+void fdisk_deinit_label(struct fdisk_label *lb)
+{
+ assert(lb);
+
+ /* private label information */
+ if (lb->op->deinit)
+ lb->op->deinit(lb);
+}
+
+/**
+ * fdisk_label_set_changed:
+ * @lb: label
+ * @changed: 0/1
+ *
+ * Marks in-memory data as changed, to force fdisk_write_disklabel() to write
+ * to device. This should be unnecessar by default, the library keeps track
+ * about changes.
+ */
+void fdisk_label_set_changed(struct fdisk_label *lb, int changed)
+{
+ assert(lb);
+ lb->changed = changed ? 1 : 0;
+}
+
+/**
+ * fdisk_label_is_changed:
+ * @lb: label
+ *
+ * Returns: 1 if in-memory data has been changed.
+ */
+int fdisk_label_is_changed(const struct fdisk_label *lb)
+{
+ assert(lb);
+ return lb ? lb->changed : 0;
+}
+
+/**
+ * fdisk_label_set_disabled:
+ * @lb: label
+ * @disabled: 0 or 1
+ *
+ * Mark label as disabled, then libfdisk is going to ignore the label when
+ * probe device for labels.
+ */
+void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled)
+{
+ assert(lb);
+
+ DBG(LABEL, ul_debug("%s label %s",
+ lb->name,
+ disabled ? "DISABLED" : "ENABLED"));
+ lb->disabled = disabled ? 1 : 0;
+}
+
+/**
+ * fdisk_label_is_disabled:
+ * @lb: label
+ *
+ * Returns: 1 if label driver disabled.
+ */
+int fdisk_label_is_disabled(const struct fdisk_label *lb)
+{
+ assert(lb);
+ return lb ? lb->disabled : 0;
+}
diff --git a/libblkid/libfdisk/src/libfdisk.h b/libblkid/libfdisk/src/libfdisk.h
new file mode 100644
index 000000000..844e17e19
--- /dev/null
+++ b/libblkid/libfdisk/src/libfdisk.h
@@ -0,0 +1,579 @@
+/*
+ * libfdisk.h - libfdisk API
+ *
+ * Copyright (C) 2012-2014 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 General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _LIBFDISK_H
+#define _LIBFDISK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+/**
+ * LIBFDISK_VERSION:
+ *
+ * Library version string
+ */
+#define LIBFDISK_VERSION "2.25.0"
+
+/**
+ * fdisk_context:
+ *
+ * Basic library handler.
+ */
+struct fdisk_context;
+
+/**
+ * fdisk_label:
+ *
+ * Disk label specific driver and setting.
+ */
+struct fdisk_label;
+
+/**
+ * fdisk_parttype:
+ *
+ * Partition type.
+ */
+struct fdisk_parttype;
+
+/**
+ * fdisk_partition:
+ *
+ * Partition abstraction (and template).
+ */
+struct fdisk_partition;
+
+/**
+ * fdisk_ask:
+ *
+ * Ask API handler for dialogs with users.
+ */
+struct fdisk_ask;
+
+/**
+ * fdisk_iter:
+ *
+ * Unified iterator.
+ */
+struct fdisk_iter;
+
+/**
+ * fdisk_table:
+ *
+ * Container for fdisk_partition objects
+ */
+struct fdisk_table;
+
+/**
+ * fdisk_field
+ *
+ * Output field description.
+ */
+struct fdisk_field;
+
+/**
+ * fdisk_script
+ *
+ * library handler for sfdisk compatible scripts
+ */
+struct fdisk_script;
+
+/**
+ * fdisk_sector_t
+ *
+ * LBA adresses type
+ */
+typedef uint64_t fdisk_sector_t;
+
+/**
+ * fdisk_labeltype:
+ *
+ * Supported partition table types (labels)
+ */
+enum fdisk_labeltype {
+ FDISK_DISKLABEL_DOS = (1 << 1),
+ FDISK_DISKLABEL_SUN = (1 << 2),
+ FDISK_DISKLABEL_SGI = (1 << 3),
+ FDISK_DISKLABEL_BSD = (1 << 4),
+ FDISK_DISKLABEL_GPT = (1 << 5)
+};
+
+/**
+ * fdisk_asktype:
+ *
+ * Ask API dialog types
+ */
+enum fdisk_asktype {
+ FDISK_ASKTYPE_NONE = 0,
+ FDISK_ASKTYPE_NUMBER,
+ FDISK_ASKTYPE_OFFSET,
+ FDISK_ASKTYPE_WARN,
+ FDISK_ASKTYPE_WARNX,
+ FDISK_ASKTYPE_INFO,
+ FDISK_ASKTYPE_YESNO,
+ FDISK_ASKTYPE_STRING,
+ FDISK_ASKTYPE_MENU
+};
+
+/* init.c */
+extern void fdisk_init_debug(int mask);
+
+/* context.h */
+
+#define FDISK_PLURAL 0
+#define FDISK_SINGULAR 1
+
+struct fdisk_context *fdisk_new_context(void);
+struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent, const char *name);
+void fdisk_unref_context(struct fdisk_context *cxt);
+void fdisk_ref_context(struct fdisk_context *cxt);
+
+struct fdisk_context *fdisk_get_parent(struct fdisk_context *cxt);
+size_t fdisk_get_npartitions(struct fdisk_context *cxt);
+
+struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name);
+int fdisk_next_label(struct fdisk_context *cxt, struct fdisk_label **lb);
+size_t fdisk_get_nlabels(struct fdisk_context *cxt);
+
+int fdisk_has_label(struct fdisk_context *cxt);
+int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id);
+#define fdisk_is_label(c, x) fdisk_is_labeltype(c, FDISK_DISKLABEL_ ## x)
+
+
+int fdisk_assign_device(struct fdisk_context *cxt,
+ const char *fname, int readonly);
+int fdisk_deassign_device(struct fdisk_context *cxt, int nosync);
+int fdisk_is_readonly(struct fdisk_context *cxt);
+
+int fdisk_enable_details(struct fdisk_context *cxt, int enable);
+int fdisk_is_details(struct fdisk_context *cxt);
+
+int fdisk_enable_listonly(struct fdisk_context *cxt, int enable);
+int fdisk_is_listonly(struct fdisk_context *cxt);
+
+int fdisk_set_unit(struct fdisk_context *cxt, const char *str);
+const char *fdisk_get_unit(struct fdisk_context *cxt, int n);
+int fdisk_use_cylinders(struct fdisk_context *cxt);
+unsigned int fdisk_get_units_per_sector(struct fdisk_context *cxt);
+
+unsigned long fdisk_get_optimal_iosize(struct fdisk_context *cxt);
+unsigned long fdisk_get_minimal_iosize(struct fdisk_context *cxt);
+unsigned long fdisk_get_physector_size(struct fdisk_context *cxt);
+unsigned long fdisk_get_sector_size(struct fdisk_context *cxt);
+unsigned long fdisk_get_alignment_offset(struct fdisk_context *cxt);
+unsigned long fdisk_get_grain_size(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba);
+fdisk_sector_t fdisk_get_last_lba(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_set_last_lba(struct fdisk_context *cxt, fdisk_sector_t lba);
+fdisk_sector_t fdisk_get_nsectors(struct fdisk_context *cxt);
+const char *fdisk_get_devname(struct fdisk_context *cxt);
+int fdisk_get_devfd(struct fdisk_context *cxt);
+
+unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt);
+
+
+
+/* parttype.c */
+struct fdisk_parttype *fdisk_new_parttype(void);
+void fdisk_ref_parttype(struct fdisk_parttype *t);
+void fdisk_unref_parttype(struct fdisk_parttype *t);
+int fdisk_parttype_set_name(struct fdisk_parttype *t, const char *str);
+int fdisk_parttype_set_typestr(struct fdisk_parttype *t, const char *str);
+int fdisk_parttype_set_code(struct fdisk_parttype *t, int code);
+size_t fdisk_label_get_nparttypes(const struct fdisk_label *lb);
+struct fdisk_parttype *fdisk_label_get_parttype(const struct fdisk_label *lb, size_t n);
+int fdisk_label_has_code_parttypes(const struct fdisk_label *lb);
+struct fdisk_parttype *fdisk_label_get_parttype_from_code(
+ const struct fdisk_label *lb,
+ unsigned int code);
+struct fdisk_parttype *fdisk_label_get_parttype_from_string(
+ const struct fdisk_label *lb,
+ const char *str);
+struct fdisk_parttype *fdisk_new_unknown_parttype(unsigned int code,
+ const char *typestr);
+struct fdisk_parttype *fdisk_copy_parttype(const struct fdisk_parttype *type);
+struct fdisk_parttype *fdisk_label_parse_parttype(
+ const struct fdisk_label *lb,
+ const char *str);
+const char *fdisk_parttype_get_string(const struct fdisk_parttype *t);
+unsigned int fdisk_parttype_get_code(const struct fdisk_parttype *t);
+const char *fdisk_parttype_get_name(const struct fdisk_parttype *t);
+int fdisk_parttype_is_unknown(const struct fdisk_parttype *t);
+
+/* label.c */
+
+/**
+ * fdisk_fieldtype
+ *
+ * Types of fdisk_field
+ */
+enum fdisk_fieldtype {
+ FDISK_FIELD_NONE = 0,
+
+ /* generic */
+ FDISK_FIELD_DEVICE,
+ FDISK_FIELD_START,
+ FDISK_FIELD_END,
+ FDISK_FIELD_SECTORS,
+ FDISK_FIELD_CYLINDERS,
+ FDISK_FIELD_SIZE,
+ FDISK_FIELD_TYPE,
+ FDISK_FIELD_TYPEID,
+
+ /* label specific */
+ FDISK_FIELD_ATTR,
+ FDISK_FIELD_BOOT,
+ FDISK_FIELD_BSIZE,
+ FDISK_FIELD_CPG,
+ FDISK_FIELD_EADDR,
+ FDISK_FIELD_FSIZE,
+ FDISK_FIELD_NAME,
+ FDISK_FIELD_SADDR,
+ FDISK_FIELD_UUID,
+
+ FDISK_NFIELDS /* must be last */
+};
+
+int fdisk_label_get_type(const struct fdisk_label *lb);
+const char *fdisk_label_get_name(const struct fdisk_label *lb);
+int fdisk_label_require_geometry(const struct fdisk_label *lb);
+
+
+extern int fdisk_write_disklabel(struct fdisk_context *cxt);
+extern int fdisk_verify_disklabel(struct fdisk_context *cxt);
+extern int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name);
+extern int fdisk_list_disklabel(struct fdisk_context *cxt);
+extern int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name, off_t *offset, size_t *size);
+
+extern int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id);
+extern int fdisk_set_disklabel_id(struct fdisk_context *cxt);
+
+extern int fdisk_get_partition(struct fdisk_context *cxt, size_t partno, struct fdisk_partition **pa);
+extern int fdisk_set_partition(struct fdisk_context *cxt, size_t partno, struct fdisk_partition *pa);
+extern int fdisk_add_partition(struct fdisk_context *cxt, struct fdisk_partition *pa, size_t *partno);
+extern int fdisk_delete_partition(struct fdisk_context *cxt, size_t partno);
+
+extern int fdisk_delete_all_partitions(struct fdisk_context *cxt);
+
+extern int fdisk_set_partition_type(struct fdisk_context *cxt, size_t partnum,
+ struct fdisk_parttype *t);
+
+
+extern int fdisk_label_get_fields_ids(
+ const struct fdisk_label *lb,
+ struct fdisk_context *cxt,
+ int **ids, size_t *nids);
+
+extern const struct fdisk_field *fdisk_label_get_field(const struct fdisk_label *lb, int id);
+extern const struct fdisk_field *fdisk_label_get_field_by_name(
+ const struct fdisk_label *lb,
+ const char *name);
+
+extern int fdisk_field_get_id(const struct fdisk_field *field);
+extern const char *fdisk_field_get_name(const struct fdisk_field *field);
+extern double fdisk_field_get_width(const struct fdisk_field *field);
+extern int fdisk_field_is_number(const struct fdisk_field *field);
+
+
+extern void fdisk_label_set_changed(struct fdisk_label *lb, int changed);
+extern int fdisk_label_is_changed(const struct fdisk_label *lb);
+
+extern void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled);
+extern int fdisk_label_is_disabled(const struct fdisk_label *lb);
+
+extern int fdisk_is_partition_used(struct fdisk_context *cxt, size_t n);
+
+extern int fdisk_toggle_partition_flag(struct fdisk_context *cxt, size_t partnum, unsigned long flag);
+
+extern struct fdisk_partition *fdisk_new_partition(void);
+extern void fdisk_reset_partition(struct fdisk_partition *pa);
+extern void fdisk_ref_partition(struct fdisk_partition *pa);
+extern void fdisk_unref_partition(struct fdisk_partition *pa);
+extern int fdisk_partition_is_freespace(struct fdisk_partition *pa);
+
+int fdisk_partition_set_start(struct fdisk_partition *pa, uint64_t off);
+int fdisk_partition_unset_start(struct fdisk_partition *pa);
+uint64_t fdisk_partition_get_start(struct fdisk_partition *pa);
+int fdisk_partition_has_start(struct fdisk_partition *pa);
+int fdisk_partition_cmp_start(struct fdisk_partition *a,
+ struct fdisk_partition *b);
+int fdisk_partition_start_follow_default(struct fdisk_partition *pa, int enable);
+int fdisk_partition_start_is_default(struct fdisk_partition *pa);
+
+int fdisk_partition_set_size(struct fdisk_partition *pa, uint64_t sz);
+int fdisk_partition_unset_size(struct fdisk_partition *pa);
+uint64_t fdisk_partition_get_size(struct fdisk_partition *pa);
+int fdisk_partition_has_size(struct fdisk_partition *pa);
+int fdisk_partition_size_explicit(struct fdisk_partition *pa, int enable);
+
+int fdisk_partition_has_end(struct fdisk_partition *pa);
+fdisk_sector_t fdisk_partition_get_end(struct fdisk_partition *pa);
+
+int fdisk_partition_set_partno(struct fdisk_partition *pa, size_t num);
+int fdisk_partition_unset_partno(struct fdisk_partition *pa);
+size_t fdisk_partition_get_partno(struct fdisk_partition *pa);
+int fdisk_partition_has_partno(struct fdisk_partition *pa);
+int fdisk_partition_cmp_partno(struct fdisk_partition *a,
+ struct fdisk_partition *b);
+int fdisk_partition_partno_follow_default(struct fdisk_partition *pa, int enable);
+
+
+extern int fdisk_partition_set_type(struct fdisk_partition *pa, struct fdisk_parttype *type);
+extern struct fdisk_parttype *fdisk_partition_get_type(struct fdisk_partition *pa);
+extern int fdisk_partition_set_name(struct fdisk_partition *pa, const char *name);
+extern const char *fdisk_partition_get_name(struct fdisk_partition *pa);
+extern int fdisk_partition_set_uuid(struct fdisk_partition *pa, const char *uuid);
+extern int fdisk_partition_set_attrs(struct fdisk_partition *pa, const char *attrs);
+extern const char *fdisk_partition_get_uuid(struct fdisk_partition *pa);
+extern const char *fdisk_partition_get_attrs(struct fdisk_partition *pa);
+extern int fdisk_partition_is_nested(struct fdisk_partition *pa);
+extern int fdisk_partition_is_container(struct fdisk_partition *pa);
+extern int fdisk_partition_get_parent(struct fdisk_partition *pa, size_t *parent);
+extern int fdisk_partition_is_used(struct fdisk_partition *pa);
+extern int fdisk_partition_is_bootable(struct fdisk_partition *pa);
+extern int fdisk_partition_to_string(struct fdisk_partition *pa,
+ struct fdisk_context *cxt,
+ int id, char **data);
+
+int fdisk_partition_next_partno(struct fdisk_partition *pa,
+ struct fdisk_context *cxt,
+ size_t *n);
+
+extern int fdisk_partition_end_follow_default(struct fdisk_partition *pa, int enable);
+extern int fdisk_partition_end_is_default(struct fdisk_partition *pa);
+
+extern int fdisk_reorder_partitions(struct fdisk_context *cxt);
+
+/* table.c */
+extern struct fdisk_table *fdisk_new_table(void);
+extern int fdisk_reset_table(struct fdisk_table *tb);
+extern void fdisk_ref_table(struct fdisk_table *tb);
+extern void fdisk_unref_table(struct fdisk_table *tb);
+extern size_t fdisk_table_get_nents(struct fdisk_table *tb);
+extern int fdisk_table_is_empty(struct fdisk_table *tb);
+extern int fdisk_table_add_partition(struct fdisk_table *tb, struct fdisk_partition *pa);
+extern int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_partition *pa);
+
+extern int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb);
+extern int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb);
+
+extern int fdisk_table_wrong_order(struct fdisk_table *tb);
+extern int fdisk_table_sort_partitions(struct fdisk_table *tb,
+ int (*cmp)(struct fdisk_partition *,
+ struct fdisk_partition *));
+
+extern int fdisk_table_next_partition(
+ struct fdisk_table *tb,
+ struct fdisk_iter *itr,
+ struct fdisk_partition **pa);
+
+extern struct fdisk_partition *fdisk_table_get_partition(
+ struct fdisk_table *tb,
+ size_t n);
+extern int fdisk_apply_table(struct fdisk_context *cxt, struct fdisk_table *tb);
+
+/* alignment.c */
+#define FDISK_ALIGN_UP 1
+#define FDISK_ALIGN_DOWN 2
+#define FDISK_ALIGN_NEAREST 3
+
+fdisk_sector_t fdisk_align_lba(struct fdisk_context *cxt, fdisk_sector_t lba, int direction);
+fdisk_sector_t fdisk_align_lba_in_range(struct fdisk_context *cxt,
+ fdisk_sector_t lba, fdisk_sector_t start, fdisk_sector_t stop);
+int fdisk_lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba);
+
+int fdisk_override_geometry(struct fdisk_context *cxt,
+ unsigned int cylinders,
+ unsigned int heads,
+ unsigned int sectors);
+int fdisk_save_user_geometry(struct fdisk_context *cxt,
+ unsigned int cylinders,
+ unsigned int heads,
+ unsigned int sectors);
+int fdisk_save_user_sector_size(struct fdisk_context *cxt,
+ unsigned int phy,
+ unsigned int log);
+int fdisk_has_user_device_properties(struct fdisk_context *cxt);
+int fdisk_reset_alignment(struct fdisk_context *cxt);
+int fdisk_reset_device_properties(struct fdisk_context *cxt);
+int fdisk_reread_partition_table(struct fdisk_context *cxt);
+
+/* iter.c */
+enum {
+
+ FDISK_ITER_FORWARD = 0,
+ FDISK_ITER_BACKWARD
+};
+extern struct fdisk_iter *fdisk_new_iter(int direction);
+extern void fdisk_free_iter(struct fdisk_iter *itr);
+extern void fdisk_reset_iter(struct fdisk_iter *itr, int direction);
+extern int fdisk_iter_get_direction(struct fdisk_iter *itr);
+
+
+/* dos.c */
+#define DOS_FLAG_ACTIVE 1
+
+extern int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i);
+extern int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable);
+extern int fdisk_dos_is_compatible(struct fdisk_label *lb);
+
+/* sun.h */
+extern int fdisk_sun_set_alt_cyl(struct fdisk_context *cxt);
+extern int fdisk_sun_set_xcyl(struct fdisk_context *cxt);
+extern int fdisk_sun_set_ilfact(struct fdisk_context *cxt);
+extern int fdisk_sun_set_rspeed(struct fdisk_context *cxt);
+extern int fdisk_sun_set_pcylcount(struct fdisk_context *cxt);
+
+/* bsd.c */
+extern int fdisk_bsd_edit_disklabel(struct fdisk_context *cxt);
+extern int fdisk_bsd_write_bootstrap(struct fdisk_context *cxt);
+extern int fdisk_bsd_link_partition(struct fdisk_context *cxt);
+
+/* sgi.h */
+#define SGI_FLAG_BOOT 1
+#define SGI_FLAG_SWAP 2
+extern int fdisk_sgi_set_bootfile(struct fdisk_context *cxt);
+extern int fdisk_sgi_create_info(struct fdisk_context *cxt);
+
+/* gpt */
+
+/* GPT partition attributes */
+enum {
+ /* System partition (disk partitioning utilities must preserve the
+ * partition as is) */
+ GPT_FLAG_REQUIRED = 1,
+
+ /* EFI firmware should ignore the content of the partition and not try
+ * to read from it */
+ GPT_FLAG_NOBLOCK,
+
+ /* Legacy BIOS bootable */
+ GPT_FLAG_LEGACYBOOT,
+
+ /* bites 48-63, Defined and used by the individual partition type.
+ *
+ * The flag GPT_FLAG_GUIDSPECIFIC forces libfdisk to ask (by ask API)
+ * for a bit number. If you want to toggle specific bit and avoid any
+ * dialog, then use the bit number (in range 48..63). For example:
+ *
+ * // start dialog to ask for bit number
+ * fdisk_toggle_partition_flag(cxt, n, GPT_FLAG_GUIDSPECIFIC);
+ *
+ * // toggle bit 60
+ * fdisk_toggle_partition_flag(cxt, n, 60);
+ */
+ GPT_FLAG_GUIDSPECIFIC
+};
+
+extern int fdisk_gpt_is_hybrid(struct fdisk_context *cxt);
+
+
+/* script.c */
+struct fdisk_script *fdisk_new_script(struct fdisk_context *cxt);
+struct fdisk_script *fdisk_new_script_from_file(struct fdisk_context *cxt,
+ const char *filename);
+void fdisk_ref_script(struct fdisk_script *dp);
+void fdisk_unref_script(struct fdisk_script *dp);
+
+const char *fdisk_script_get_header(struct fdisk_script *dp, const char *name);
+int fdisk_script_set_header(struct fdisk_script *dp, const char *name, const char *data);
+struct fdisk_table *fdisk_script_get_table(struct fdisk_script *dp);
+int fdisk_script_get_nlines(struct fdisk_script *dp);
+
+int fdisk_script_read_context(struct fdisk_script *dp, struct fdisk_context *cxt);
+int fdisk_script_write_file(struct fdisk_script *dp, FILE *f);
+int fdisk_script_read_file(struct fdisk_script *dp, FILE *f);
+int fdisk_script_read_line(struct fdisk_script *dp, FILE *f, char *buf, size_t bufsz);
+
+int fdisk_set_script(struct fdisk_context *cxt, struct fdisk_script *dp);
+struct fdisk_script *fdisk_get_script(struct fdisk_context *cxt);
+
+int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *dp);
+int fdisk_apply_script(struct fdisk_context *cxt, struct fdisk_script *dp);
+
+
+/* ask.c */
+#define fdisk_is_ask(a, x) (fdisk_ask_get_type(a) == FDISK_ASKTYPE_ ## x)
+
+int fdisk_set_ask(struct fdisk_context *cxt,
+ int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *),
+ void *data);
+
+
+void fdisk_ref_ask(struct fdisk_ask *ask);
+void fdisk_unref_ask(struct fdisk_ask *ask);
+const char *fdisk_ask_get_query(struct fdisk_ask *ask);
+int fdisk_ask_get_type(struct fdisk_ask *ask);
+const char *fdisk_ask_number_get_range(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask);
+int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result);
+uint64_t fdisk_ask_number_get_base(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_unit(struct fdisk_ask *ask);
+int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative);
+int fdisk_ask_number_inchars(struct fdisk_ask *ask);
+int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew);
+
+int fdisk_ask_number(struct fdisk_context *cxt,
+ uintmax_t low,
+ uintmax_t dflt,
+ uintmax_t high,
+ const char *query,
+ uintmax_t *result);
+char *fdisk_ask_string_get_result(struct fdisk_ask *ask);
+int fdisk_ask_string_set_result(struct fdisk_ask *ask, char *result);
+int fdisk_ask_string(struct fdisk_context *cxt,
+ const char *query,
+ char **result);
+int fdisk_ask_yesno(struct fdisk_context *cxt,
+ const char *query,
+ int *result);
+int fdisk_ask_yesno_get_result(struct fdisk_ask *ask);
+int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, int result);
+int fdisk_ask_menu_get_default(struct fdisk_ask *ask);
+int fdisk_ask_menu_set_result(struct fdisk_ask *ask, int key);
+int fdisk_ask_menu_get_result(struct fdisk_ask *ask, int *key);
+int fdisk_ask_menu_get_item(struct fdisk_ask *ask, size_t idx, int *key,
+ const char **name, const char **desc);
+size_t fdisk_ask_menu_get_nitems(struct fdisk_ask *ask);
+int fdisk_ask_print_get_errno(struct fdisk_ask *ask);
+const char *fdisk_ask_print_get_mesg(struct fdisk_ask *ask);
+
+int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...);
+int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...);
+int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...);
+
+/* utils.h */
+extern char *fdisk_partname(const char *dev, size_t partno);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBFDISK_H */
diff --git a/libblkid/libfdisk/src/libfdisk.h.in b/libblkid/libfdisk/src/libfdisk.h.in
new file mode 100644
index 000000000..f82d5bd97
--- /dev/null
+++ b/libblkid/libfdisk/src/libfdisk.h.in
@@ -0,0 +1,579 @@
+/*
+ * libfdisk.h - libfdisk API
+ *
+ * Copyright (C) 2012-2014 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 General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _LIBFDISK_H
+#define _LIBFDISK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+/**
+ * LIBFDISK_VERSION:
+ *
+ * Library version string
+ */
+#define LIBFDISK_VERSION "@LIBFDISK_VERSION@"
+
+/**
+ * fdisk_context:
+ *
+ * Basic library handler.
+ */
+struct fdisk_context;
+
+/**
+ * fdisk_label:
+ *
+ * Disk label specific driver and setting.
+ */
+struct fdisk_label;
+
+/**
+ * fdisk_parttype:
+ *
+ * Partition type.
+ */
+struct fdisk_parttype;
+
+/**
+ * fdisk_partition:
+ *
+ * Partition abstraction (and template).
+ */
+struct fdisk_partition;
+
+/**
+ * fdisk_ask:
+ *
+ * Ask API handler for dialogs with users.
+ */
+struct fdisk_ask;
+
+/**
+ * fdisk_iter:
+ *
+ * Unified iterator.
+ */
+struct fdisk_iter;
+
+/**
+ * fdisk_table:
+ *
+ * Container for fdisk_partition objects
+ */
+struct fdisk_table;
+
+/**
+ * fdisk_field
+ *
+ * Output field description.
+ */
+struct fdisk_field;
+
+/**
+ * fdisk_script
+ *
+ * library handler for sfdisk compatible scripts
+ */
+struct fdisk_script;
+
+/**
+ * fdisk_sector_t
+ *
+ * LBA adresses type
+ */
+typedef uint64_t fdisk_sector_t;
+
+/**
+ * fdisk_labeltype:
+ *
+ * Supported partition table types (labels)
+ */
+enum fdisk_labeltype {
+ FDISK_DISKLABEL_DOS = (1 << 1),
+ FDISK_DISKLABEL_SUN = (1 << 2),
+ FDISK_DISKLABEL_SGI = (1 << 3),
+ FDISK_DISKLABEL_BSD = (1 << 4),
+ FDISK_DISKLABEL_GPT = (1 << 5)
+};
+
+/**
+ * fdisk_asktype:
+ *
+ * Ask API dialog types
+ */
+enum fdisk_asktype {
+ FDISK_ASKTYPE_NONE = 0,
+ FDISK_ASKTYPE_NUMBER,
+ FDISK_ASKTYPE_OFFSET,
+ FDISK_ASKTYPE_WARN,
+ FDISK_ASKTYPE_WARNX,
+ FDISK_ASKTYPE_INFO,
+ FDISK_ASKTYPE_YESNO,
+ FDISK_ASKTYPE_STRING,
+ FDISK_ASKTYPE_MENU
+};
+
+/* init.c */
+extern void fdisk_init_debug(int mask);
+
+/* context.h */
+
+#define FDISK_PLURAL 0
+#define FDISK_SINGULAR 1
+
+struct fdisk_context *fdisk_new_context(void);
+struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent, const char *name);
+void fdisk_unref_context(struct fdisk_context *cxt);
+void fdisk_ref_context(struct fdisk_context *cxt);
+
+struct fdisk_context *fdisk_get_parent(struct fdisk_context *cxt);
+size_t fdisk_get_npartitions(struct fdisk_context *cxt);
+
+struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name);
+int fdisk_next_label(struct fdisk_context *cxt, struct fdisk_label **lb);
+size_t fdisk_get_nlabels(struct fdisk_context *cxt);
+
+int fdisk_has_label(struct fdisk_context *cxt);
+int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id);
+#define fdisk_is_label(c, x) fdisk_is_labeltype(c, FDISK_DISKLABEL_ ## x)
+
+
+int fdisk_assign_device(struct fdisk_context *cxt,
+ const char *fname, int readonly);
+int fdisk_deassign_device(struct fdisk_context *cxt, int nosync);
+int fdisk_is_readonly(struct fdisk_context *cxt);
+
+int fdisk_enable_details(struct fdisk_context *cxt, int enable);
+int fdisk_is_details(struct fdisk_context *cxt);
+
+int fdisk_enable_listonly(struct fdisk_context *cxt, int enable);
+int fdisk_is_listonly(struct fdisk_context *cxt);
+
+int fdisk_set_unit(struct fdisk_context *cxt, const char *str);
+const char *fdisk_get_unit(struct fdisk_context *cxt, int n);
+int fdisk_use_cylinders(struct fdisk_context *cxt);
+unsigned int fdisk_get_units_per_sector(struct fdisk_context *cxt);
+
+unsigned long fdisk_get_optimal_iosize(struct fdisk_context *cxt);
+unsigned long fdisk_get_minimal_iosize(struct fdisk_context *cxt);
+unsigned long fdisk_get_physector_size(struct fdisk_context *cxt);
+unsigned long fdisk_get_sector_size(struct fdisk_context *cxt);
+unsigned long fdisk_get_alignment_offset(struct fdisk_context *cxt);
+unsigned long fdisk_get_grain_size(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba);
+fdisk_sector_t fdisk_get_last_lba(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_set_last_lba(struct fdisk_context *cxt, fdisk_sector_t lba);
+fdisk_sector_t fdisk_get_nsectors(struct fdisk_context *cxt);
+const char *fdisk_get_devname(struct fdisk_context *cxt);
+int fdisk_get_devfd(struct fdisk_context *cxt);
+
+unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt);
+fdisk_sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt);
+
+
+
+/* parttype.c */
+struct fdisk_parttype *fdisk_new_parttype(void);
+void fdisk_ref_parttype(struct fdisk_parttype *t);
+void fdisk_unref_parttype(struct fdisk_parttype *t);
+int fdisk_parttype_set_name(struct fdisk_parttype *t, const char *str);
+int fdisk_parttype_set_typestr(struct fdisk_parttype *t, const char *str);
+int fdisk_parttype_set_code(struct fdisk_parttype *t, int code);
+size_t fdisk_label_get_nparttypes(const struct fdisk_label *lb);
+struct fdisk_parttype *fdisk_label_get_parttype(const struct fdisk_label *lb, size_t n);
+int fdisk_label_has_code_parttypes(const struct fdisk_label *lb);
+struct fdisk_parttype *fdisk_label_get_parttype_from_code(
+ const struct fdisk_label *lb,
+ unsigned int code);
+struct fdisk_parttype *fdisk_label_get_parttype_from_string(
+ const struct fdisk_label *lb,
+ const char *str);
+struct fdisk_parttype *fdisk_new_unknown_parttype(unsigned int code,
+ const char *typestr);
+struct fdisk_parttype *fdisk_copy_parttype(const struct fdisk_parttype *type);
+struct fdisk_parttype *fdisk_label_parse_parttype(
+ const struct fdisk_label *lb,
+ const char *str);
+const char *fdisk_parttype_get_string(const struct fdisk_parttype *t);
+unsigned int fdisk_parttype_get_code(const struct fdisk_parttype *t);
+const char *fdisk_parttype_get_name(const struct fdisk_parttype *t);
+int fdisk_parttype_is_unknown(const struct fdisk_parttype *t);
+
+/* label.c */
+
+/**
+ * fdisk_fieldtype
+ *
+ * Types of fdisk_field
+ */
+enum fdisk_fieldtype {
+ FDISK_FIELD_NONE = 0,
+
+ /* generic */
+ FDISK_FIELD_DEVICE,
+ FDISK_FIELD_START,
+ FDISK_FIELD_END,
+ FDISK_FIELD_SECTORS,
+ FDISK_FIELD_CYLINDERS,
+ FDISK_FIELD_SIZE,
+ FDISK_FIELD_TYPE,
+ FDISK_FIELD_TYPEID,
+
+ /* label specific */
+ FDISK_FIELD_ATTR,
+ FDISK_FIELD_BOOT,
+ FDISK_FIELD_BSIZE,
+ FDISK_FIELD_CPG,
+ FDISK_FIELD_EADDR,
+ FDISK_FIELD_FSIZE,
+ FDISK_FIELD_NAME,
+ FDISK_FIELD_SADDR,
+ FDISK_FIELD_UUID,
+
+ FDISK_NFIELDS /* must be last */
+};
+
+int fdisk_label_get_type(const struct fdisk_label *lb);
+const char *fdisk_label_get_name(const struct fdisk_label *lb);
+int fdisk_label_require_geometry(const struct fdisk_label *lb);
+
+
+extern int fdisk_write_disklabel(struct fdisk_context *cxt);
+extern int fdisk_verify_disklabel(struct fdisk_context *cxt);
+extern int fdisk_create_disklabel(struct fdisk_context *cxt, const char *name);
+extern int fdisk_list_disklabel(struct fdisk_context *cxt);
+extern int fdisk_locate_disklabel(struct fdisk_context *cxt, int n, const char **name, off_t *offset, size_t *size);
+
+extern int fdisk_get_disklabel_id(struct fdisk_context *cxt, char **id);
+extern int fdisk_set_disklabel_id(struct fdisk_context *cxt);
+
+extern int fdisk_get_partition(struct fdisk_context *cxt, size_t partno, struct fdisk_partition **pa);
+extern int fdisk_set_partition(struct fdisk_context *cxt, size_t partno, struct fdisk_partition *pa);
+extern int fdisk_add_partition(struct fdisk_context *cxt, struct fdisk_partition *pa, size_t *partno);
+extern int fdisk_delete_partition(struct fdisk_context *cxt, size_t partno);
+
+extern int fdisk_delete_all_partitions(struct fdisk_context *cxt);
+
+extern int fdisk_set_partition_type(struct fdisk_context *cxt, size_t partnum,
+ struct fdisk_parttype *t);
+
+
+extern int fdisk_label_get_fields_ids(
+ const struct fdisk_label *lb,
+ struct fdisk_context *cxt,
+ int **ids, size_t *nids);
+
+extern const struct fdisk_field *fdisk_label_get_field(const struct fdisk_label *lb, int id);
+extern const struct fdisk_field *fdisk_label_get_field_by_name(
+ const struct fdisk_label *lb,
+ const char *name);
+
+extern int fdisk_field_get_id(const struct fdisk_field *field);
+extern const char *fdisk_field_get_name(const struct fdisk_field *field);
+extern double fdisk_field_get_width(const struct fdisk_field *field);
+extern int fdisk_field_is_number(const struct fdisk_field *field);
+
+
+extern void fdisk_label_set_changed(struct fdisk_label *lb, int changed);
+extern int fdisk_label_is_changed(const struct fdisk_label *lb);
+
+extern void fdisk_label_set_disabled(struct fdisk_label *lb, int disabled);
+extern int fdisk_label_is_disabled(const struct fdisk_label *lb);
+
+extern int fdisk_is_partition_used(struct fdisk_context *cxt, size_t n);
+
+extern int fdisk_toggle_partition_flag(struct fdisk_context *cxt, size_t partnum, unsigned long flag);
+
+extern struct fdisk_partition *fdisk_new_partition(void);
+extern void fdisk_reset_partition(struct fdisk_partition *pa);
+extern void fdisk_ref_partition(struct fdisk_partition *pa);
+extern void fdisk_unref_partition(struct fdisk_partition *pa);
+extern int fdisk_partition_is_freespace(struct fdisk_partition *pa);
+
+int fdisk_partition_set_start(struct fdisk_partition *pa, uint64_t off);
+int fdisk_partition_unset_start(struct fdisk_partition *pa);
+uint64_t fdisk_partition_get_start(struct fdisk_partition *pa);
+int fdisk_partition_has_start(struct fdisk_partition *pa);
+int fdisk_partition_cmp_start(struct fdisk_partition *a,
+ struct fdisk_partition *b);
+int fdisk_partition_start_follow_default(struct fdisk_partition *pa, int enable);
+int fdisk_partition_start_is_default(struct fdisk_partition *pa);
+
+int fdisk_partition_set_size(struct fdisk_partition *pa, uint64_t sz);
+int fdisk_partition_unset_size(struct fdisk_partition *pa);
+uint64_t fdisk_partition_get_size(struct fdisk_partition *pa);
+int fdisk_partition_has_size(struct fdisk_partition *pa);
+int fdisk_partition_size_explicit(struct fdisk_partition *pa, int enable);
+
+int fdisk_partition_has_end(struct fdisk_partition *pa);
+fdisk_sector_t fdisk_partition_get_end(struct fdisk_partition *pa);
+
+int fdisk_partition_set_partno(struct fdisk_partition *pa, size_t num);
+int fdisk_partition_unset_partno(struct fdisk_partition *pa);
+size_t fdisk_partition_get_partno(struct fdisk_partition *pa);
+int fdisk_partition_has_partno(struct fdisk_partition *pa);
+int fdisk_partition_cmp_partno(struct fdisk_partition *a,
+ struct fdisk_partition *b);
+int fdisk_partition_partno_follow_default(struct fdisk_partition *pa, int enable);
+
+
+extern int fdisk_partition_set_type(struct fdisk_partition *pa, struct fdisk_parttype *type);
+extern struct fdisk_parttype *fdisk_partition_get_type(struct fdisk_partition *pa);
+extern int fdisk_partition_set_name(struct fdisk_partition *pa, const char *name);
+extern const char *fdisk_partition_get_name(struct fdisk_partition *pa);
+extern int fdisk_partition_set_uuid(struct fdisk_partition *pa, const char *uuid);
+extern int fdisk_partition_set_attrs(struct fdisk_partition *pa, const char *attrs);
+extern const char *fdisk_partition_get_uuid(struct fdisk_partition *pa);
+extern const char *fdisk_partition_get_attrs(struct fdisk_partition *pa);
+extern int fdisk_partition_is_nested(struct fdisk_partition *pa);
+extern int fdisk_partition_is_container(struct fdisk_partition *pa);
+extern int fdisk_partition_get_parent(struct fdisk_partition *pa, size_t *parent);
+extern int fdisk_partition_is_used(struct fdisk_partition *pa);
+extern int fdisk_partition_is_bootable(struct fdisk_partition *pa);
+extern int fdisk_partition_to_string(struct fdisk_partition *pa,
+ struct fdisk_context *cxt,
+ int id, char **data);
+
+int fdisk_partition_next_partno(struct fdisk_partition *pa,
+ struct fdisk_context *cxt,
+ size_t *n);
+
+extern int fdisk_partition_end_follow_default(struct fdisk_partition *pa, int enable);
+extern int fdisk_partition_end_is_default(struct fdisk_partition *pa);
+
+extern int fdisk_reorder_partitions(struct fdisk_context *cxt);
+
+/* table.c */
+extern struct fdisk_table *fdisk_new_table(void);
+extern int fdisk_reset_table(struct fdisk_table *tb);
+extern void fdisk_ref_table(struct fdisk_table *tb);
+extern void fdisk_unref_table(struct fdisk_table *tb);
+extern size_t fdisk_table_get_nents(struct fdisk_table *tb);
+extern int fdisk_table_is_empty(struct fdisk_table *tb);
+extern int fdisk_table_add_partition(struct fdisk_table *tb, struct fdisk_partition *pa);
+extern int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_partition *pa);
+
+extern int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb);
+extern int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb);
+
+extern int fdisk_table_wrong_order(struct fdisk_table *tb);
+extern int fdisk_table_sort_partitions(struct fdisk_table *tb,
+ int (*cmp)(struct fdisk_partition *,
+ struct fdisk_partition *));
+
+extern int fdisk_table_next_partition(
+ struct fdisk_table *tb,
+ struct fdisk_iter *itr,
+ struct fdisk_partition **pa);
+
+extern struct fdisk_partition *fdisk_table_get_partition(
+ struct fdisk_table *tb,
+ size_t n);
+extern int fdisk_apply_table(struct fdisk_context *cxt, struct fdisk_table *tb);
+
+/* alignment.c */
+#define FDISK_ALIGN_UP 1
+#define FDISK_ALIGN_DOWN 2
+#define FDISK_ALIGN_NEAREST 3
+
+fdisk_sector_t fdisk_align_lba(struct fdisk_context *cxt, fdisk_sector_t lba, int direction);
+fdisk_sector_t fdisk_align_lba_in_range(struct fdisk_context *cxt,
+ fdisk_sector_t lba, fdisk_sector_t start, fdisk_sector_t stop);
+int fdisk_lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba);
+
+int fdisk_override_geometry(struct fdisk_context *cxt,
+ unsigned int cylinders,
+ unsigned int heads,
+ unsigned int sectors);
+int fdisk_save_user_geometry(struct fdisk_context *cxt,
+ unsigned int cylinders,
+ unsigned int heads,
+ unsigned int sectors);
+int fdisk_save_user_sector_size(struct fdisk_context *cxt,
+ unsigned int phy,
+ unsigned int log);
+int fdisk_has_user_device_properties(struct fdisk_context *cxt);
+int fdisk_reset_alignment(struct fdisk_context *cxt);
+int fdisk_reset_device_properties(struct fdisk_context *cxt);
+int fdisk_reread_partition_table(struct fdisk_context *cxt);
+
+/* iter.c */
+enum {
+
+ FDISK_ITER_FORWARD = 0,
+ FDISK_ITER_BACKWARD
+};
+extern struct fdisk_iter *fdisk_new_iter(int direction);
+extern void fdisk_free_iter(struct fdisk_iter *itr);
+extern void fdisk_reset_iter(struct fdisk_iter *itr, int direction);
+extern int fdisk_iter_get_direction(struct fdisk_iter *itr);
+
+
+/* dos.c */
+#define DOS_FLAG_ACTIVE 1
+
+extern int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i);
+extern int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable);
+extern int fdisk_dos_is_compatible(struct fdisk_label *lb);
+
+/* sun.h */
+extern int fdisk_sun_set_alt_cyl(struct fdisk_context *cxt);
+extern int fdisk_sun_set_xcyl(struct fdisk_context *cxt);
+extern int fdisk_sun_set_ilfact(struct fdisk_context *cxt);
+extern int fdisk_sun_set_rspeed(struct fdisk_context *cxt);
+extern int fdisk_sun_set_pcylcount(struct fdisk_context *cxt);
+
+/* bsd.c */
+extern int fdisk_bsd_edit_disklabel(struct fdisk_context *cxt);
+extern int fdisk_bsd_write_bootstrap(struct fdisk_context *cxt);
+extern int fdisk_bsd_link_partition(struct fdisk_context *cxt);
+
+/* sgi.h */
+#define SGI_FLAG_BOOT 1
+#define SGI_FLAG_SWAP 2
+extern int fdisk_sgi_set_bootfile(struct fdisk_context *cxt);
+extern int fdisk_sgi_create_info(struct fdisk_context *cxt);
+
+/* gpt */
+
+/* GPT partition attributes */
+enum {
+ /* System partition (disk partitioning utilities must preserve the
+ * partition as is) */
+ GPT_FLAG_REQUIRED = 1,
+
+ /* EFI firmware should ignore the content of the partition and not try
+ * to read from it */
+ GPT_FLAG_NOBLOCK,
+
+ /* Legacy BIOS bootable */
+ GPT_FLAG_LEGACYBOOT,
+
+ /* bites 48-63, Defined and used by the individual partition type.
+ *
+ * The flag GPT_FLAG_GUIDSPECIFIC forces libfdisk to ask (by ask API)
+ * for a bit number. If you want to toggle specific bit and avoid any
+ * dialog, then use the bit number (in range 48..63). For example:
+ *
+ * // start dialog to ask for bit number
+ * fdisk_toggle_partition_flag(cxt, n, GPT_FLAG_GUIDSPECIFIC);
+ *
+ * // toggle bit 60
+ * fdisk_toggle_partition_flag(cxt, n, 60);
+ */
+ GPT_FLAG_GUIDSPECIFIC
+};
+
+extern int fdisk_gpt_is_hybrid(struct fdisk_context *cxt);
+
+
+/* script.c */
+struct fdisk_script *fdisk_new_script(struct fdisk_context *cxt);
+struct fdisk_script *fdisk_new_script_from_file(struct fdisk_context *cxt,
+ const char *filename);
+void fdisk_ref_script(struct fdisk_script *dp);
+void fdisk_unref_script(struct fdisk_script *dp);
+
+const char *fdisk_script_get_header(struct fdisk_script *dp, const char *name);
+int fdisk_script_set_header(struct fdisk_script *dp, const char *name, const char *data);
+struct fdisk_table *fdisk_script_get_table(struct fdisk_script *dp);
+int fdisk_script_get_nlines(struct fdisk_script *dp);
+
+int fdisk_script_read_context(struct fdisk_script *dp, struct fdisk_context *cxt);
+int fdisk_script_write_file(struct fdisk_script *dp, FILE *f);
+int fdisk_script_read_file(struct fdisk_script *dp, FILE *f);
+int fdisk_script_read_line(struct fdisk_script *dp, FILE *f, char *buf, size_t bufsz);
+
+int fdisk_set_script(struct fdisk_context *cxt, struct fdisk_script *dp);
+struct fdisk_script *fdisk_get_script(struct fdisk_context *cxt);
+
+int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *dp);
+int fdisk_apply_script(struct fdisk_context *cxt, struct fdisk_script *dp);
+
+
+/* ask.c */
+#define fdisk_is_ask(a, x) (fdisk_ask_get_type(a) == FDISK_ASKTYPE_ ## x)
+
+int fdisk_set_ask(struct fdisk_context *cxt,
+ int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *),
+ void *data);
+
+
+void fdisk_ref_ask(struct fdisk_ask *ask);
+void fdisk_unref_ask(struct fdisk_ask *ask);
+const char *fdisk_ask_get_query(struct fdisk_ask *ask);
+int fdisk_ask_get_type(struct fdisk_ask *ask);
+const char *fdisk_ask_number_get_range(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask);
+int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result);
+uint64_t fdisk_ask_number_get_base(struct fdisk_ask *ask);
+uint64_t fdisk_ask_number_get_unit(struct fdisk_ask *ask);
+int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative);
+int fdisk_ask_number_inchars(struct fdisk_ask *ask);
+int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew);
+
+int fdisk_ask_number(struct fdisk_context *cxt,
+ uintmax_t low,
+ uintmax_t dflt,
+ uintmax_t high,
+ const char *query,
+ uintmax_t *result);
+char *fdisk_ask_string_get_result(struct fdisk_ask *ask);
+int fdisk_ask_string_set_result(struct fdisk_ask *ask, char *result);
+int fdisk_ask_string(struct fdisk_context *cxt,
+ const char *query,
+ char **result);
+int fdisk_ask_yesno(struct fdisk_context *cxt,
+ const char *query,
+ int *result);
+int fdisk_ask_yesno_get_result(struct fdisk_ask *ask);
+int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, int result);
+int fdisk_ask_menu_get_default(struct fdisk_ask *ask);
+int fdisk_ask_menu_set_result(struct fdisk_ask *ask, int key);
+int fdisk_ask_menu_get_result(struct fdisk_ask *ask, int *key);
+int fdisk_ask_menu_get_item(struct fdisk_ask *ask, size_t idx, int *key,
+ const char **name, const char **desc);
+size_t fdisk_ask_menu_get_nitems(struct fdisk_ask *ask);
+int fdisk_ask_print_get_errno(struct fdisk_ask *ask);
+const char *fdisk_ask_print_get_mesg(struct fdisk_ask *ask);
+
+int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...);
+int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...);
+int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...);
+
+/* utils.h */
+extern char *fdisk_partname(const char *dev, size_t partno);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBFDISK_H */
diff --git a/libblkid/libfdisk/src/libfdisk.sym b/libblkid/libfdisk/src/libfdisk.sym
new file mode 100644
index 000000000..bf85d4e2a
--- /dev/null
+++ b/libblkid/libfdisk/src/libfdisk.sym
@@ -0,0 +1,234 @@
+/*
+ * The symbol versioning ensures that a new application requiring symbol foo;
+ * can't run with old libblkid.so not providing foo;
+ * version info can't enforce this since we never change the SONAME.
+ *
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ */
+MOUNT_2.26 {
+global:
+ fdisk_add_partition;
+ fdisk_align_lba;
+ fdisk_align_lba_in_range;
+ fdisk_apply_script;
+ fdisk_apply_script_headers;
+ fdisk_apply_table;
+ fdisk_ask_get_query;
+ fdisk_ask_get_type;
+ fdisk_ask_menu_get_default;
+ fdisk_ask_menu_get_item;
+ fdisk_ask_menu_get_nitems;
+ fdisk_ask_menu_get_result;
+ fdisk_ask_menu_set_result;
+ fdisk_ask_number;
+ fdisk_ask_number_get_base;
+ fdisk_ask_number_get_default;
+ fdisk_ask_number_get_high;
+ fdisk_ask_number_get_low;
+ fdisk_ask_number_get_range;
+ fdisk_ask_number_get_result;
+ fdisk_ask_number_get_unit;
+ fdisk_ask_number_inchars;
+ fdisk_ask_number_set_relative;
+ fdisk_ask_number_set_result;
+ fdisk_ask_partnum;
+ fdisk_ask_print_get_errno;
+ fdisk_ask_print_get_mesg;
+ fdisk_ask_string;
+ fdisk_ask_string_get_result;
+ fdisk_ask_string_set_result;
+ fdisk_ask_yesno;
+ fdisk_ask_yesno_get_result;
+ fdisk_ask_yesno_set_result;
+ fdisk_assign_device;
+ fdisk_bsd_edit_disklabel;
+ fdisk_bsd_link_partition;
+ fdisk_bsd_write_bootstrap;
+ fdisk_copy_parttype;
+ fdisk_create_disklabel;
+ fdisk_deassign_device;
+ fdisk_delete_all_partitions;
+ fdisk_delete_partition;
+ fdisk_dos_enable_compatible;
+ fdisk_dos_is_compatible;
+ fdisk_dos_move_begin;
+ fdisk_enable_details;
+ fdisk_enable_listonly;
+ fdisk_field_get_id;
+ fdisk_field_get_name;
+ fdisk_field_get_width;
+ fdisk_field_is_number;
+ fdisk_free_iter;
+ fdisk_get_alignment_offset;
+ fdisk_get_devfd;
+ fdisk_get_devname;
+ fdisk_get_disklabel_id;
+ fdisk_get_first_lba;
+ fdisk_get_freespaces;
+ fdisk_get_geom_cylinders;
+ fdisk_get_geom_heads;
+ fdisk_get_geom_sectors;
+ fdisk_get_grain_size;
+ fdisk_get_label;
+ fdisk_get_last_lba;
+ fdisk_get_minimal_iosize;
+ fdisk_get_nlabels;
+ fdisk_get_npartitions;
+ fdisk_get_nsectors;
+ fdisk_get_optimal_iosize;
+ fdisk_get_parent;
+ fdisk_get_partition;
+ fdisk_get_partitions;
+ fdisk_get_physector_size;
+ fdisk_get_script;
+ fdisk_get_sector_size;
+ fdisk_get_unit;
+ fdisk_get_units_per_sector;
+ fdisk_gpt_is_hybrid;
+ fdisk_has_label;
+ fdisk_has_user_device_properties;
+ fdisk_info;
+ fdisk_init_debug;
+ fdisk_is_details;
+ fdisk_is_labeltype;
+ fdisk_is_listonly;
+ fdisk_is_partition_used;
+ fdisk_is_readonly;
+ fdisk_iter_get_direction;
+ fdisk_label_get_field;
+ fdisk_label_get_field_by_name;
+ fdisk_label_get_fields_ids;
+ fdisk_label_get_name;
+ fdisk_label_get_nparttypes;
+ fdisk_label_get_parttype;
+ fdisk_label_get_parttype_from_code;
+ fdisk_label_get_parttype_from_string;
+ fdisk_label_get_type;
+ fdisk_label_has_code_parttypes;
+ fdisk_label_is_changed;
+ fdisk_label_is_disabled;
+ fdisk_label_parse_parttype;
+ fdisk_label_require_geometry;
+ fdisk_label_set_changed;
+ fdisk_label_set_disabled;
+ fdisk_lba_is_phy_aligned;
+ fdisk_list_disklabel;
+ fdisk_locate_disklabel;
+ fdisk_new_context;
+ fdisk_new_iter;
+ fdisk_new_nested_context;
+ fdisk_new_partition;
+ fdisk_new_parttype;
+ fdisk_new_script;
+ fdisk_new_script_from_file;
+ fdisk_new_table;
+ fdisk_new_unknown_parttype;
+ fdisk_next_label;
+ fdisk_override_geometry;
+ fdisk_partition_cmp_partno;
+ fdisk_partition_cmp_start;
+ fdisk_partition_end_follow_default;
+ fdisk_partition_end_is_default;
+ fdisk_partition_get_attrs;
+ fdisk_partition_get_end;
+ fdisk_partition_get_name;
+ fdisk_partition_get_parent;
+ fdisk_partition_get_partno;
+ fdisk_partition_get_size;
+ fdisk_partition_get_start;
+ fdisk_partition_get_type;
+ fdisk_partition_get_uuid;
+ fdisk_partition_has_end;
+ fdisk_partition_has_partno;
+ fdisk_partition_has_size;
+ fdisk_partition_has_start;
+ fdisk_partition_is_bootable;
+ fdisk_partition_is_container;
+ fdisk_partition_is_freespace;
+ fdisk_partition_is_nested;
+ fdisk_partition_is_used;
+ fdisk_partition_next_partno;
+ fdisk_partition_partno_follow_default;
+ fdisk_partition_set_attrs;
+ fdisk_partition_set_name;
+ fdisk_partition_set_partno;
+ fdisk_partition_set_size;
+ fdisk_partition_set_start;
+ fdisk_partition_set_type;
+ fdisk_partition_set_uuid;
+ fdisk_partition_size_explicit;
+ fdisk_partition_start_follow_default;
+ fdisk_partition_start_is_default;
+ fdisk_toggle_partition_flag;
+ fdisk_partition_to_string;
+ fdisk_partition_unset_partno;
+ fdisk_partition_unset_size;
+ fdisk_partition_unset_start;
+ fdisk_partname;
+ fdisk_parttype_get_code;
+ fdisk_parttype_get_name;
+ fdisk_parttype_get_string;
+ fdisk_parttype_is_unknown;
+ fdisk_parttype_set_code;
+ fdisk_parttype_set_name;
+ fdisk_parttype_set_typestr;
+ fdisk_ref_ask;
+ fdisk_ref_context;
+ fdisk_ref_partition;
+ fdisk_ref_parttype;
+ fdisk_ref_script;
+ fdisk_ref_table;
+ fdisk_reorder_partitions;
+ fdisk_reread_partition_table;
+ fdisk_reset_alignment;
+ fdisk_reset_device_properties;
+ fdisk_reset_iter;
+ fdisk_reset_partition;
+ fdisk_reset_table;
+ fdisk_save_user_geometry;
+ fdisk_save_user_sector_size;
+ fdisk_script_get_header;
+ fdisk_script_get_nlines;
+ fdisk_script_get_table;
+ fdisk_script_read_context;
+ fdisk_script_read_file;
+ fdisk_script_read_line;
+ fdisk_script_set_header;
+ fdisk_script_write_file;
+ fdisk_set_ask;
+ fdisk_set_disklabel_id;
+ fdisk_set_first_lba;
+ fdisk_set_last_lba;
+ fdisk_set_partition;
+ fdisk_set_partition_type;
+ fdisk_set_script;
+ fdisk_set_unit;
+ fdisk_sgi_create_info;
+ fdisk_sgi_set_bootfile;
+ fdisk_sun_set_alt_cyl;
+ fdisk_sun_set_ilfact;
+ fdisk_sun_set_pcylcount;
+ fdisk_sun_set_rspeed;
+ fdisk_sun_set_xcyl;
+ fdisk_table_add_partition;
+ fdisk_table_get_nents;
+ fdisk_table_get_partition;
+ fdisk_table_is_empty;
+ fdisk_table_next_partition;
+ fdisk_table_remove_partition;
+ fdisk_table_sort_partitions;
+ fdisk_table_wrong_order;
+ fdisk_unref_ask;
+ fdisk_unref_context;
+ fdisk_unref_partition;
+ fdisk_unref_parttype;
+ fdisk_unref_script;
+ fdisk_unref_table;
+ fdisk_use_cylinders;
+ fdisk_verify_disklabel;
+ fdisk_warn;
+ fdisk_warnx;
+ fdisk_write_disklabel;
+local:
+ *;
+};
diff --git a/libblkid/libfdisk/src/partition.c b/libblkid/libfdisk/src/partition.c
new file mode 100644
index 000000000..8f8402716
--- /dev/null
+++ b/libblkid/libfdisk/src/partition.c
@@ -0,0 +1,963 @@
+
+#include "c.h"
+#include "strutils.h"
+
+#include "fdiskP.h"
+
+/**
+ * SECTION: partition
+ * @title: Partition
+ * @short_description: generic label independent partition abstraction
+ *
+ * The fdisk_partition provides label independent abstraction. The partitions
+ * are not directly connected with partition table (label) data. Any change to
+ * fdisk_partition does not affects in-memory or on-disk label data.
+ *
+ * The fdisk_partition is possible to use as a template for
+ * fdisk_add_partition() or fdisk_set_partition() operations.
+ */
+
+static void init_partition(struct fdisk_partition *pa)
+{
+ FDISK_INIT_UNDEF(pa->size);
+ FDISK_INIT_UNDEF(pa->start);
+ FDISK_INIT_UNDEF(pa->partno);
+ FDISK_INIT_UNDEF(pa->parent_partno);
+ FDISK_INIT_UNDEF(pa->boot);
+
+ INIT_LIST_HEAD(&pa->parts);
+}
+
+/**
+ * fdisk_new_partition:
+ *
+ * Returns: new instance.
+ */
+struct fdisk_partition *fdisk_new_partition(void)
+{
+ struct fdisk_partition *pa = calloc(1, sizeof(*pa));
+
+ pa->refcount = 1;
+ init_partition(pa);
+ DBG(PART, ul_debugobj(pa, "alloc"));
+ return pa;
+}
+
+/**
+ * fdisk_reset_partition:
+ * @pa: partition
+ *
+ * Resets partition content.
+ */
+void fdisk_reset_partition(struct fdisk_partition *pa)
+{
+ int ref;
+
+ if (!pa)
+ return;
+
+ DBG(PART, ul_debugobj(pa, "reset"));
+ ref = pa->refcount;
+
+ fdisk_unref_parttype(pa->type);
+ free(pa->name);
+ free(pa->uuid);
+ free(pa->attrs);
+
+ memset(pa, 0, sizeof(*pa));
+ pa->refcount = ref;
+
+ init_partition(pa);
+}
+
+/**
+ * fdisk_ref_partition:
+ * @pa: partition pointer
+ *
+ * Incremparts reference counter.
+ */
+void fdisk_ref_partition(struct fdisk_partition *pa)
+{
+ if (pa)
+ pa->refcount++;
+}
+
+/**
+ * fdisk_unref_partition:
+ * @pa: partition pointer
+ *
+ * De-incremparts reference counter, on zero the @pa is automatically
+ * deallocated.
+ */
+void fdisk_unref_partition(struct fdisk_partition *pa)
+{
+ if (!pa)
+ return;
+
+ pa->refcount--;
+ if (pa->refcount <= 0) {
+ fdisk_reset_partition(pa);
+ list_del(&pa->parts);
+ DBG(PART, ul_debugobj(pa, "free"));
+ free(pa);
+ }
+}
+
+/**
+ * fdisk_partition_set_start:
+ * @pa: partition
+ * @off: offset in sectors, maximal is UINT64_MAX-1
+ *
+ * Note that zero is valid offset too. Use fdisk_partition_unset_start() to
+ * undefine the offset.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_set_start(struct fdisk_partition *pa, fdisk_sector_t off)
+{
+ if (!pa)
+ return -EINVAL;
+ if (FDISK_IS_UNDEF(off))
+ return -ERANGE;
+ pa->start = off;
+ return 0;
+}
+
+/**
+ * fdisk_partition_unset_start:
+ * @pa: partition
+ *
+ * Sets the size as undefined. See fdisk_partition_has_start().
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_unset_start(struct fdisk_partition *pa)
+{
+ if (!pa)
+ return -EINVAL;
+ FDISK_INIT_UNDEF(pa->start);
+ return 0;
+}
+
+/**
+ * fdisk_partition_get_start:
+ * @pa: partition
+ *
+ * The zero is also valid offset. The function may return random undefined
+ * value when start offset is undefined (for example after
+ * fdisk_partition_unset_start()). Always use fdisk_partition_has_start() to be
+ * sure that you work with valid numbers.
+ *
+ * Returns: start offset in sectors
+ */
+fdisk_sector_t fdisk_partition_get_start(struct fdisk_partition *pa)
+{
+ return pa->start;
+}
+
+/**
+ * fdisk_partition_has_start:
+ * @pa: partition
+ *
+ * Returns: 1 or 0
+ */
+int fdisk_partition_has_start(struct fdisk_partition *pa)
+{
+ return pa && !FDISK_IS_UNDEF(pa->start);
+}
+
+
+/**
+ * fdisk_partition_cmp_start:
+ * @a: partition
+ * @b: partition
+ *
+ * Compares partitons according to start offset, See fdisk_sort_table().
+ *
+ * Return: 0 if the same, <0 if @b greater, >0 if @a greater.
+ */
+int fdisk_partition_cmp_start(struct fdisk_partition *a,
+ struct fdisk_partition *b)
+{
+ int no_a = FDISK_IS_UNDEF(a->start),
+ no_b = FDISK_IS_UNDEF(b->start);
+
+ if (no_a && no_b)
+ return 0;
+ if (no_a)
+ return -1;
+ if (no_b)
+ return 1;
+
+ return cmp_numbers(a->start, b->start);
+}
+
+/**
+ * fdisk_partition_start_follow_default
+ * @pa: partition
+ * @enable: 0|1
+ *
+ * When @pa used as a tempalate for fdisk_add_partition() when force label driver
+ * to use the first possible space for the new partition.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_start_follow_default(struct fdisk_partition *pa, int enable)
+{
+ if (!pa)
+ return -EINVAL;
+ pa->start_follow_default = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * fdisk_partition_start_is_default:
+ * @pa: partition
+ *
+ * See fdisk_partition_start_follow_default().
+ *
+ * Returns: 1 if the partition follows default
+ */
+int fdisk_partition_start_is_default(struct fdisk_partition *pa)
+{
+ assert(pa);
+ return pa->start_follow_default;
+}
+
+
+/**
+ * fdisk_partition_set_size:
+ * @pa: partition
+ * @sz: size in sectors, maximal is UIN64_MAX-1
+ *
+ * Note that zero is valid size too. Use fdisk_partition_unset_size() to
+ * undefine the size.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_set_size(struct fdisk_partition *pa, fdisk_sector_t sz)
+{
+ if (!pa)
+ return -EINVAL;
+ if (FDISK_IS_UNDEF(sz))
+ return -ERANGE;
+ pa->size = sz;
+ return 0;
+}
+
+/**
+ * fdisk_partition_unset_size:
+ * @pa: partition
+ *
+ * Sets the size as undefined. See fdisk_partition_has_size().
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_unset_size(struct fdisk_partition *pa)
+{
+ if (!pa)
+ return -EINVAL;
+ FDISK_INIT_UNDEF(pa->size);
+ return 0;
+}
+
+/**
+ * fdisk_partition_get_size:
+ * @pa: partition
+ *
+ * The zero is also valid size. The function may return random undefined
+ * value when size is undefined (for example after fdisk_partition_unset_size()).
+ * Always use fdisk_partition_has_size() to be sure that you work with valid
+ * numbers.
+ *
+ * Returns: size offset in sectors
+ */
+fdisk_sector_t fdisk_partition_get_size(struct fdisk_partition *pa)
+{
+ return pa->size;
+}
+
+/**
+ * fdisk_partition_has_size:
+ * @pa: partition
+ *
+ * Returns: 1 or 0
+ */
+int fdisk_partition_has_size(struct fdisk_partition *pa)
+{
+ return pa && !FDISK_IS_UNDEF(pa->size);
+}
+
+/**
+ * fdisk_partition_size_explicit:
+ * @pa: partition
+ * @enable: 0|1
+ *
+ * By default libfdisk aligns the size when add the new partition (by
+ * fdisk_add_partrition()). If you want to disable this functionality use
+ * @enable = 1.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_size_explicit(struct fdisk_partition *pa, int enable)
+{
+ if (!pa)
+ return -EINVAL;
+ pa->size_explicit = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * fdisk_partition_set_partno:
+ * @pa: partition
+ * @num: partitin number (0 is the first partition, maximal is SIZE_MAX-1)
+ *
+ * Note that zero is valid partno too. Use fdisk_partition_unset_partno() to
+ * undefine the partno.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_set_partno(struct fdisk_partition *pa, size_t num)
+{
+ if (!pa)
+ return -EINVAL;
+ if (FDISK_IS_UNDEF(num))
+ return -ERANGE;
+ pa->partno = num;
+ return 0;
+}
+
+/**
+ * fdisk_partition_unset_partno:
+ * @pa: partition
+ *
+ * Sets the partno as undefined. See fdisk_partition_has_partno().
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_unset_partno(struct fdisk_partition *pa)
+{
+ if (!pa)
+ return -EINVAL;
+ FDISK_INIT_UNDEF(pa->partno);
+ return 0;
+}
+
+/**
+ * fdisk_partition_get_partno:
+ * @pa: partition
+ *
+ * The zero is also valid parition number. The function may return random
+ * value when partno is undefined (for example after fdisk_partition_unset_partno()).
+ * Always use fdisk_partition_has_partno() to be sure that you work with valid
+ * numbers.
+ *
+ * Returns: partition number (0 is the first partition)
+ */
+size_t fdisk_partition_get_partno(struct fdisk_partition *pa)
+{
+ return pa->partno;
+}
+
+/**
+ * fdisk_partition_has_partno:
+ * @pa: partition
+ *
+ * Returns: 1 or 0
+ */
+int fdisk_partition_has_partno(struct fdisk_partition *pa)
+{
+ return pa && !FDISK_IS_UNDEF(pa->partno);
+}
+
+
+/**
+ * fdisk_partition_cmp_partno:
+ * @a: partition
+ * @b: partition
+ *
+ * Compares partitons according to partition number See fdisk_sort_table().
+ *
+ * Return: 0 if the same, <0 if @b greater, >0 if @a greater.
+ */
+int fdisk_partition_cmp_partno(struct fdisk_partition *a,
+ struct fdisk_partition *b)
+{
+ return a->partno - b->partno;
+}
+
+/**
+ * fdisk_partition_partno_follow_default
+ * @pa: partition
+ * @enable: 0|1
+ *
+ * When @pa used as a tempalate for fdisk_add_partition() when force label driver
+ * to add a new partition to the default (next) position.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_partno_follow_default(struct fdisk_partition *pa, int enable)
+{
+ if (!pa)
+ return -EINVAL;
+ pa->partno_follow_default = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * fdisk_partition_set_type:
+ * @pa: partition
+ * @type: partition type
+ *
+ * Sets parition type.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_set_type(struct fdisk_partition *pa,
+ struct fdisk_parttype *type)
+{
+ if (!pa)
+ return -EINVAL;
+
+ fdisk_ref_parttype(type);
+ fdisk_unref_parttype(pa->type);
+ pa->type = type;
+
+ return 0;
+}
+
+/**
+ * fdisk_partition_get_type:
+ * @pa: partition
+ *
+ * Returns: pointer to partition type.
+ */
+struct fdisk_parttype *fdisk_partition_get_type(struct fdisk_partition *pa)
+{
+ return pa ? pa->type : NULL;
+}
+
+int fdisk_partition_set_name(struct fdisk_partition *pa, const char *name)
+{
+ char *p = NULL;
+
+ if (!pa)
+ return -EINVAL;
+ if (name) {
+ p = strdup(name);
+ if (!p)
+ return -ENOMEM;
+ }
+ free(pa->name);
+ pa->name = p;
+ return 0;
+}
+
+const char *fdisk_partition_get_name(struct fdisk_partition *pa)
+{
+ return pa ? pa->name : NULL;
+}
+
+int fdisk_partition_set_uuid(struct fdisk_partition *pa, const char *uuid)
+{
+ char *p = NULL;
+
+ if (!pa)
+ return -EINVAL;
+ if (uuid) {
+ p = strdup(uuid);
+ if (!p)
+ return -ENOMEM;
+ }
+ free(pa->uuid);
+ pa->uuid = p;
+ return 0;
+}
+
+/**
+ * fdisk_partition_has_end:
+ * @pa: partition
+ *
+ * Returns: 1 if the partition has defined last sector
+ */
+int fdisk_partition_has_end(struct fdisk_partition *pa)
+{
+ return pa && !FDISK_IS_UNDEF(pa->start) && !FDISK_IS_UNDEF(pa->size);
+}
+
+/**
+ * fdisk_partition_get_end:
+ * @pa: partition
+ *
+ * This function may returns absolute non-sense, always check
+ * fdisk_partition_has_end().
+ *
+ * Note that partition end is defined by fdisk_partition_set_start() and
+ * fdisk_partition_set_size().
+ *
+ * Returns: last partition sector LBA.
+ */
+fdisk_sector_t fdisk_partition_get_end(struct fdisk_partition *pa)
+{
+ return pa->start + pa->size - (pa->size == 0 ? 0 : 1);
+}
+
+/**
+ * fdisk_partition_end_follow_default
+ * @pa: partition
+ * @enable: 0|1
+ *
+ * When @pa used as a tempalate for fdisk_add_partition() when force label driver
+ * to use all the possible space for the new partition.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_partition_end_follow_default(struct fdisk_partition *pa, int enable)
+{
+ if (!pa)
+ return -EINVAL;
+ pa->end_follow_default = enable ? 1 : 0;
+ return 0;
+}
+
+/**
+ * fdisk_partition_end_is_default:
+ * @pa: partition
+ *
+ * Returns: 1 if the partition follows default
+ */
+int fdisk_partition_end_is_default(struct fdisk_partition *pa)
+{
+ assert(pa);
+ return pa->end_follow_default;
+}
+
+const char *fdisk_partition_get_uuid(struct fdisk_partition *pa)
+{
+ return pa ? pa->uuid : NULL;
+}
+
+const char *fdisk_partition_get_attrs(struct fdisk_partition *pa)
+{
+ return pa ? pa->attrs : NULL;
+}
+
+int fdisk_partition_set_attrs(struct fdisk_partition *pa, const char *attrs)
+{
+ char *p = NULL;
+
+ if (!pa)
+ return -EINVAL;
+ if (attrs) {
+ p = strdup(attrs);
+ if (!p)
+ return -ENOMEM;
+ }
+ free(pa->attrs);
+ pa->attrs = p;
+ return 0;
+}
+
+int fdisk_partition_is_nested(struct fdisk_partition *pa)
+{
+ return pa && !FDISK_IS_UNDEF(pa->parent_partno);
+}
+
+int fdisk_partition_is_container(struct fdisk_partition *pa)
+{
+ return pa && pa->container;
+}
+
+int fdisk_partition_get_parent(struct fdisk_partition *pa, size_t *parent)
+{
+ if (pa && parent)
+ *parent = pa->parent_partno;
+ else
+ return -EINVAL;
+ return 0;
+}
+
+int fdisk_partition_is_used(struct fdisk_partition *pa)
+{
+ return pa && pa->used;
+}
+
+int fdisk_partition_is_bootable(struct fdisk_partition *pa)
+{
+ return pa && pa->boot == 1;
+}
+
+int fdisk_partition_is_freespace(struct fdisk_partition *pa)
+{
+ return pa && pa->freespace;
+}
+
+int fdisk_partition_next_partno(
+ struct fdisk_partition *pa,
+ struct fdisk_context *cxt,
+ size_t *n)
+{
+ assert(cxt);
+ assert(n);
+
+ if (pa && pa->partno_follow_default) {
+ size_t i;
+
+ DBG(PART, ul_debugobj(pa, "next partno (follow default)"));
+
+ for (i = 0; i < cxt->label->nparts_max; i++) {
+ if (!fdisk_is_partition_used(cxt, i)) {
+ *n = i;
+ return 0;
+ }
+ }
+ return -ERANGE;
+
+ } else if (pa && fdisk_partition_has_partno(pa)) {
+
+ DBG(PART, ul_debugobj(pa, "next partno (specified=%zu)", pa->partno));
+
+ if (pa->partno >= cxt->label->nparts_max)
+ return -ERANGE;
+ *n = pa->partno;
+ } else
+ return fdisk_ask_partnum(cxt, n, 1);
+
+ return 0;
+}
+
+/**
+ * fdisk_partition_to_string:
+ * @pa: partition
+ * @cxt: context
+ * @id: field (FDISK_FIELD_*)
+ * @data: returns string with allocated data
+ *
+ * Returns info about partition converted to printable string.
+ *
+ * For example
+ * <informalexample>
+ * <programlisting>
+ * struct fdisk_parition *pa;
+ *
+ * fdisk_get_partition(cxt, 0, &pa);
+ * fdisk_partition_to_string(pa, FDISK_FIELD_UUID, &data);
+ * printf("first partition uuid: %s\n", data);
+ * free(data);
+ * fdisk_unref_partition(pa);
+ * </programlisting>
+ * </informalexample>
+ *
+ * returns UUID for the first partition.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_partition_to_string(struct fdisk_partition *pa,
+ struct fdisk_context *cxt,
+ int id,
+ char **data)
+{
+ char *p = NULL;
+ int rc = 0;
+ uint64_t x;
+
+ if (!pa || !cxt)
+ return -EINVAL;
+
+ switch (id) {
+ case FDISK_FIELD_DEVICE:
+ if (pa->freespace)
+ p = strdup(_("Free space"));
+ else if (fdisk_partition_has_partno(pa) && cxt->dev_path) {
+ if (cxt->label->flags & FDISK_LABEL_FL_INCHARS_PARTNO)
+ rc = asprintf(&p, "%c", (int) pa->partno + 'a');
+ else
+ p = fdisk_partname(cxt->dev_path, pa->partno + 1);
+ }
+ break;
+ case FDISK_FIELD_BOOT:
+ if (fdisk_partition_is_bootable(pa))
+ rc = asprintf(&p, "%c", pa->boot ? '*' : ' ');
+ break;
+ case FDISK_FIELD_START:
+ if (fdisk_partition_has_start(pa)) {
+ x = fdisk_cround(cxt, pa->start);
+ rc = pa->start_post ?
+ asprintf(&p, "%ju%c", x, pa->start_post) :
+ asprintf(&p, "%ju", x);
+ }
+ break;
+ case FDISK_FIELD_END:
+ if (fdisk_partition_has_end(pa)) {
+ x = fdisk_cround(cxt, fdisk_partition_get_end(pa));
+ rc = pa->end_post ?
+ asprintf(&p, "%ju%c", x, pa->end_post) :
+ asprintf(&p, "%ju", x);
+ }
+ break;
+ case FDISK_FIELD_SIZE:
+ if (fdisk_partition_has_size(pa)) {
+ uint64_t sz = pa->size * cxt->sector_size;
+
+ if (fdisk_is_details(cxt)) {
+ rc = pa->size_post ?
+ asprintf(&p, "%ju%c", sz, pa->size_post) :
+ asprintf(&p, "%ju", sz);
+ } else {
+ p = size_to_human_string(SIZE_SUFFIX_1LETTER, sz);
+ if (!p)
+ rc = -ENOMEM;
+ }
+ }
+ break;
+ case FDISK_FIELD_CYLINDERS:
+ rc = asprintf(&p, "%ju", (uintmax_t)
+ fdisk_cround(cxt, fdisk_partition_has_size(pa) ? pa->size : 0));
+ break;
+ case FDISK_FIELD_SECTORS:
+ rc = asprintf(&p, "%ju",
+ fdisk_partition_has_size(pa) ? (uintmax_t) pa->size : 0);
+ break;
+ case FDISK_FIELD_BSIZE:
+ rc = asprintf(&p, "%ju", pa->bsize);
+ break;
+ case FDISK_FIELD_FSIZE:
+ rc = asprintf(&p, "%ju", pa->fsize);
+ break;
+ case FDISK_FIELD_CPG:
+ rc = asprintf(&p, "%ju", pa->cpg);
+ break;
+ case FDISK_FIELD_TYPE:
+ p = pa->type && pa->type->name ? strdup(pa->type->name) : NULL;
+ break;
+ case FDISK_FIELD_TYPEID:
+ if (pa->type && fdisk_parttype_get_string(pa->type))
+ rc = asprintf(&p, "%s", fdisk_parttype_get_string(pa->type));
+ else if (pa->type)
+ rc = asprintf(&p, "%x", fdisk_parttype_get_code(pa->type));
+ break;
+ case FDISK_FIELD_UUID:
+ p = pa->uuid ? strdup(pa->uuid) : NULL;
+ break;
+ case FDISK_FIELD_NAME:
+ p = pa->name ? strdup(pa->name) : NULL;
+ break;
+ case FDISK_FIELD_ATTR:
+ p = pa->attrs ? strdup(pa->attrs) : NULL;
+ break;
+ case FDISK_FIELD_SADDR:
+ p = pa->start_chs ? strdup(pa->start_chs) : NULL;
+ break;
+ case FDISK_FIELD_EADDR:
+ p = pa->end_chs ? strdup(pa->end_chs) : NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rc < 0)
+ rc = -ENOMEM;
+ else if (rc > 0)
+ rc = 0;
+
+ if (data)
+ *data = p;
+ return rc;
+}
+
+/**
+ * fdisk_get_partition:
+ * @cxt: context
+ * @partno: partition number (0 is the first partition)
+ * @pa: returns data about partition
+ *
+ * Reads disklabel and fills in @pa with data about partition @n.
+ *
+ * Note that partno may address unused partition and then this function does
+ * not fill anything to @pa. See fdisk_is_partition_used(). If @pa points to
+ * NULL then the function allocates a newly allocated fdisk_partition struct,
+ * use fdisk_unref_partition() to deallocate.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_get_partition(struct fdisk_context *cxt, size_t partno,
+ struct fdisk_partition **pa)
+{
+ int rc;
+ struct fdisk_partition *np = NULL;
+
+ if (!cxt || !cxt->label || !pa)
+ return -EINVAL;
+ if (!cxt->label->op->get_part)
+ return -ENOSYS;
+ if (!fdisk_is_partition_used(cxt, partno))
+ return -EINVAL;
+
+ if (!*pa) {
+ np = *pa = fdisk_new_partition();
+ if (!*pa)
+ return -ENOMEM;
+ } else
+ fdisk_reset_partition(*pa);
+
+ (*pa)->partno = partno;
+ rc = cxt->label->op->get_part(cxt, partno, *pa);
+
+ if (rc) {
+ if (np) {
+ fdisk_unref_partition(np);
+ *pa = NULL;
+ } else
+ fdisk_reset_partition(*pa);
+ } else
+ (*pa)->size_explicit = 1;
+ return rc;
+}
+
+/**
+ * fdisk_set_partition:
+ * @cxt: context
+ * @partno: partition number (0 is the first partition)
+ * @pa: new partition setting
+ *
+ * Modifies disklabel according to setting with in @pa.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_set_partition(struct fdisk_context *cxt, size_t partno,
+ struct fdisk_partition *pa)
+{
+ if (!cxt || !cxt->label || !pa)
+ return -EINVAL;
+ if (!cxt->label->op->set_part)
+ return -ENOSYS;
+
+ DBG(CXT, ul_debugobj(cxt, "setting partition %zu %p (start=%ju, end=%ju, size=%ju, "
+ "defaults(start=%s, end=%s, partno=%s)",
+ partno, pa,
+ (uintmax_t) fdisk_partition_get_start(pa),
+ (uintmax_t) fdisk_partition_get_end(pa),
+ (uintmax_t) fdisk_partition_get_size(pa),
+ pa->start_follow_default ? "yes" : "no",
+ pa->end_follow_default ? "yes" : "no",
+ pa->partno_follow_default ? "yes" : "no"));
+
+ return cxt->label->op->set_part(cxt, partno, pa);
+}
+
+/**
+ * fdisk_add_partition:
+ * @cxt: fdisk context
+ * @pa: template for the partition (or NULL)
+ * @partno: NULL or returns new partition number
+ *
+ * If @pa is not specified or any @pa item is missiong the libfdisk will ask by
+ * fdisk_ask_ API.
+ *
+ * Adds a new partition to disklabel.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_add_partition(struct fdisk_context *cxt,
+ struct fdisk_partition *pa,
+ size_t *partno)
+{
+ int rc;
+
+ assert(cxt);
+ assert(cxt->label);
+
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+ if (!cxt->label->op->add_part)
+ return -ENOSYS;
+ if (fdisk_missing_geometry(cxt))
+ return -EINVAL;
+
+ if (pa)
+ DBG(CXT, ul_debugobj(cxt, "adding new partition %p (start=%ju, end=%ju, size=%ju, "
+ "defaults(start=%s, end=%s, partno=%s)",
+ pa,
+ (uintmax_t) fdisk_partition_get_start(pa),
+ (uintmax_t) fdisk_partition_get_end(pa),
+ (uintmax_t) fdisk_partition_get_size(pa),
+ pa->start_follow_default ? "yes" : "no",
+ pa->end_follow_default ? "yes" : "no",
+ pa->partno_follow_default ? "yes" : "no"));
+ else
+ DBG(CXT, ul_debugobj(cxt, "adding partition"));
+
+ rc = cxt->label->op->add_part(cxt, pa, partno);
+
+ DBG(CXT, ul_debugobj(cxt, "add partition done (rc=%d)", rc));
+ return rc;
+}
+
+/**
+ * fdisk_delete_partition:
+ * @cxt: fdisk context
+ * @partno: partition number to delete (0 is the first partition)
+ *
+ * Deletes a @partno partition from disklabel.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_delete_partition(struct fdisk_context *cxt, size_t partno)
+{
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+ if (!cxt->label->op->del_part)
+ return -ENOSYS;
+
+ DBG(CXT, ul_debugobj(cxt, "deleting %s partition number %zd",
+ cxt->label->name, partno));
+ return cxt->label->op->del_part(cxt, partno);
+}
+
+/**
+ * fdisk_delete_all_partitions:
+ * @cxt: fdisk context
+ *
+ * Delete all used partitions from disklabel.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_delete_all_partitions(struct fdisk_context *cxt)
+{
+ size_t i;
+ int rc;
+
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+
+ for (i = 0; i < cxt->label->nparts_max; i++) {
+
+ if (!fdisk_is_partition_used(cxt, i))
+ continue;
+ rc = fdisk_delete_partition(cxt, i);
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * fdisk_is_partition_used:
+ * @cxt: context
+ * @n: partition number (0 is the first partition)
+ *
+ * This is faster than fdisk_get_partition() + fdisk_partition_is_used().
+ *
+ * Returns: 0 or 1
+ */
+int fdisk_is_partition_used(struct fdisk_context *cxt, size_t n)
+{
+ if (!cxt || !cxt->label)
+ return -EINVAL;
+ if (!cxt->label->op->part_is_used)
+ return -ENOSYS;
+
+ return cxt->label->op->part_is_used(cxt, n);
+}
+
diff --git a/libblkid/libfdisk/src/parttype.c b/libblkid/libfdisk/src/parttype.c
new file mode 100644
index 000000000..aedf4e83b
--- /dev/null
+++ b/libblkid/libfdisk/src/parttype.c
@@ -0,0 +1,401 @@
+
+#include <ctype.h>
+
+#include "nls.h"
+#include "fdiskP.h"
+
+/**
+ * SECTION: parttype
+ * @title: Partition types
+ * @short_description: abstraction to partition types
+ *
+ * There are two basic types of parttypes, string based (e.g. GPT)
+ * and code/hex based (e.g. MBR).
+ */
+
+/**
+ * fdisk_new_parttype:
+ *
+ * It's recommended to use fdisk_label_get_parttype_from_code() or
+ * fdisk_label_get_parttype_from_string() for well known types rather
+ * than allocate a new instance.
+ *
+ * Returns: new instance.
+ */
+struct fdisk_parttype *fdisk_new_parttype(void)
+{
+ struct fdisk_parttype *t = calloc(1, sizeof(*t));
+
+ t->refcount = 1;
+ t->flags = FDISK_PARTTYPE_ALLOCATED;
+ DBG(PARTTYPE, ul_debugobj(t, "alloc"));
+ return t;
+}
+
+/**
+ * fdisk_ref_parttype:
+ * @t: partition type
+ *
+ * Incremparts reference counter for allocated types
+ */
+void fdisk_ref_parttype(struct fdisk_parttype *t)
+{
+ if (fdisk_parttype_is_allocated(t))
+ t->refcount++;
+}
+
+/**
+ * fdisk_unref_parttype
+ * @t: partition pointer
+ *
+ * De-incremparts reference counter, on zero the @t is automatically
+ * deallocated.
+ */
+void fdisk_unref_parttype(struct fdisk_parttype *t)
+{
+ if (!fdisk_parttype_is_allocated(t))
+ return;
+
+ t->refcount--;
+ if (t->refcount <= 0) {
+ DBG(PARTTYPE, ul_debugobj(t, "free"));
+ free(t->typestr);
+ free(t->name);
+ free(t);
+ }
+}
+
+/**
+ * fdisk_parttype_set_name:
+ * @t: partition type
+ * @str: type name
+ *
+ * Sets type name to allocated partition type, for static types
+ * it returns -EINVAL.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int fdisk_parttype_set_name(struct fdisk_parttype *t, const char *str)
+{
+ char *p = NULL;
+
+ if (!t || !fdisk_parttype_is_allocated(t))
+ return -EINVAL;
+ if (str) {
+ p = strdup(str);
+ if (!p)
+ return -ENOMEM;
+ }
+
+ free(t->name);
+ t->name = p;
+ return 0;
+}
+
+/**
+ * fdisk_parttype_set_typestr:
+ * @t: partition type
+ * @str: type identificator (e.g. GUID for GPT)
+ *
+ * Sets type string to allocated partition type, for static types
+ * it returns -EINVAL. Don't use this function for MBR, see
+ * fdisk_parttype_set_code().
+ *
+ * Return: 0 on success, <0 on error
+ */
+int fdisk_parttype_set_typestr(struct fdisk_parttype *t, const char *str)
+{
+ char *p = NULL;
+
+ if (!t || !fdisk_parttype_is_allocated(t))
+ return -EINVAL;
+ if (str) {
+ p = strdup(str);
+ if (!p)
+ return -ENOMEM;
+ }
+
+ free(t->typestr);
+ t->typestr = p;
+ return 0;
+}
+
+/**
+ * fdisk_parttype_set_code:
+ * @t: partition type
+ * @code: type identificator (e.g. MBR type codes)
+ *
+ * Sets type code to allocated partition type, for static types it returns
+ * -EINVAL. Don't use this function for GPT, see fdisk_parttype_set_typestr().
+ *
+ * Return: 0 on success, <0 on error
+ */
+int fdisk_parttype_set_code(struct fdisk_parttype *t, int code)
+{
+ if (!t || !fdisk_parttype_is_allocated(t))
+ return -EINVAL;
+ t->code = code;
+ return 0;
+}
+
+/**
+ * fdisk_label_get_nparttypes:
+ * @lb: label
+ *
+ * Returns: number of types supported by label.
+ */
+size_t fdisk_label_get_nparttypes(const struct fdisk_label *lb)
+{
+ if (!lb)
+ return 0;
+ return lb->nparttypes;
+}
+
+/**
+ * fdisk_label_get_parttype:
+ * @lb: label
+ * @n: number
+ *
+ * Returns: return parttype
+ */
+struct fdisk_parttype *fdisk_label_get_parttype(const struct fdisk_label *lb, size_t n)
+{
+ if (!lb || n >= lb->nparttypes)
+ return NULL;
+ return &lb->parttypes[n];
+}
+
+/**
+ * fdisk_label_has_code_parttypes:
+ * @lb: label
+ *
+ * Returns: 1 if the label uses code as partition type
+ * identifiers (e.g. MBR) or 0.
+ */
+int fdisk_label_has_code_parttypes(const struct fdisk_label *lb)
+{
+ assert(lb);
+
+ if (lb->parttypes && lb->parttypes[0].typestr)
+ return 0;
+ return 1;
+}
+
+
+/**
+ * fdisk_label_get_parttype_from_code:
+ * @lb: label
+ * @code: code to search for
+ *
+ * Search for partition type in label-specific table. The result
+ * is pointer to static array of label types.
+ *
+ * Returns: partition type or NULL upon failure or invalid @code.
+ */
+struct fdisk_parttype *fdisk_label_get_parttype_from_code(
+ const struct fdisk_label *lb,
+ unsigned int code)
+{
+ size_t i;
+
+ assert(lb);
+
+ if (!lb->nparttypes)
+ return NULL;
+
+ for (i = 0; i < lb->nparttypes; i++)
+ if (lb->parttypes[i].code == code)
+ return &lb->parttypes[i];
+ return NULL;
+}
+
+/**
+ * fdisk_label_get_parttype_from_string:
+ * @lb: label
+ * @str: string to search for
+ *
+ * Search for partition type in label-specific table. The result
+ * is pointer to static array of label types.
+ *
+ * Returns: partition type or NULL upon failure or invalid @str.
+ */
+struct fdisk_parttype *fdisk_label_get_parttype_from_string(
+ const struct fdisk_label *lb,
+ const char *str)
+{
+ size_t i;
+
+ assert(lb);
+
+ if (!lb->nparttypes)
+ return NULL;
+
+ for (i = 0; i < lb->nparttypes; i++)
+ if (lb->parttypes[i].typestr
+ && strcasecmp(lb->parttypes[i].typestr, str) == 0)
+ return &lb->parttypes[i];
+
+ return NULL;
+}
+
+/**
+ * fdisk_new_unknown_parttype:
+ * @code: type as number
+ * @typestr: type as string
+
+ * Allocates new 'unknown' partition type. Use fdisk_unref_parttype() to
+ * deallocate.
+ *
+ * Returns: newly allocated partition type, or NULL upon failure.
+ */
+struct fdisk_parttype *fdisk_new_unknown_parttype(unsigned int code,
+ const char *typestr)
+{
+ struct fdisk_parttype *t = fdisk_new_parttype();
+
+ if (!t)
+ return NULL;
+
+ fdisk_parttype_set_name(t, _("unknown"));
+ fdisk_parttype_set_code(t, code);
+ fdisk_parttype_set_typestr(t, typestr);
+ t->flags |= FDISK_PARTTYPE_UNKNOWN;
+
+ return t;
+}
+
+/**
+ * fdisk_copy_parttype:
+ * @type: type to copy
+ *
+ * Use fdisk_unref_parttype() to deallocate.
+ *
+ * Returns: newly allocated partition type, or NULL upon failure.
+ */
+struct fdisk_parttype *fdisk_copy_parttype(const struct fdisk_parttype *type)
+{
+ struct fdisk_parttype *t = fdisk_new_parttype();
+
+ if (!t)
+ return NULL;
+
+ fdisk_parttype_set_name(t, type->name);
+ fdisk_parttype_set_code(t, type->code);
+ fdisk_parttype_set_typestr(t, type->typestr);
+
+ return t;
+}
+
+/**
+ * fdisk_label_parse_parttype:
+ * @lb: label
+ * @str: string to parse from
+ *
+ * Parses partition type from @str according to the label. Thefunction returns
+ * a pointer to static table of the partition types, or newly allocated
+ * partition type for unknown types (see fdisk_parttype_is_unknown(). It's
+ * safe to call fdisk_unref_parttype() for all results.
+ *
+ * Returns: pointer to type or NULL on error.
+ */
+struct fdisk_parttype *fdisk_label_parse_parttype(
+ const struct fdisk_label *lb,
+ const char *str)
+{
+ struct fdisk_parttype *types, *ret;
+ unsigned int code = 0;
+ char *typestr = NULL, *end = NULL;
+
+ assert(lb);
+
+ if (!lb->nparttypes)
+ return NULL;
+
+ DBG(LABEL, ul_debugobj((void *) lb, "parsing '%s' (%s) partition type",
+ str, lb->name));
+ types = lb->parttypes;
+
+ if (types[0].typestr == NULL && isxdigit(*str)) {
+
+ errno = 0;
+ code = strtol(str, &end, 16);
+
+ if (errno || *end != '\0') {
+ DBG(LABEL, ul_debugobj((void *) lb, "parsing failed: %m"));
+ return NULL;
+ }
+ ret = fdisk_label_get_parttype_from_code(lb, code);
+ if (ret)
+ goto done;
+ } else {
+ int i;
+
+ /* maybe specified by type string (e.g. UUID) */
+ ret = fdisk_label_get_parttype_from_string(lb, str);
+ if (ret)
+ goto done;
+
+ /* maybe specified by order number */
+ errno = 0;
+ i = strtol(str, &end, 0);
+ if (errno == 0 && *end == '\0' && i > 0
+ && i - 1 < (int) lb->nparttypes) {
+ ret = &types[i - 1];
+ goto done;
+ }
+ }
+
+ ret = fdisk_new_unknown_parttype(code, typestr);
+done:
+ DBG(PARTTYPE, ul_debugobj(ret, "returns parsed '%s' partition type", ret->name));
+ return ret;
+}
+
+/**
+ * fdisk_parttype_get_string:
+ * @t: type
+ *
+ * Returns: partition type string (e.g. GUID for GPT)
+ */
+const char *fdisk_parttype_get_string(const struct fdisk_parttype *t)
+{
+ assert(t);
+ return t->typestr && *t->typestr ? t->typestr : NULL;
+}
+
+/**
+ * fdisk_parttype_get_code:
+ * @t: type
+ *
+ * Returns: partition type code (e.g. for MBR)
+ */
+unsigned int fdisk_parttype_get_code(const struct fdisk_parttype *t)
+{
+ assert(t);
+ return t->code;
+}
+
+/**
+ * fdisk_parttype_get_name:
+ * @t: type
+ *
+ * Returns: partition type human readable name
+ */
+const char *fdisk_parttype_get_name(const struct fdisk_parttype *t)
+{
+ assert(t);
+ return t->name;
+}
+
+/**
+ * fdisk_parttype_is_unknown:
+ * @t: type
+ *
+ * Checks for example result from fdisk_label_parse_parttype().
+ *
+ * Returns: 1 is type is "unknonw" or 0.
+ */
+int fdisk_parttype_is_unknown(const struct fdisk_parttype *t)
+{
+ return t && (t->flags & FDISK_PARTTYPE_UNKNOWN) ? 1 : 0;
+}
diff --git a/libblkid/libfdisk/src/script.c b/libblkid/libfdisk/src/script.c
new file mode 100644
index 000000000..83bda995d
--- /dev/null
+++ b/libblkid/libfdisk/src/script.c
@@ -0,0 +1,1235 @@
+
+#include "fdiskP.h"
+#include "strutils.h"
+
+/**
+ * SECTION: script
+ * @title: Script
+ * @short_description: text based sfdisk compatible description of partition table
+ *
+ * The libfdisk scripts are based on original sfdisk script (dumps). Each
+ * script has two parts: script headers and partition table entries
+ * (partitions).
+ *
+ * For more details about script format see sfdisk man page.
+ */
+
+/* script header (e.g. unit: sectors) */
+struct fdisk_scriptheader {
+ struct list_head headers;
+ char *name;
+ char *data;
+};
+
+/* script control struct */
+struct fdisk_script {
+ struct fdisk_table *table;
+ struct list_head headers;
+ struct fdisk_context *cxt;
+
+ int refcount;
+
+ /* parser's state */
+ size_t nlines;
+ int fmt; /* input format */
+ struct fdisk_label *label;
+};
+
+
+static void fdisk_script_free_header(struct fdisk_script *dp, struct fdisk_scriptheader *fi)
+{
+ if (!fi)
+ return;
+
+ DBG(SCRIPT, ul_debugobj(fi, "free header %s", fi->name));
+ free(fi->name);
+ free(fi->data);
+ list_del(&fi->headers);
+ free(fi);
+}
+
+/**
+ * fdisk_new_script:
+ * @cxt: context
+ *
+ * The script hold fdisk_table and additional information to read/write
+ * script to the file.
+ *
+ * Returns: newly allocated script struct.
+ */
+struct fdisk_script *fdisk_new_script(struct fdisk_context *cxt)
+{
+ struct fdisk_script *dp = NULL;
+
+ dp = calloc(1, sizeof(*dp));
+ if (!dp)
+ return NULL;
+
+ DBG(SCRIPT, ul_debugobj(dp, "alloc"));
+ dp->refcount = 1;
+ dp->cxt = cxt;
+ fdisk_ref_context(cxt);
+
+ dp->table = fdisk_new_table();
+ if (!dp->table) {
+ fdisk_unref_script(dp);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&dp->headers);
+ return dp;
+}
+
+/**
+ * fdisk_new_script_from_file:
+ * @cxt: context
+ * @filename: path to the script file
+ *
+ * Allocates a new script and reads script from @filename.
+ *
+ * Returns: new script instance or NULL in case of error (check errno for more details).
+ */
+struct fdisk_script *fdisk_new_script_from_file(struct fdisk_context *cxt,
+ const char *filename)
+{
+ int rc;
+ FILE *f;
+ struct fdisk_script *dp, *res = NULL;
+
+ assert(cxt);
+ assert(filename);
+
+ DBG(SCRIPT, ul_debug("opening %s", filename));
+ f = fopen(filename, "r");
+ if (!f)
+ return NULL;
+
+ dp = fdisk_new_script(cxt);
+ if (!dp)
+ goto done;
+
+ rc = fdisk_script_read_file(dp, f);
+ if (rc) {
+ errno = -rc;
+ goto done;
+ }
+
+ res = dp;
+done:
+ fclose(f);
+ if (!res)
+ fdisk_unref_script(dp);
+ else
+ errno = 0;
+
+ return res;
+}
+
+/**
+ * fdisk_ref_script:
+ * @dp: script pointer
+ *
+ * Incremparts reference counter.
+ */
+void fdisk_ref_script(struct fdisk_script *dp)
+{
+ if (dp)
+ dp->refcount++;
+}
+
+static void fdisk_reset_script(struct fdisk_script *dp)
+{
+ assert(dp);
+
+ DBG(SCRIPT, ul_debugobj(dp, "reset"));
+ fdisk_unref_table(dp->table);
+ dp->table = NULL;
+
+ while (!list_empty(&dp->headers)) {
+ struct fdisk_scriptheader *fi = list_entry(dp->headers.next,
+ struct fdisk_scriptheader, headers);
+ fdisk_script_free_header(dp, fi);
+ }
+ INIT_LIST_HEAD(&dp->headers);
+}
+
+/**
+ * fdisk_unref_script:
+ * @dp: script pointer
+ *
+ * De-incremparts reference counter, on zero the @dp is automatically
+ * deallocated.
+ */
+void fdisk_unref_script(struct fdisk_script *dp)
+{
+ if (!dp)
+ return;
+
+ dp->refcount--;
+ if (dp->refcount <= 0) {
+ fdisk_reset_script(dp);
+ fdisk_unref_context(dp->cxt);
+ DBG(SCRIPT, ul_debugobj(dp, "free script"));
+ free(dp);
+ }
+}
+
+static struct fdisk_scriptheader *script_get_header(struct fdisk_script *dp,
+ const char *name)
+{
+ struct list_head *p;
+
+ list_for_each(p, &dp->headers) {
+ struct fdisk_scriptheader *fi = list_entry(p, struct fdisk_scriptheader, headers);
+
+ if (strcasecmp(fi->name, name) == 0)
+ return fi;
+ }
+
+ return NULL;
+}
+
+/**
+ * fdisk_script_get_header:
+ * @dp: script instance
+ * @name: header name
+ *
+ * Returns: pointer to header data or NULL.
+ */
+const char *fdisk_script_get_header(struct fdisk_script *dp, const char *name)
+{
+ struct fdisk_scriptheader *fi;
+
+ assert(dp);
+ assert(name);
+
+ fi = script_get_header(dp, name);
+ return fi ? fi->data : NULL;
+}
+
+
+/**
+ * fdisk_script_set_header:
+ * @dp: script instance
+ * @name: header name
+ * @data: header data (or NULL)
+ *
+ * The headers are used as global options (in script) for whole partition
+ * table, always one header per line.
+ *
+ * If no @data specified then the header is removed. If header does not exist
+ * and @data specified then a new header added.
+ *
+ * Note that libfdisk allows to specify arbitrary custom header, the default
+ * build-in headers are "unit" and "label", and some label specific headers
+ * (for example "uuid" and "name" for GPT).
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_script_set_header(struct fdisk_script *dp,
+ const char *name,
+ const char *data)
+{
+ struct fdisk_scriptheader *fi;
+
+ assert(dp);
+ assert(name);
+
+ if (!dp || !name)
+ return -EINVAL;
+
+ fi = script_get_header(dp, name);
+ if (!fi && !data)
+ return 0; /* want to remove header that does not exist, success */
+
+ if (!data) {
+ /* no data, remove the header */
+ fdisk_script_free_header(dp, fi);
+ return 0;
+ }
+
+ if (!fi) {
+ /* new header */
+ fi = calloc(1, sizeof(*fi));
+ if (!fi)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&fi->headers);
+ fi->name = strdup(name);
+ fi->data = strdup(data);
+ if (!fi->data || !fi->name) {
+ fdisk_script_free_header(dp, fi);
+ return -ENOMEM;
+ }
+ list_add_tail(&fi->headers, &dp->headers);
+ } else {
+ /* update existing */
+ char *x = strdup(data);
+
+ if (!x)
+ return -ENOMEM;
+ free(fi->data);
+ fi->data = x;
+ }
+
+ if (strcmp(name, "label") == 0)
+ dp->label = NULL;
+
+ return 0;
+}
+
+/**
+ * fdisk_script_get_table:
+ * @dp: script
+ *
+ * The table (container with partitions) is possible to create by
+ * fdisk_script_read_context() or fdisk_script_read_file(), otherwise
+ * this function returns NULL.
+ *
+ * Returns: NULL or script.
+ */
+struct fdisk_table *fdisk_script_get_table(struct fdisk_script *dp)
+{
+ assert(dp);
+ return dp ? dp->table : NULL;
+}
+
+static struct fdisk_label *script_get_label(struct fdisk_script *dp)
+{
+ assert(dp);
+ assert(dp->cxt);
+
+ if (!dp->label) {
+ dp->label = fdisk_get_label(dp->cxt,
+ fdisk_script_get_header(dp, "label"));
+ DBG(SCRIPT, ul_debugobj(dp, "label '%s'", dp->label ? dp->label->name : ""));
+ }
+ return dp->label;
+}
+
+/**
+ * fdisk_script_get_nlines:
+ * @dp: script
+ *
+ * Returns: number of parsed lines or <0 on error.
+ */
+int fdisk_script_get_nlines(struct fdisk_script *dp)
+{
+ assert(dp);
+ return dp->nlines;
+}
+
+/**
+ * fdisk_script_read_context:
+ * @dp: script
+ * @cxt: context
+ *
+ * Reads data from the @cxt context (on disk partition table) into the script.
+ * If the context is no specified than defaults to context used for fdisk_new_script().
+ *
+ * Return: 0 on success, <0 on error.
+ */
+int fdisk_script_read_context(struct fdisk_script *dp, struct fdisk_context *cxt)
+{
+ struct fdisk_label *lb;
+ int rc;
+ char *p = NULL;
+
+ assert(dp);
+
+ if (!cxt)
+ cxt = dp->cxt;
+
+ if (!dp || !cxt)
+ return -EINVAL;
+
+ DBG(SCRIPT, ul_debugobj(dp, "reading context into script"));
+ fdisk_reset_script(dp);
+
+ lb = fdisk_get_label(cxt, NULL);
+ if (!lb)
+ return -EINVAL;
+
+ /* allocate and fill new table */
+ rc = fdisk_get_partitions(cxt, &dp->table);
+ if (rc)
+ return rc;
+
+ /* generate headers */
+ rc = fdisk_script_set_header(dp, "label", fdisk_label_get_name(lb));
+
+ if (!rc && fdisk_get_disklabel_id(cxt, &p) == 0 && p) {
+ rc = fdisk_script_set_header(dp, "label-id", p);
+ free(p);
+ }
+ if (!rc && cxt->dev_path)
+ rc = fdisk_script_set_header(dp, "device", cxt->dev_path);
+ if (!rc)
+ rc = fdisk_script_set_header(dp, "unit", "sectors");
+
+ /* TODO: label specific headers (e.g. uuid for GPT) */
+
+ DBG(SCRIPT, ul_debugobj(dp, "read context done [rc=%d]", rc));
+ return rc;
+}
+
+/**
+ * fdisk_script_write_file:
+ * @dp: script
+ * @f: output file
+ *
+ * Writes script @dp to the ile @f.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_script_write_file(struct fdisk_script *dp, FILE *f)
+{
+ struct list_head *h;
+ struct fdisk_partition *pa;
+ struct fdisk_iter itr;
+ const char *devname = NULL;
+
+ assert(dp);
+ assert(f);
+
+ DBG(SCRIPT, ul_debugobj(dp, "writing script to file"));
+
+ /* script headers */
+ list_for_each(h, &dp->headers) {
+ struct fdisk_scriptheader *fi = list_entry(h, struct fdisk_scriptheader, headers);
+ fprintf(f, "%s: %s\n", fi->name, fi->data);
+ if (strcmp(fi->name, "device") == 0)
+ devname = fi->data;
+ }
+
+ if (!dp->table) {
+ DBG(SCRIPT, ul_debugobj(dp, "script table empty"));
+ return 0;
+ }
+
+ DBG(SCRIPT, ul_debugobj(dp, "%zu entries", fdisk_table_get_nents(dp->table)));
+
+ fputc('\n', f);
+
+ fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+ while (fdisk_table_next_partition(dp->table, &itr, &pa) == 0) {
+ char *p = NULL;
+
+ if (devname)
+ p = fdisk_partname(devname, pa->partno + 1);
+ if (p) {
+ DBG(SCRIPT, ul_debugobj(dp, "write %s entry", p));
+ fprintf(f, "%s :", p);
+ } else
+ fprintf(f, "%zu :", pa->partno + 1);
+
+ if (fdisk_partition_has_start(pa))
+ fprintf(f, " start=%12ju", pa->start);
+ if (fdisk_partition_has_size(pa))
+ fprintf(f, ", size=%12ju", pa->size);
+
+ if (pa->type && fdisk_parttype_get_string(pa->type))
+ fprintf(f, ", type=%s", fdisk_parttype_get_string(pa->type));
+ else if (pa->type)
+ fprintf(f, ", type=%x", fdisk_parttype_get_code(pa->type));
+
+ if (pa->uuid)
+ fprintf(f, ", uuid=%s", pa->uuid);
+ if (pa->name && *pa->name)
+ fprintf(f, ", name=\"%s\"", pa->name);
+
+ /* for MBR attr=80 means bootable */
+ if (pa->attrs) {
+ struct fdisk_label *lb = script_get_label(dp);
+
+ if (!lb || fdisk_label_get_type(lb) != FDISK_DISKLABEL_DOS)
+ fprintf(f, ", attrs=\"%s\"", pa->attrs);
+ }
+ if (fdisk_partition_is_bootable(pa))
+ fprintf(f, ", bootable");
+ fputc('\n', f);
+ }
+
+ DBG(SCRIPT, ul_debugobj(dp, "write script done"));
+ return 0;
+}
+
+static inline int is_header_line(const char *s)
+{
+ const char *p = strchr(s, ':');
+
+ if (!p || p == s || !*(p + 1) || strchr(s, '='))
+ return 0;
+
+ return 1;
+}
+
+/* parses "<name>: value", note modifies @s*/
+static int parse_header_line(struct fdisk_script *dp, char *s)
+{
+ int rc = -EINVAL;
+ char *name, *value;
+
+ DBG(SCRIPT, ul_debugobj(dp, " parse header '%s'", s));
+
+ if (!s || !*s)
+ return -EINVAL;
+
+ name = s;
+ value = strchr(s, ':');
+ if (!value)
+ goto done;
+ *value = '\0';
+ value++;
+
+ ltrim_whitespace((unsigned char *) name);
+ rtrim_whitespace((unsigned char *) name);
+ ltrim_whitespace((unsigned char *) value);
+ rtrim_whitespace((unsigned char *) value);
+
+ if (strcmp(name, "label") == 0) {
+ if (dp->cxt && !fdisk_get_label(dp->cxt, value))
+ goto done; /* unknown label name */
+ } else if (strcmp(name, "unit") == 0) {
+ if (strcmp(value, "sectors") != 0)
+ goto done; /* only "sectors" supported */
+ } else if (strcmp(name, "label-id") == 0
+ || strcmp(name, "device") == 0) {
+ ; /* whatever is posssible */
+ } else
+ goto done; /* unknown header */
+
+ if (*name && *value)
+ rc = fdisk_script_set_header(dp, name, value);
+done:
+ if (rc)
+ DBG(SCRIPT, ul_debugobj(dp, "header parse error: "
+ "[rc=%d, name='%s', value='%s']",
+ rc, name, value));
+ return rc;
+
+}
+
+/* returns zero terminated string with next token and @str is updated */
+static char *next_token(char **str)
+{
+ char *tk_begin = NULL,
+ *tk_end = NULL,
+ *end = NULL,
+ *p;
+ int open_quote = 0;
+
+ for (p = *str; p && *p; p++) {
+ if (!tk_begin) {
+ if (isblank(*p))
+ continue;
+ tk_begin = *p == '"' ? p + 1 : p;
+ }
+ if (*p == '"')
+ open_quote ^= 1;
+ if (open_quote)
+ continue;
+ if (isblank(*p) || *p == ',' || *p == ';' || *p == '"' )
+ tk_end = p;
+ else if (*(p + 1) == '\0')
+ tk_end = p + 1;
+ if (tk_begin && tk_end)
+ break;
+ }
+
+ if (!tk_end)
+ return NULL;
+ end = isblank(*tk_end) ? (char *) skip_blank(tk_end) : tk_end;
+ if (*end == ',' || *end == ';')
+ end++;
+
+ *tk_end = '\0';
+ *str = end;
+ return tk_begin;
+}
+
+static int next_number(char **s, uint64_t *num, int *power)
+{
+ char *tk;
+ int rc = -EINVAL;
+
+ assert(num);
+ assert(s);
+
+ tk = next_token(s);
+ if (tk)
+ rc = parse_size(tk, (uintmax_t *) num, power);
+ return rc;
+}
+
+static int next_string(char **s, char **str)
+{
+ char *tk;
+ int rc = -EINVAL;
+
+ assert(s);
+ assert(str);
+
+ tk = next_token(s);
+ if (tk) {
+ *str = strdup(tk);
+ rc = !*str ? -ENOMEM : 0;
+ }
+ return rc;
+}
+
+static int partno_from_devname(char *s)
+{
+ int pno;
+ size_t sz;
+ char *end, *p;
+
+ sz = rtrim_whitespace((unsigned char *)s);
+ p = s + sz - 1;
+
+ while (p > s && isdigit(*(p - 1)))
+ p--;
+
+ errno = 0;
+ pno = strtol(p, &end, 10);
+ if (errno || !end || p == end)
+ return -1;
+ return pno - 1;
+}
+
+/* dump format
+ * <device>: start=<num>, size=<num>, type=<string>, ...
+ */
+static int parse_script_line(struct fdisk_script *dp, char *s)
+{
+ char *p, *x;
+ struct fdisk_partition *pa;
+ int rc = 0;
+ uint64_t num;
+ int pno;
+
+ assert(dp);
+ assert(s);
+
+ DBG(SCRIPT, ul_debugobj(dp, " parse script line: '%s'", s));
+
+ pa = fdisk_new_partition();
+ if (!pa)
+ return -ENOMEM;
+
+ fdisk_partition_start_follow_default(pa, 1);
+ fdisk_partition_end_follow_default(pa, 1);
+ fdisk_partition_partno_follow_default(pa, 1);
+
+ /* set partno */
+ p = strchr(s, ':');
+ x = strchr(s, '=');
+ if (p && (!x || p < x)) {
+ *p = '\0';
+ p++;
+
+ pno = partno_from_devname(s);
+ if (pno >= 0) {
+ fdisk_partition_partno_follow_default(pa, 0);
+ fdisk_partition_set_partno(pa, pno);
+ }
+ } else
+ p = s;
+
+ while (rc == 0 && p && *p) {
+
+ DBG(SCRIPT, ul_debugobj(dp, " parsing '%s'", p));
+ p = (char *) skip_blank(p);
+
+ if (!strncasecmp(p, "start=", 6)) {
+ p += 6;
+ rc = next_number(&p, &num, NULL);
+ if (!rc) {
+ fdisk_partition_set_start(pa, num);
+ fdisk_partition_start_follow_default(pa, 0);
+ }
+ } else if (!strncasecmp(p, "size=", 5)) {
+ int pow = 0;
+
+ p += 5;
+ rc = next_number(&p, &num, &pow);
+ if (!rc) {
+ if (pow) /* specified as <num><suffix> */
+ num /= dp->cxt->sector_size;
+ else /* specified as number of sectors */
+ fdisk_partition_size_explicit(pa, 1);
+ fdisk_partition_set_size(pa, num);
+ fdisk_partition_end_follow_default(pa, 0);
+ }
+
+ } else if (!strncasecmp(p, "bootable", 8)) {
+ char *tk = next_token(&p);
+ if (strcmp(tk, "bootable") == 0)
+ pa->boot = 1;
+ else
+ rc = -EINVAL;
+
+ } else if (!strncasecmp(p, "attrs=", 6)) {
+ p += 6;
+ rc = next_string(&p, &pa->attrs);
+
+ } else if (!strncasecmp(p, "uuid=", 5)) {
+ p += 5;
+ rc = next_string(&p, &pa->uuid);
+
+ } else if (!strncasecmp(p, "name=", 5)) {
+ p += 5;
+ rc = next_string(&p, &pa->name);
+
+ } else if (!strncasecmp(p, "type=", 5) ||
+
+ !strncasecmp(p, "Id=", 3)) { /* backward compatiility */
+ char *type;
+
+ p += (*p == 'I' ? 3 : 5); /* "Id=" or "type=" */
+
+ rc = next_string(&p, &type);
+ if (rc)
+ break;
+ pa->type = fdisk_label_parse_parttype(
+ script_get_label(dp), type);
+ free(type);
+
+ if (!pa->type || fdisk_parttype_is_unknown(pa->type)) {
+ rc = -EINVAL;
+ fdisk_unref_parttype(pa->type);
+ pa->type = NULL;
+ break;
+ }
+
+ } else {
+ DBG(SCRIPT, ul_debugobj(dp, "script parse error: unknown field '%s'", p));
+ rc = -EINVAL;
+ break;
+ }
+ }
+
+ if (!rc)
+ rc = fdisk_table_add_partition(dp->table, pa);
+ if (rc)
+ DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
+
+ fdisk_unref_partition(pa);
+ return rc;
+}
+
+/* original sfdisk supports partition types shortcuts like 'L' = Linux native
+ */
+static struct fdisk_parttype *translate_type_shortcuts(struct fdisk_script *dp, char *str)
+{
+ struct fdisk_label *lb;
+ const char *type = NULL;
+
+ if (strlen(str) != 1)
+ return NULL;
+
+ lb = script_get_label(dp);
+ if (!lb)
+ return NULL;
+
+ if (lb->id == FDISK_DISKLABEL_DOS) {
+ switch (*str) {
+ case 'L': /* Linux */
+ type = "83";
+ break;
+ case 'S': /* Swap */
+ type = "82";
+ break;
+ case 'E': /* Dos extended */
+ type = "05";
+ break;
+ case 'X': /* Linux extended */
+ type = "85";
+ break;
+ }
+ } else if (lb->id == FDISK_DISKLABEL_GPT) {
+ switch (*str) {
+ case 'L': /* Linux */
+ type = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
+ break;
+ case 'S': /* Swap */
+ type = "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F";
+ break;
+ case 'H': /* Home */
+ type = "933AC7E1-2EB4-4F13-B844-0E14E2AEF915";
+ break;
+ }
+ }
+
+ return type ? fdisk_label_parse_parttype(lb, type) : NULL;
+}
+
+/* simple format:
+ * <start>, <size>, <type>, <bootable>, ...
+ */
+static int parse_commas_line(struct fdisk_script *dp, char *s)
+{
+ int rc = 0;
+ char *p = s, *str;
+ struct fdisk_partition *pa;
+ enum { ITEM_START, ITEM_SIZE, ITEM_TYPE, ITEM_BOOTABLE };
+ int item = -1;
+
+ assert(dp);
+ assert(s);
+
+ pa = fdisk_new_partition();
+ if (!pa)
+ return -ENOMEM;
+
+ fdisk_partition_start_follow_default(pa, 1);
+ fdisk_partition_end_follow_default(pa, 1);
+ fdisk_partition_partno_follow_default(pa, 1);
+
+ while (rc == 0 && p && *p) {
+ uint64_t num;
+ char *begin;
+
+ p = (char *) skip_blank(p);
+ item++;
+
+ DBG(SCRIPT, ul_debugobj(dp, " parsing item %d ('%s')", item, p));
+ begin = p;
+
+ switch (item) {
+ case ITEM_START:
+ if (*p == ',' || *p == ';')
+ fdisk_partition_start_follow_default(pa, 1);
+ else {
+ rc = next_number(&p, &num, NULL);
+ if (!rc)
+ fdisk_partition_set_start(pa, num);
+ fdisk_partition_start_follow_default(pa, 0);
+ }
+ break;
+ case ITEM_SIZE:
+ if (*p == ',' || *p == ';' || *p == '+')
+ fdisk_partition_end_follow_default(pa, 1);
+ else {
+ int pow = 0;
+ rc = next_number(&p, &num, &pow);
+ if (!rc) {
+ if (pow) /* specified as <size><suffix> */
+ num /= dp->cxt->sector_size;
+ else /* specified as number of sectors */
+ fdisk_partition_size_explicit(pa, 1);
+ fdisk_partition_set_size(pa, num);
+ }
+ fdisk_partition_end_follow_default(pa, 0);
+ }
+ break;
+ case ITEM_TYPE:
+ if (*p == ',' || *p == ';')
+ break; /* use default type */
+
+ rc = next_string(&p, &str);
+ if (rc)
+ break;
+
+ pa->type = translate_type_shortcuts(dp, str);
+ if (!pa->type)
+ pa->type = fdisk_label_parse_parttype(
+ script_get_label(dp), str);
+ free(str);
+
+ if (!pa->type || fdisk_parttype_is_unknown(pa->type)) {
+ rc = -EINVAL;
+ fdisk_unref_parttype(pa->type);
+ pa->type = NULL;
+ break;
+ }
+ break;
+ case ITEM_BOOTABLE:
+ if (*p == ',' || *p == ';')
+ break;
+ else {
+ char *tk = next_token(&p);
+ if (tk && *tk == '*' && *(tk + 1) == '\0')
+ pa->boot = 1;
+ else if (tk && *tk == '-' && *(tk + 1) == '\0')
+ pa->boot = 0;
+ else
+ rc = -EINVAL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (begin == p)
+ p++;
+ }
+
+ if (!rc)
+ rc = fdisk_table_add_partition(dp->table, pa);
+ if (rc)
+ DBG(SCRIPT, ul_debugobj(dp, "script parse error: [rc=%d]", rc));
+
+ fdisk_unref_partition(pa);
+ return rc;
+}
+
+/* modifies @s ! */
+int fdisk_script_read_buffer(struct fdisk_script *dp, char *s)
+{
+ int rc = 0;
+
+ assert(dp);
+ assert(s);
+
+ DBG(SCRIPT, ul_debugobj(dp, " parsing buffer"));
+
+ s = (char *) skip_blank(s);
+ if (!s || !*s)
+ return 0; /* nothing baby, ignore */
+
+ if (!dp->table) {
+ dp->table = fdisk_new_table();
+ if (!dp->table)
+ return -ENOMEM;
+ }
+
+ /* parse header lines only if no partition specified yet */
+ if (fdisk_table_is_empty(dp->table) && is_header_line(s))
+ rc = parse_header_line(dp, s);
+
+ /* parse script format */
+ else if (strchr(s, '='))
+ rc = parse_script_line(dp, s);
+
+ /* parse simple <value>, ... format */
+ else
+ rc = parse_commas_line(dp, s);
+
+ if (rc)
+ DBG(SCRIPT, ul_debugobj(dp, "%zu: parse error [rc=%d]",
+ dp->nlines, rc));
+ return rc;
+}
+
+/**
+ * fdisk_script_read_line:
+ * @dp: script
+ * @f: file
+ * @buf: buffer to store one line of the file
+ * @bufsz: buffer size
+ *
+ * Reads next line into dump.
+ *
+ * Returns: 0 on success, <0 on error, 1 when nothing to read.
+ */
+int fdisk_script_read_line(struct fdisk_script *dp, FILE *f, char *buf, size_t bufsz)
+{
+ char *s;
+
+ assert(dp);
+ assert(f);
+
+ DBG(SCRIPT, ul_debugobj(dp, " parsing line %zu", dp->nlines));
+
+ /* read the next non-blank non-comment line */
+ do {
+ if (fgets(buf, bufsz, f) == NULL)
+ return 1;
+ dp->nlines++;
+ s = strchr(buf, '\n');
+ if (!s) {
+ /* Missing final newline? Otherwise an extremely */
+ /* long line - assume file was corrupted */
+ if (feof(f)) {
+ DBG(SCRIPT, ul_debugobj(dp, "no final newline"));
+ s = strchr(buf, '\0');
+ } else {
+ DBG(SCRIPT, ul_debugobj(dp,
+ "%zu: missing newline at line", dp->nlines));
+ return -EINVAL;
+ }
+ }
+
+ *s = '\0';
+ if (--s >= buf && *s == '\r')
+ *s = '\0';
+ s = (char *) skip_blank(buf);
+ } while (*s == '\0' || *s == '#');
+
+ return fdisk_script_read_buffer(dp, s);
+}
+
+
+/**
+ * fdisk_script_read_file:
+ * @dp: script
+ * @f: input file
+ *
+ * Reads file @f into script @dp.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_script_read_file(struct fdisk_script *dp, FILE *f)
+{
+ char buf[BUFSIZ];
+ int rc;
+
+ assert(dp);
+ assert(f);
+
+ DBG(SCRIPT, ul_debugobj(dp, "parsing file"));
+
+ while (!feof(f)) {
+ rc = fdisk_script_read_line(dp, f, buf, sizeof(buf));
+ if (rc)
+ break;
+ }
+
+ if (rc == 1)
+ rc = 0; /* end of file */
+
+ DBG(SCRIPT, ul_debugobj(dp, "parsing file done [rc=%d]", rc));
+ return rc;
+}
+
+/**
+ * fdisk_set_script:
+ * @cxt: context
+ * @dp: script (or NULL to remove previous reference)
+ *
+ * Sets reference to the @dp script. The script headers might be used by label
+ * drivers to overwrite built-in defaults (for example disk label Id) and label
+ * driver might optimize the default semantic to be more usable for scripts
+ * (for example to not ask for primary/logical/extended partition type).
+ *
+ * Note that script also contains reference to the fdisk context (see
+ * fdisk_new_script()). This context may be completely independent on
+ * context used for fdisk_set_script().
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int fdisk_set_script(struct fdisk_context *cxt, struct fdisk_script *dp)
+{
+ assert(cxt);
+
+ /* unref old */
+ if (cxt->script)
+ fdisk_unref_script(cxt->script);
+
+ /* ref new */
+ cxt->script = dp;
+ if (cxt->script) {
+ DBG(CXT, ul_debugobj(cxt, "setting reference to script %p", cxt->script));
+ fdisk_ref_script(cxt->script);
+ }
+
+ return 0;
+}
+
+/**
+ * fdisk_get_script:
+ * @cxt: context
+ *
+ * Returns: the current script or NULL.
+ */
+struct fdisk_script *fdisk_get_script(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ return cxt->script;
+}
+
+/**
+ * fdisk_apply_script_headers:
+ * @cxt: context
+ * @dp: script
+ *
+ * Associte context @cxt with script @dp and creates a new empty disklabel.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_apply_script_headers(struct fdisk_context *cxt, struct fdisk_script *dp)
+{
+ const char *name;
+
+ assert(cxt);
+ assert(dp);
+
+ DBG(SCRIPT, ul_debugobj(dp, "applying script headers"));
+ fdisk_set_script(cxt, dp);
+
+ /* create empty label */
+ name = fdisk_script_get_header(dp, "label");
+ if (!name)
+ return -EINVAL;
+
+ return fdisk_create_disklabel(cxt, name);
+}
+
+/**
+ * fdisk_apply_script:
+ * @cxt: context
+ * @dp: script
+ *
+ * This function creates a new disklabel and partition within context @cxt. You
+ * have to call fdisk_write_disklabel() to apply changes to the device.
+ *
+ * Returns: 0 on error, <0 on error.
+ */
+int fdisk_apply_script(struct fdisk_context *cxt, struct fdisk_script *dp)
+{
+ int rc;
+ struct fdisk_script *old;
+
+ assert(dp);
+ assert(cxt);
+
+ DBG(CXT, ul_debugobj(cxt, "applying script %p", dp));
+
+ old = fdisk_get_script(cxt);
+
+ /* create empty disk label */
+ rc = fdisk_apply_script_headers(cxt, dp);
+
+ /* create partitions */
+ if (!rc && dp->table)
+ rc = fdisk_apply_table(cxt, dp->table);
+
+ fdisk_set_script(cxt, old);
+ DBG(CXT, ul_debugobj(cxt, "script done [rc=%d]", rc));
+ return rc;
+}
+
+#ifdef TEST_PROGRAM
+int test_dump(struct fdisk_test *ts, int argc, char *argv[])
+{
+ char *devname = argv[1];
+ struct fdisk_context *cxt;
+ struct fdisk_script *dp;
+
+ cxt = fdisk_new_context();
+ fdisk_assign_device(cxt, devname, 1);
+
+ dp = fdisk_new_script(cxt);
+ fdisk_script_read_context(dp, NULL);
+
+ fdisk_script_write_file(dp, stdout);
+ fdisk_unref_script(dp);
+ fdisk_unref_context(cxt);
+
+ return 0;
+}
+
+int test_read(struct fdisk_test *ts, int argc, char *argv[])
+{
+ char *filename = argv[1];
+ struct fdisk_script *dp;
+ struct fdisk_context *cxt;
+ FILE *f;
+
+ if (!(f = fopen(filename, "r")))
+ err(EXIT_FAILURE, "%s: cannot open", filename);
+
+ cxt = fdisk_new_context();
+ dp = fdisk_new_script(cxt);
+
+ fdisk_script_read_file(dp, f);
+ fclose(f);
+
+ fdisk_script_write_file(dp, stdout);
+ fdisk_unref_script(dp);
+ fdisk_unref_context(cxt);
+
+ return 0;
+}
+
+int test_stdin(struct fdisk_test *ts, int argc, char *argv[])
+{
+ char buf[BUFSIZ];
+ struct fdisk_script *dp;
+ struct fdisk_context *cxt;
+ int rc = 0;
+
+ cxt = fdisk_new_context();
+ dp = fdisk_new_script(cxt);
+ fdisk_script_set_header(dp, "label", "dos");
+
+ printf("<start>, <size>, <type>, <bootable: *|->\n");
+ do {
+ struct fdisk_partition *pa;
+ size_t n = fdisk_table_get_nents(dp->table);
+
+ printf(" #%zu :\n", n + 1);
+ rc = fdisk_script_read_line(dp, stdin, buf, sizeof(buf));
+
+ if (rc == 0) {
+ pa = fdisk_table_get_partition(dp->table, n);
+ printf(" #%zu %12ju %12ju\n", n + 1,
+ fdisk_partition_get_start(pa),
+ fdisk_partition_get_size(pa));
+ }
+ } while (rc == 0);
+
+ if (!rc)
+ fdisk_script_write_file(dp, stdout);
+ fdisk_unref_script(dp);
+ fdisk_unref_context(cxt);
+
+ return rc;
+}
+
+int test_apply(struct fdisk_test *ts, int argc, char *argv[])
+{
+ char *devname = argv[1], *scriptname = argv[2];
+ struct fdisk_context *cxt;
+ struct fdisk_script *dp = NULL;
+ struct fdisk_table *tb = NULL;
+ struct fdisk_iter *itr = NULL;
+ struct fdisk_partition *pa = NULL;
+ int rc;
+
+ cxt = fdisk_new_context();
+ fdisk_assign_device(cxt, devname, 0);
+
+ dp = fdisk_new_script_from_file(cxt, scriptname);
+ if (!dp)
+ return -errno;
+
+ rc = fdisk_apply_script(cxt, dp);
+ if (rc)
+ goto done;
+ fdisk_unref_script(dp);
+
+ /* list result */
+ fdisk_list_disklabel(cxt);
+ fdisk_get_partitions(cxt, &tb);
+
+ itr = fdisk_new_iter(FDISK_ITER_FORWARD);
+ while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
+ printf(" #%zu %12ju %12ju\n", fdisk_partition_get_partno(pa),
+ fdisk_partition_get_start(pa),
+ fdisk_partition_get_size(pa));
+ }
+
+done:
+ fdisk_free_iter(itr);
+ fdisk_unref_table(tb);
+
+ /*fdisk_write_disklabel(cxt);*/
+ fdisk_unref_context(cxt);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct fdisk_test tss[] = {
+ { "--dump", test_dump, "<device> dump PT as script" },
+ { "--read", test_read, "<file> read PT script from file" },
+ { "--apply", test_apply, "<device> <file> try apply script from file to device" },
+ { "--stdin", test_stdin, " read input like sfdisk" },
+ { NULL }
+ };
+
+ return fdisk_run_test(tss, argc, argv);
+}
+
+#endif
diff --git a/libblkid/libfdisk/src/sgi.c b/libblkid/libfdisk/src/sgi.c
new file mode 100644
index 000000000..cd4cedff0
--- /dev/null
+++ b/libblkid/libfdisk/src/sgi.c
@@ -0,0 +1,1185 @@
+/*
+ *
+ * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
+ * 2013 Karel Zak <kzak@redhat.com>
+ *
+ * This is a re-written version for libfdisk, the original was fdisksgilabel.c
+ * from util-linux fdisk, by:
+ *
+ * Andreas Neuper, Sep 1998,
+ * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, Mar 1999,
+ * Phillip Kesling <pkesling@sgi.com>, Mar 2003.
+ */
+
+#include "c.h"
+#include "nls.h"
+#include "all-io.h"
+
+#include "blkdev.h"
+
+#include "bitops.h"
+#include "pt-sgi.h"
+#include "pt-mbr.h"
+#include "fdiskP.h"
+
+/**
+ * SECTION: sgi
+ * @title: SGI
+ * @short_description: disk label specific functions
+ *
+ */
+
+/*
+ * in-memory fdisk SGI stuff
+ */
+struct fdisk_sgi_label {
+ struct fdisk_label head; /* generic fdisk part */
+ struct sgi_disklabel *header; /* on-disk data (pointer to cxt->firstsector) */
+
+ struct sgi_freeblocks {
+ unsigned int first;
+ unsigned int last;
+ } freelist[SGI_MAXPARTITIONS + 1];
+};
+
+static struct fdisk_parttype sgi_parttypes[] =
+{
+ {SGI_TYPE_VOLHDR, N_("SGI volhdr")},
+ {SGI_TYPE_TRKREPL, N_("SGI trkrepl")},
+ {SGI_TYPE_SECREPL, N_("SGI secrepl")},
+ {SGI_TYPE_SWAP, N_("SGI raw")},
+ {SGI_TYPE_BSD, N_("SGI bsd")},
+ {SGI_TYPE_SYSV, N_("SGI sysv")},
+ {SGI_TYPE_ENTIRE_DISK, N_("SGI volume")},
+ {SGI_TYPE_EFS, N_("SGI efs")},
+ {SGI_TYPE_LVOL, N_("SGI lvol")},
+ {SGI_TYPE_RLVOL, N_("SGI rlvol")},
+ {SGI_TYPE_XFS, N_("SGI xfs")},
+ {SGI_TYPE_XFSLOG, N_("SGI xfslog")},
+ {SGI_TYPE_XLV, N_("SGI xlv")},
+ {SGI_TYPE_XVM, N_("SGI xvm")},
+ {MBR_LINUX_SWAP_PARTITION, N_("Linux swap")},
+ {MBR_LINUX_DATA_PARTITION, N_("Linux native")},
+ {MBR_LINUX_LVM_PARTITION, N_("Linux LVM")},
+ {MBR_LINUX_RAID_PARTITION, N_("Linux RAID")},
+ {0, NULL }
+};
+
+static unsigned int sgi_get_start_sector(struct fdisk_context *cxt, int i );
+static unsigned int sgi_get_num_sectors(struct fdisk_context *cxt, int i );
+static int sgi_get_bootpartition(struct fdisk_context *cxt);
+static int sgi_get_swappartition(struct fdisk_context *cxt);
+
+/* Returns a pointer buffer with on-disk data. */
+static inline struct sgi_disklabel *self_disklabel(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SGI));
+
+ return ((struct fdisk_sgi_label *) cxt->label)->header;
+}
+
+/* Returns in-memory fdisk data. */
+static inline struct fdisk_sgi_label *self_label(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SGI));
+
+ return (struct fdisk_sgi_label *) cxt->label;
+}
+
+/*
+ * Information within second on-disk block
+ */
+#define SGI_INFO_MAGIC 0x00072959
+
+struct sgi_info {
+ unsigned int magic; /* looks like a magic number */
+ unsigned int a2;
+ unsigned int a3;
+ unsigned int a4;
+ unsigned int b1;
+ unsigned short b2;
+ unsigned short b3;
+ unsigned int c[16];
+ unsigned short d[3];
+ unsigned char scsi_string[50];
+ unsigned char serial[137];
+ unsigned short check1816;
+ unsigned char installer[225];
+};
+
+static struct sgi_info *sgi_new_info(void)
+{
+ struct sgi_info *info = calloc(1, sizeof(struct sgi_info));
+
+ if (!info)
+ return NULL;
+
+ info->magic = cpu_to_be32(SGI_INFO_MAGIC);
+ info->b1 = cpu_to_be32(-1);
+ info->b2 = cpu_to_be16(-1);
+ info->b3 = cpu_to_be16(1);
+
+ /* You may want to replace this string !!!!!!! */
+ strcpy((char *) info->scsi_string, "IBM OEM 0662S12 3 30");
+ strcpy((char *) info->serial, "0000");
+ info->check1816 = cpu_to_be16(18 * 256 + 16);
+ strcpy((char *) info->installer, "Sfx version 5.3, Oct 18, 1994");
+
+ return info;
+}
+
+static void sgi_free_info(struct sgi_info *info)
+{
+ free(info);
+}
+
+/**
+ * fdisk_sgi_create_info:
+ * @cxt: context
+ *
+ * This function add hint about SGI label (e.g. set "sgilabel" as volume name)
+ * to the first SGI volume. This is probably old SGI convention without any
+ * effect to the device partitioning.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_sgi_create_info(struct fdisk_context *cxt)
+{
+ struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+
+ /* I keep SGI's habit to write the sgilabel to the second block */
+ sgilabel->volume[0].block_num = cpu_to_be32(2);
+ sgilabel->volume[0].num_bytes = cpu_to_be32(sizeof(struct sgi_info));
+ strncpy((char *) sgilabel->volume[0].name, "sgilabel", 8);
+
+ fdisk_info(cxt, _("SGI info created on second sector."));
+ return 0;
+}
+
+
+/*
+ * only dealing with free blocks here
+ */
+static void set_freelist(struct fdisk_context *cxt,
+ size_t i, unsigned int f, unsigned int l)
+{
+ struct fdisk_sgi_label *sgi = self_label(cxt);
+
+ if (i < ARRAY_SIZE(sgi->freelist)) {
+ sgi->freelist[i].first = f;
+ sgi->freelist[i].last = l;
+ }
+}
+
+static void add_to_freelist(struct fdisk_context *cxt,
+ unsigned int f, unsigned int l)
+{
+ struct fdisk_sgi_label *sgi = self_label(cxt);
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(sgi->freelist); i++) {
+ if (sgi->freelist[i].last == 0)
+ break;
+ }
+ set_freelist(cxt, i, f, l);
+}
+
+static void clear_freelist(struct fdisk_context *cxt)
+{
+ struct fdisk_sgi_label *sgi = self_label(cxt);
+
+ memset(sgi->freelist, 0, sizeof(sgi->freelist));
+}
+
+static unsigned int is_in_freelist(struct fdisk_context *cxt, unsigned int b)
+{
+ struct fdisk_sgi_label *sgi = self_label(cxt);
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(sgi->freelist); i++) {
+ if (sgi->freelist[i].first <= b
+ && sgi->freelist[i].last >= b)
+ return sgi->freelist[i].last;
+ }
+
+ return 0;
+}
+
+
+static int sgi_get_nsect(struct fdisk_context *cxt)
+{
+ struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+ return be16_to_cpu(sgilabel->devparam.nsect);
+}
+
+static int sgi_get_ntrks(struct fdisk_context *cxt)
+{
+ struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+ return be16_to_cpu(sgilabel->devparam.ntrks);
+}
+
+static size_t count_used_partitions(struct fdisk_context *cxt)
+{
+ size_t i, ct = 0;
+
+ for (i = 0; i < cxt->label->nparts_max; i++)
+ ct += sgi_get_num_sectors(cxt, i) > 0;
+
+ return ct;
+}
+
+static int sgi_probe_label(struct fdisk_context *cxt)
+{
+ struct fdisk_sgi_label *sgi;
+ struct sgi_disklabel *sgilabel;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SGI));
+ assert(sizeof(struct sgi_disklabel) <= 512);
+
+ /* map first sector to header */
+ sgi = (struct fdisk_sgi_label *) cxt->label;
+ sgi->header = (struct sgi_disklabel *) cxt->firstsector;
+ sgilabel = sgi->header;
+
+ if (be32_to_cpu(sgilabel->magic) != SGI_LABEL_MAGIC) {
+ sgi->header = NULL;
+ return 0;
+ }
+
+ /*
+ * test for correct checksum
+ */
+ if (sgi_pt_checksum(sgilabel) != 0)
+ fdisk_warnx(cxt, _("Detected an SGI disklabel with wrong checksum."));
+
+ clear_freelist(cxt);
+ cxt->label->nparts_max = SGI_MAXPARTITIONS;
+ cxt->label->nparts_cur = count_used_partitions(cxt);
+ return 1;
+}
+
+static int sgi_list_table(struct fdisk_context *cxt)
+{
+ struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+ struct sgi_device_parameter *sgiparam = &sgilabel->devparam;
+ int rc = 0;
+
+ if (fdisk_is_details(cxt))
+ fdisk_info(cxt, _(
+ "Label geometry: %d heads, %llu sectors\n"
+ " %llu cylinders, %d physical cylinders\n"
+ " %d extra sects/cyl, interleave %d:1\n"),
+ cxt->geom.heads, cxt->geom.sectors,
+ cxt->geom.cylinders, be16_to_cpu(sgiparam->pcylcount),
+ (int) sgiparam->sparecyl, be16_to_cpu(sgiparam->ilfact));
+
+ fdisk_info(cxt, _("Bootfile: %s"), sgilabel->boot_file);
+ return rc;
+}
+
+static unsigned int sgi_get_start_sector(struct fdisk_context *cxt, int i)
+{
+ struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+ return be32_to_cpu(sgilabel->partitions[i].first_block);
+}
+
+static unsigned int sgi_get_num_sectors(struct fdisk_context *cxt, int i)
+{
+ struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+ return be32_to_cpu(sgilabel->partitions[i].num_blocks);
+}
+
+static int sgi_get_sysid(struct fdisk_context *cxt, int i)
+{
+ struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+ return be32_to_cpu(sgilabel->partitions[i].type);
+}
+
+static int sgi_get_bootpartition(struct fdisk_context *cxt)
+{
+ struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+ return be16_to_cpu(sgilabel->root_part_num);
+}
+
+static int sgi_get_swappartition(struct fdisk_context *cxt)
+{
+ struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+ return be16_to_cpu(sgilabel->swap_part_num);
+}
+
+static unsigned int sgi_get_lastblock(struct fdisk_context *cxt)
+{
+ return cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders;
+}
+
+static struct fdisk_parttype *sgi_get_parttype(struct fdisk_context *cxt, size_t n)
+{
+ struct fdisk_parttype *t;
+
+ if (n >= cxt->label->nparts_max)
+ return NULL;
+
+ t = fdisk_label_get_parttype_from_code(cxt->label, sgi_get_sysid(cxt, n));
+ return t ? : fdisk_new_unknown_parttype(sgi_get_sysid(cxt, n), NULL);
+}
+
+/* fdisk_get_partition() backend */
+static int sgi_get_partition(struct fdisk_context *cxt, size_t n, struct fdisk_partition *pa)
+{
+ fdisk_sector_t start, len;
+
+ pa->used = sgi_get_num_sectors(cxt, n) > 0;
+ if (!pa->used)
+ return 0;
+
+ start = sgi_get_start_sector(cxt, n);
+ len = sgi_get_num_sectors(cxt, n);
+
+ pa->type = sgi_get_parttype(cxt, n);
+ pa->size = len;
+ pa->start = start;
+
+ if (pa->type && pa->type->code == SGI_TYPE_ENTIRE_DISK)
+ pa->wholedisk = 1;
+
+ pa->attrs = sgi_get_swappartition(cxt) == (int) n ? "swap" :
+ sgi_get_bootpartition(cxt) == (int) n ? "boot" : NULL;
+ if (pa->attrs)
+ pa->attrs = strdup(pa->attrs);
+
+ return 0;
+}
+
+
+static int sgi_check_bootfile(struct fdisk_context *cxt, const char *name)
+{
+ size_t sz;
+ struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+
+ sz = strlen(name);
+
+ if (sz < 3) {
+ /* "/a\n" is minimum */
+ fdisk_warnx(cxt, _("Invalid bootfile! The bootfile must "
+ "be an absolute non-zero pathname, "
+ "e.g. \"/unix\" or \"/unix.save\"."));
+ return -EINVAL;
+
+ } else if (sz > sizeof(sgilabel->boot_file)) {
+ fdisk_warnx(cxt, P_("Name of bootfile is too long: %zu byte maximum.",
+ "Name of bootfile is too long: %zu bytes maximum.",
+ sizeof(sgilabel->boot_file)),
+ sizeof(sgilabel->boot_file));
+ return -EINVAL;
+
+ } else if (*name != '/') {
+ fdisk_warnx(cxt, _("Bootfile must have a fully qualified pathname."));
+ return -EINVAL;
+ }
+
+ if (strncmp(name, (char *) sgilabel->boot_file,
+ sizeof(sgilabel->boot_file))) {
+ fdisk_warnx(cxt, _("Be aware that the bootfile is not checked "
+ "for existence. SGI's default is \"/unix\", "
+ "and for backup \"/unix.save\"."));
+ return 0; /* filename is correct and did change */
+ }
+
+ return 1; /* filename did not change */
+}
+
+/**
+ * fdisk_sgi_set_bootfile:
+ * @cxt: context
+ *
+ * Allows to set SGI boot file. The function uses Ask API for dialog with
+ * user.
+ *
+ * Returns: 0 on success, <0 on error
+ */
+int fdisk_sgi_set_bootfile(struct fdisk_context *cxt)
+{
+ int rc = 0;
+ size_t sz;
+ char *name = NULL;
+ struct sgi_disklabel *sgilabel = self_disklabel(cxt);
+
+ fdisk_info(cxt, _("The current boot file is: %s"), sgilabel->boot_file);
+
+ rc = fdisk_ask_string(cxt, _("Enter of the new boot file"), &name);
+ if (rc == 0)
+ rc = sgi_check_bootfile(cxt, name);
+ if (rc) {
+ if (rc == 1)
+ fdisk_info(cxt, _("Boot file is unchanged."));
+ goto done;
+ }
+
+ memset(sgilabel->boot_file, 0, sizeof(sgilabel->boot_file));
+ sz = strlen(name);
+
+ assert(sz <= sizeof(sgilabel->boot_file)); /* see sgi_check_bootfile() */
+
+ memcpy(sgilabel->boot_file, name, sz);
+
+ fdisk_info(cxt, _("Bootfile has been changed to \"%s\"."), name);
+done:
+ free(name);
+ return rc;
+}
+
+static int sgi_write_disklabel(struct fdisk_context *cxt)
+{
+ struct sgi_disklabel *sgilabel;
+ struct sgi_info *info = NULL;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SGI));
+
+ sgilabel = self_disklabel(cxt);
+ sgilabel->csum = 0;
+ sgilabel->csum = cpu_to_be32(sgi_pt_checksum(sgilabel));
+
+ assert(sgi_pt_checksum(sgilabel) == 0);
+
+ if (lseek(cxt->dev_fd, 0, SEEK_SET) < 0)
+ goto err;
+ if (write_all(cxt->dev_fd, sgilabel, DEFAULT_SECTOR_SIZE))
+ goto err;
+ if (!strncmp((char *) sgilabel->volume[0].name, "sgilabel", 8)) {
+ /*
+ * Keep this habit of first writing the "sgilabel".
+ * I never tested whether it works without. (AN 1998-10-02)
+ */
+ int infostartblock
+ = be32_to_cpu(sgilabel->volume[0].block_num);
+
+ if (lseek(cxt->dev_fd, (off_t) infostartblock *
+ DEFAULT_SECTOR_SIZE, SEEK_SET) < 0)
+ goto err;
+ info = sgi_new_info();
+ if (!info)
+ goto err;
+ if (write_all(cxt->dev_fd, info, sizeof(*info)))
+ goto err;
+ }
+
+ sgi_free_info(info);
+ return 0;
+err:
+ sgi_free_info(info);
+ return -errno;
+}
+
+static int compare_start(struct fdisk_context *cxt,
+ const void *x, const void *y)
+{
+ /*
+ * Sort according to start sectors and prefer the largest partition:
+ * entry zero is the entire-disk entry.
+ */
+ unsigned int i = *(int *) x;
+ unsigned int j = *(int *) y;
+ unsigned int a = sgi_get_start_sector(cxt, i);
+ unsigned int b = sgi_get_start_sector(cxt, j);
+ unsigned int c = sgi_get_num_sectors(cxt, i);
+ unsigned int d = sgi_get_num_sectors(cxt, j);
+
+ if (a == b)
+ return (d > c) ? 1 : (d == c) ? 0 : -1;
+ return (a > b) ? 1 : -1;
+}
+
+static void generic_swap(void *a0, void *b0, int size)
+{
+ char *a = a0, *b = b0;
+
+ for (; size > 0; --size, a++, b++) {
+ char t = *a;
+ *a = *b;
+ *b = t;
+ }
+}
+
+
+/* heap sort, based on Matt Mackall's linux kernel version */
+static void sort(void *base0, size_t num, size_t size, struct fdisk_context *cxt,
+ int (*cmp_func)(struct fdisk_context *, const void *, const void *))
+{
+ /* pre-scale counters for performance */
+ int i = (num/2 - 1) * size;
+ size_t n = num * size, c, r;
+ char *base = base0;
+
+ /* heapify */
+ for ( ; i >= 0; i -= size) {
+ for (r = i; r * 2 + size < n; r = c) {
+ c = r * 2 + size;
+ if (c < n - size &&
+ cmp_func(cxt, base + c, base + c + size) < 0)
+ c += size;
+ if (cmp_func(cxt, base + r, base + c) >= 0)
+ break;
+ generic_swap(base + r, base + c, size);
+ }
+ }
+
+ /* sort */
+ for (i = n - size; i > 0; i -= size) {
+ generic_swap(base, base + i, size);
+ for (r = 0; r * 2 + size < (size_t) i; r = c) {
+ c = r * 2 + size;
+ if (c < i - size &&
+ cmp_func(cxt, base + c, base + c + size) < 0)
+ c += size;
+ if (cmp_func(cxt, base + r, base + c) >= 0)
+ break;
+ generic_swap(base + r, base + c, size);
+ }
+ }
+}
+
+static int verify_disklabel(struct fdisk_context *cxt, int verbose)
+{
+ int Index[SGI_MAXPARTITIONS]; /* list of valid partitions */
+ int sortcount = 0; /* number of used partitions, i.e. non-zero lengths */
+ int entire = 0, i = 0;
+ unsigned int start = 0;
+ long long gap = 0; /* count unused blocks */
+ unsigned int lastblock = sgi_get_lastblock(cxt);
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SGI));
+
+ clear_freelist(cxt);
+ memset(Index, 0, sizeof(Index));
+
+ for (i=0; i < SGI_MAXPARTITIONS; i++) {
+ if (sgi_get_num_sectors(cxt, i) != 0) {
+ Index[sortcount++] = i;
+ if (sgi_get_sysid(cxt, i) == SGI_TYPE_ENTIRE_DISK
+ && entire++ == 1) {
+ if (verbose)
+ fdisk_info(cxt, _("More than one entire "
+ "disk entry present."));
+ }
+ }
+ }
+ if (sortcount == 0) {
+ if (verbose)
+ fdisk_info(cxt, _("No partitions defined."));
+ if (lastblock)
+ add_to_freelist(cxt, 0, lastblock);
+ return (lastblock > 0) ? 1 : (lastblock == 0) ? 0 : -1;
+ }
+
+ sort(Index, sortcount, sizeof(Index[0]), cxt, compare_start);
+
+ if (sgi_get_sysid(cxt, Index[0]) == SGI_TYPE_ENTIRE_DISK) {
+ if (verbose && Index[0] != 10)
+ fdisk_info(cxt, _("IRIX likes it when partition 11 "
+ "covers the entire disk."));
+
+ if (verbose && sgi_get_start_sector(cxt, Index[0]) != 0)
+ fdisk_info(cxt, _("The entire disk partition should "
+ "start at block 0, not at block %d."),
+ sgi_get_start_sector(cxt, Index[0]));
+
+ if (verbose && sgi_get_num_sectors(cxt, Index[0]) != lastblock)
+ DBG(LABEL, ul_debug(
+ "entire disk partition=%ds, but disk=%ds",
+ sgi_get_num_sectors(cxt, Index[0]),
+ lastblock));
+ lastblock = sgi_get_num_sectors(cxt, Index[0]);
+ } else if (verbose) {
+ fdisk_info(cxt, _("Partition 11 should cover the entire disk."));
+ DBG(LABEL, ul_debug("sysid=%d\tpartition=%d",
+ sgi_get_sysid(cxt, Index[0]), Index[0]+1));
+ }
+ for (i=1, start=0; i<sortcount; i++) {
+ int cylsize = sgi_get_nsect(cxt) * sgi_get_ntrks(cxt);
+
+ if (verbose && cylsize
+ && (sgi_get_start_sector(cxt, Index[i]) % cylsize) != 0)
+ DBG(LABEL, ul_debug("partition %d does not start on "
+ "cylinder boundary.", Index[i]+1));
+
+ if (verbose && cylsize
+ && sgi_get_num_sectors(cxt, Index[i]) % cylsize != 0)
+ DBG(LABEL, ul_debug("partition %d does not end on "
+ "cylinder boundary.", Index[i]+1));
+
+ /* We cannot handle several "entire disk" entries. */
+ if (sgi_get_sysid(cxt, Index[i]) == SGI_TYPE_ENTIRE_DISK)
+ continue;
+
+ if (start > sgi_get_start_sector(cxt, Index[i])) {
+ if (verbose)
+ fdisk_info(cxt,
+ P_("Partitions %d and %d overlap by %d sector.",
+ "Partitions %d and %d overlap by %d sectors.",
+ start - sgi_get_start_sector(cxt, Index[i])),
+ Index[i-1]+1, Index[i]+1,
+ start - sgi_get_start_sector(cxt, Index[i]));
+ if (gap > 0) gap = -gap;
+ if (gap == 0) gap = -1;
+ }
+ if (start < sgi_get_start_sector(cxt, Index[i])) {
+ if (verbose)
+ fdisk_info(cxt,
+ P_("Unused gap of %8u sector: sector %8u",
+ "Unused gap of %8u sectors: sectors %8u-%u",
+ sgi_get_start_sector(cxt, Index[i]) - start),
+ sgi_get_start_sector(cxt, Index[i]) - start,
+ start, sgi_get_start_sector(cxt, Index[i])-1);
+ gap += sgi_get_start_sector(cxt, Index[i]) - start;
+ add_to_freelist(cxt, start,
+ sgi_get_start_sector(cxt, Index[i]));
+ }
+ start = sgi_get_start_sector(cxt, Index[i])
+ + sgi_get_num_sectors(cxt, Index[i]);
+ /* Align free space on cylinder boundary. */
+ if (cylsize && start % cylsize)
+ start += cylsize - (start % cylsize);
+
+ DBG(LABEL, ul_debug("%2d:%12d\t%12d\t%12d", Index[i],
+ sgi_get_start_sector(cxt, Index[i]),
+ sgi_get_num_sectors(cxt, Index[i]),
+ sgi_get_sysid(cxt, Index[i])));
+ }
+ if (start < lastblock) {
+ if (verbose)
+ fdisk_info(cxt, P_("Unused gap of %8u sector: sector %8u",
+ "Unused gap of %8u sectors: sectors %8u-%u",
+ lastblock - start),
+ lastblock - start, start, lastblock-1);
+ gap += lastblock - start;
+ add_to_freelist(cxt, start, lastblock);
+ }
+ /*
+ * Done with arithmetics. Go for details now.
+ */
+ if (verbose) {
+ if (sgi_get_bootpartition(cxt) < 0
+ || !sgi_get_num_sectors(cxt, sgi_get_bootpartition(cxt)))
+ fdisk_info(cxt, _("The boot partition does not exist."));
+
+ if (sgi_get_swappartition(cxt) < 0
+ || !sgi_get_num_sectors(cxt, sgi_get_swappartition(cxt)))
+ fdisk_info(cxt, _("The swap partition does not exist."));
+
+ else if (sgi_get_sysid(cxt, sgi_get_swappartition(cxt)) != SGI_TYPE_SWAP
+ && sgi_get_sysid(cxt, sgi_get_swappartition(cxt)) != MBR_LINUX_SWAP_PARTITION)
+ fdisk_info(cxt, _("The swap partition has no swap type."));
+
+ if (sgi_check_bootfile(cxt, "/unix"))
+ fdisk_info(cxt, _("You have chosen an unusual bootfile name."));
+ }
+
+ return (gap > 0) ? 1 : (gap == 0) ? 0 : -1;
+}
+
+static int sgi_verify_disklabel(struct fdisk_context *cxt)
+{
+ return verify_disklabel(cxt, 1);
+}
+
+static int sgi_gaps(struct fdisk_context *cxt)
+{
+ /*
+ * returned value is:
+ * = 0 : disk is properly filled to the rim
+ * < 0 : there is an overlap
+ * > 0 : there is still some vacant space
+ */
+ return verify_disklabel(cxt, 0);
+}
+
+/* Returns partition index of first entry marked as entire disk. */
+static int sgi_entire(struct fdisk_context *cxt)
+{
+ size_t i;
+
+ for (i = 0; i < SGI_MAXPARTITIONS; i++)
+ if (sgi_get_sysid(cxt, i) == SGI_TYPE_ENTIRE_DISK)
+ return i;
+ return -1;
+}
+
+static int set_partition(struct fdisk_context *cxt, size_t i,
+ unsigned int start, unsigned int length, int sys)
+{
+ struct sgi_disklabel *sgilabel;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SGI));
+
+ sgilabel = self_disklabel(cxt);
+ sgilabel->partitions[i].type = cpu_to_be32(sys);
+ sgilabel->partitions[i].num_blocks = cpu_to_be32(length);
+ sgilabel->partitions[i].first_block = cpu_to_be32(start);
+
+ fdisk_label_set_changed(cxt->label, 1);
+
+ if (sgi_gaps(cxt) < 0) /* rebuild freelist */
+ fdisk_warnx(cxt, _("Partition overlap on the disk."));
+ if (length) {
+ struct fdisk_parttype *t =
+ fdisk_label_get_parttype_from_code(cxt->label, sys);
+ fdisk_info_new_partition(cxt, i + 1, start, start + length, t);
+ }
+
+ return 0;
+}
+
+static void sgi_set_entire(struct fdisk_context *cxt)
+{
+ size_t n;
+
+ for (n = 10; n < cxt->label->nparts_max; n++) {
+ if (!sgi_get_num_sectors(cxt, n)) {
+ set_partition(cxt, n, 0, sgi_get_lastblock(cxt), SGI_TYPE_ENTIRE_DISK);
+ break;
+ }
+ }
+}
+
+static void sgi_set_volhdr(struct fdisk_context *cxt)
+{
+ size_t n;
+
+ for (n = 8; n < cxt->label->nparts_max; n++) {
+ if (!sgi_get_num_sectors(cxt, n)) {
+ /* Choose same default volume header size as IRIX fx uses. */
+ if (4096 < sgi_get_lastblock(cxt))
+ set_partition(cxt, n, 0, 4096, SGI_TYPE_VOLHDR);
+ break;
+ }
+ }
+}
+
+static int sgi_delete_partition(struct fdisk_context *cxt, size_t partnum)
+{
+ int rc;
+
+ assert(cxt);
+ assert(cxt->label);
+
+ if (partnum > cxt->label->nparts_max)
+ return -EINVAL;
+
+ rc = set_partition(cxt, partnum, 0, 0, 0);
+
+ cxt->label->nparts_cur = count_used_partitions(cxt);
+
+ return rc;
+}
+
+static int sgi_add_partition(struct fdisk_context *cxt,
+ struct fdisk_partition *pa,
+ size_t *partno)
+{
+ struct fdisk_sgi_label *sgi;
+ char mesg[256];
+ unsigned int first = 0, last = 0;
+ struct fdisk_ask *ask;
+ int sys = pa && pa->type ? pa->type->code : SGI_TYPE_XFS;
+ int rc;
+ size_t n;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SGI));
+
+ rc = fdisk_partition_next_partno(pa, cxt, &n);
+ if (rc)
+ return rc;
+ if (n == 10)
+ sys = SGI_TYPE_ENTIRE_DISK;
+ else if (n == 8)
+ sys = 0;
+
+ sgi = self_label(cxt);
+
+ if (sgi_get_num_sectors(cxt, n)) {
+ fdisk_warnx(cxt, _("Partition %zu is already defined. "
+ "Delete it before re-adding it."), n + 1);
+ return -EINVAL;
+ }
+ if (!cxt->script && sgi_entire(cxt) == -1 && sys != SGI_TYPE_ENTIRE_DISK) {
+ fdisk_info(cxt, _("Attempting to generate entire disk entry automatically."));
+ sgi_set_entire(cxt);
+ sgi_set_volhdr(cxt);
+ }
+ if (sgi_gaps(cxt) == 0 && sys != SGI_TYPE_ENTIRE_DISK) {
+ fdisk_warnx(cxt, _("The entire disk is already covered with partitions."));
+ return -EINVAL;
+ }
+ if (sgi_gaps(cxt) < 0) {
+ fdisk_warnx(cxt, _("You got a partition overlap on the disk. Fix it first!"));
+ return -EINVAL;
+ }
+
+ if (sys == SGI_TYPE_ENTIRE_DISK) {
+ first = 0;
+ last = sgi_get_lastblock(cxt);
+ } else {
+ first = sgi->freelist[0].first;
+ last = sgi->freelist[0].last;
+ }
+
+ /* first sector */
+ if (pa && pa->start_follow_default)
+ ;
+ else if (pa && fdisk_partition_has_start(pa)) {
+ first = pa->start;
+ last = is_in_freelist(cxt, first);
+
+ if (sys != SGI_TYPE_ENTIRE_DISK && !last)
+ return -ERANGE;
+ } else {
+ snprintf(mesg, sizeof(mesg), _("First %s"),
+ fdisk_get_unit(cxt, FDISK_SINGULAR));
+ ask = fdisk_new_ask();
+ if (!ask)
+ return -ENOMEM;
+
+ fdisk_ask_set_query(ask, mesg);
+ fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+
+ fdisk_ask_number_set_low(ask, fdisk_scround(cxt, first)); /* minimal */
+ fdisk_ask_number_set_default(ask, fdisk_scround(cxt, first)); /* default */
+ fdisk_ask_number_set_high(ask, fdisk_scround(cxt, last) - 1); /* maximal */
+
+ rc = fdisk_do_ask(cxt, ask);
+ first = fdisk_ask_number_get_result(ask);
+ fdisk_unref_ask(ask);
+
+ if (rc)
+ return rc;
+ if (fdisk_use_cylinders(cxt))
+ first *= fdisk_get_units_per_sector(cxt);
+ }
+
+ if (first && sys == SGI_TYPE_ENTIRE_DISK)
+ fdisk_info(cxt, _("It is highly recommended that the "
+ "eleventh partition covers the entire "
+ "disk and is of type 'SGI volume'."));
+ if (!last)
+ last = is_in_freelist(cxt, first);
+
+ /* last sector */
+ if (pa && pa->end_follow_default)
+ last -= 1ULL;
+ else if (pa && fdisk_partition_has_size(pa)) {
+ if (first + pa->size - 1ULL > last)
+ return -ERANGE;
+ last = first + pa->size - 1ULL;
+ } else {
+ snprintf(mesg, sizeof(mesg),
+ _("Last %s or +%s or +size{K,M,G,T,P}"),
+ fdisk_get_unit(cxt, FDISK_SINGULAR),
+ fdisk_get_unit(cxt, FDISK_PLURAL));
+
+ ask = fdisk_new_ask();
+ if (!ask)
+ return -ENOMEM;
+
+ fdisk_ask_set_query(ask, mesg);
+ fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+
+ fdisk_ask_number_set_low(ask, fdisk_scround(cxt, first)); /* minimal */
+ fdisk_ask_number_set_default(ask, fdisk_scround(cxt, last) - 1);/* default */
+ fdisk_ask_number_set_high(ask, fdisk_scround(cxt, last) - 1);/* maximal */
+ fdisk_ask_number_set_base(ask, fdisk_scround(cxt, first));
+
+ if (fdisk_use_cylinders(cxt))
+ fdisk_ask_number_set_unit(ask,
+ cxt->sector_size *
+ fdisk_get_units_per_sector(cxt));
+ else
+ fdisk_ask_number_set_unit(ask,cxt->sector_size);
+
+ rc = fdisk_do_ask(cxt, ask);
+ last = fdisk_ask_number_get_result(ask) + 1;
+
+ fdisk_unref_ask(ask);
+ if (rc)
+ return rc;
+ if (fdisk_use_cylinders(cxt))
+ last *= fdisk_get_units_per_sector(cxt);
+ }
+
+ if (sys == SGI_TYPE_ENTIRE_DISK
+ && (first != 0 || last != sgi_get_lastblock(cxt)))
+ fdisk_info(cxt, _("It is highly recommended that the "
+ "eleventh partition covers the entire "
+ "disk and is of type 'SGI volume'."));
+
+ set_partition(cxt, n, first, last - first, sys);
+ cxt->label->nparts_cur = count_used_partitions(cxt);
+ if (partno)
+ *partno = n;
+ return 0;
+}
+
+static int sgi_create_disklabel(struct fdisk_context *cxt)
+{
+ struct fdisk_sgi_label *sgi;
+ struct sgi_disklabel *sgilabel;
+ int rc;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SGI));
+
+#ifdef HDIO_GETGEO
+ if (cxt->geom.heads && cxt->geom.sectors) {
+ fdisk_sector_t llsectors;
+
+ if (blkdev_get_sectors(cxt->dev_fd, (unsigned long long *) &llsectors) == 0) {
+ /* the get device size ioctl was successful */
+ fdisk_sector_t llcyls;
+ int sec_fac = cxt->sector_size / 512;
+
+ llcyls = llsectors / (cxt->geom.heads * cxt->geom.sectors * sec_fac);
+ cxt->geom.cylinders = llcyls;
+ if (cxt->geom.cylinders != llcyls) /* truncated? */
+ cxt->geom.cylinders = ~0;
+ } else {
+ /* otherwise print error and use truncated version */
+ fdisk_warnx(cxt,
+ _("BLKGETSIZE ioctl failed on %s. "
+ "Using geometry cylinder value of %llu. "
+ "This value may be truncated for devices "
+ "> 33.8 GB."), cxt->dev_path, cxt->geom.cylinders);
+ }
+ }
+#endif
+ rc = fdisk_init_firstsector_buffer(cxt);
+ if (rc)
+ return rc;
+
+ sgi = (struct fdisk_sgi_label *) cxt->label;
+ sgi->header = (struct sgi_disklabel *) cxt->firstsector;
+
+ sgilabel = sgi->header;
+
+ sgilabel->magic = cpu_to_be32(SGI_LABEL_MAGIC);
+ sgilabel->root_part_num = cpu_to_be16(0);
+ sgilabel->swap_part_num = cpu_to_be16(1);
+
+ /* sizeof(sgilabel->boot_file) = 16 > 6 */
+ memset(sgilabel->boot_file, 0, 16);
+ strcpy((char *) sgilabel->boot_file, "/unix");
+
+ sgilabel->devparam.skew = (0);
+ sgilabel->devparam.gap1 = (0);
+ sgilabel->devparam.gap2 = (0);
+ sgilabel->devparam.sparecyl = (0);
+ sgilabel->devparam.pcylcount = cpu_to_be16(cxt->geom.cylinders);
+ sgilabel->devparam.head_vol0 = cpu_to_be16(0);
+ sgilabel->devparam.ntrks = cpu_to_be16(cxt->geom.heads);
+ /* tracks/cylinder (heads) */
+ sgilabel->devparam.cmd_tag_queue_depth = (0);
+ sgilabel->devparam.unused0 = (0);
+ sgilabel->devparam.unused1 = cpu_to_be16(0);
+ sgilabel->devparam.nsect = cpu_to_be16(cxt->geom.sectors);
+ /* sectors/track */
+ sgilabel->devparam.bytes = cpu_to_be16(cxt->sector_size);
+ sgilabel->devparam.ilfact = cpu_to_be16(1);
+ sgilabel->devparam.flags = cpu_to_be32(
+ SGI_DEVPARAM_TRACK_FWD
+ | SGI_DEVPARAM_IGNORE_ERRORS
+ | SGI_DEVPARAM_RESEEK);
+ sgilabel->devparam.datarate = cpu_to_be32(0);
+ sgilabel->devparam.retries_on_error = cpu_to_be32(1);
+ sgilabel->devparam.ms_per_word = cpu_to_be32(0);
+ sgilabel->devparam.xylogics_gap1 = cpu_to_be16(0);
+ sgilabel->devparam.xylogics_syncdelay = cpu_to_be16(0);
+ sgilabel->devparam.xylogics_readdelay = cpu_to_be16(0);
+ sgilabel->devparam.xylogics_gap2 = cpu_to_be16(0);
+ sgilabel->devparam.xylogics_readgate = cpu_to_be16(0);
+ sgilabel->devparam.xylogics_writecont = cpu_to_be16(0);
+
+ memset(&(sgilabel->volume), 0,
+ sizeof(struct sgi_volume) * SGI_MAXVOLUMES);
+ memset(&(sgilabel->partitions), 0,
+ sizeof(struct sgi_partition) * SGI_MAXPARTITIONS);
+ cxt->label->nparts_max = SGI_MAXPARTITIONS;
+
+ /* don't create default layout when a script defined */
+ if (!cxt->script) {
+ sgi_set_entire(cxt);
+ sgi_set_volhdr(cxt);
+ }
+ cxt->label->nparts_cur = count_used_partitions(cxt);
+
+ fdisk_info(cxt, _("Created a new SGI disklabel."));
+ return 0;
+}
+
+static int sgi_set_partition(struct fdisk_context *cxt,
+ size_t i,
+ struct fdisk_partition *pa)
+{
+ struct sgi_disklabel *sgilabel;
+
+ if (i >= cxt->label->nparts_max)
+ return -EINVAL;
+
+ sgilabel = self_disklabel(cxt);
+
+ if (pa->type) {
+ struct fdisk_parttype *t = pa->type;
+
+ if (t->code > UINT32_MAX)
+ return -EINVAL;
+
+ if (sgi_get_num_sectors(cxt, i) == 0) /* caught already before, ... */ {
+ fdisk_warnx(cxt, _("Sorry, only for non-empty partitions you can change the tag."));
+ return -EINVAL;
+ }
+
+ if ((i == 10 && t->code != SGI_TYPE_ENTIRE_DISK)
+ || (i == 8 && t->code != 0))
+ fdisk_info(cxt, _("Consider leaving partition 9 as volume header (0), "
+ "and partition 11 as entire volume (6), "
+ "as IRIX expects it."));
+
+ if (cxt->script == NULL
+ && ((t->code != SGI_TYPE_ENTIRE_DISK) && (t->code != SGI_TYPE_VOLHDR))
+ && (sgi_get_start_sector(cxt, i) < 1)) {
+ int yes = 0;
+ fdisk_ask_yesno(cxt,
+ _("It is highly recommended that the partition at offset 0 "
+ "is of type \"SGI volhdr\", the IRIX system will rely on it to "
+ "retrieve from its directory standalone tools like sash and fx. "
+ "Only the \"SGI volume\" entire disk section may violate this. "
+ "Are you sure about tagging this partition differently?"), &yes);
+ if (!yes)
+ return 1;
+ }
+
+ sgilabel->partitions[i].type = cpu_to_be32(t->code);
+ }
+
+ if (fdisk_partition_has_start(pa))
+ sgilabel->partitions[i].first_block = cpu_to_be32(pa->start);
+ if (fdisk_partition_has_size(pa))
+ sgilabel->partitions[i].num_blocks = cpu_to_be32(pa->size);
+
+ fdisk_label_set_changed(cxt->label, 1);
+ return 0;
+}
+
+
+static int sgi_partition_is_used(
+ struct fdisk_context *cxt,
+ size_t i)
+{
+ assert(cxt);
+ assert(fdisk_is_label(cxt, SGI));
+
+ if (i >= cxt->label->nparts_max)
+ return 0;
+ return sgi_get_num_sectors(cxt, i) ? 1 : 0;
+}
+
+static int sgi_toggle_partition_flag(struct fdisk_context *cxt, size_t i, unsigned long flag)
+{
+ struct sgi_disklabel *sgilabel;
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SGI));
+
+ if (i >= cxt->label->nparts_max)
+ return -EINVAL;
+
+ sgilabel = self_disklabel(cxt);
+
+ switch (flag) {
+ case SGI_FLAG_BOOT:
+ sgilabel->root_part_num =
+ be16_to_cpu(sgilabel->root_part_num) == i ?
+ 0 : cpu_to_be16(i);
+ fdisk_label_set_changed(cxt->label, 1);
+ break;
+ case SGI_FLAG_SWAP:
+ sgilabel->swap_part_num =
+ be16_to_cpu(sgilabel->swap_part_num) == i ?
+ 0 : cpu_to_be16(i);
+ fdisk_label_set_changed(cxt->label, 1);
+ break;
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct fdisk_field sgi_fields[] =
+{
+ { FDISK_FIELD_DEVICE, N_("Device"), 10, 0 },
+ { FDISK_FIELD_START, N_("Start"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_END, N_("End"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_SECTORS, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_CYLINDERS,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_SIZE, N_("Size"), 5, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
+ { FDISK_FIELD_TYPEID, N_("Id"), 2, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_TYPE, N_("Type"), 0.1, FDISK_FIELDFL_EYECANDY },
+ { FDISK_FIELD_ATTR, N_("Attrs"), 0, FDISK_FIELDFL_NUMBER }
+};
+
+static const struct fdisk_label_operations sgi_operations =
+{
+ .probe = sgi_probe_label,
+ .write = sgi_write_disklabel,
+ .verify = sgi_verify_disklabel,
+ .create = sgi_create_disklabel,
+ .list = sgi_list_table,
+
+ .get_part = sgi_get_partition,
+ .set_part = sgi_set_partition,
+ .add_part = sgi_add_partition,
+ .del_part = sgi_delete_partition,
+
+ .part_is_used = sgi_partition_is_used,
+ .part_toggle_flag = sgi_toggle_partition_flag
+};
+
+/* Allocates an SGI label driver. */
+struct fdisk_label *fdisk_new_sgi_label(struct fdisk_context *cxt)
+{
+ struct fdisk_label *lb;
+ struct fdisk_sgi_label *sgi;
+
+ assert(cxt);
+
+ sgi = calloc(1, sizeof(*sgi));
+ if (!sgi)
+ return NULL;
+
+ /* initialize generic part of the driver */
+ lb = (struct fdisk_label *) sgi;
+ lb->name = "sgi";
+ lb->id = FDISK_DISKLABEL_SGI;
+ lb->op = &sgi_operations;
+ lb->parttypes = sgi_parttypes;
+ lb->nparttypes = ARRAY_SIZE(sgi_parttypes) - 1;
+ lb->fields = sgi_fields;
+ lb->nfields = ARRAY_SIZE(sgi_fields);
+
+ lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
+
+ return lb;
+}
diff --git a/libblkid/libfdisk/src/sun.c b/libblkid/libfdisk/src/sun.c
new file mode 100644
index 000000000..babff6263
--- /dev/null
+++ b/libblkid/libfdisk/src/sun.c
@@ -0,0 +1,1130 @@
+/*
+ * Copyright (C) 2013 Karel Zak <kzak@redhat.com>
+ *
+ * Based on original code from fdisk:
+ * Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996
+ * Merged with fdisk for other architectures, aeb, June 1998.
+ * Arnaldo Carvalho de Melo <acme@conectiva.com.br> Mar 1999, Internationalization
+ */
+#include <stdio.h> /* stderr */
+#include <stdlib.h> /* qsort */
+#include <string.h> /* strstr */
+#include <unistd.h> /* write */
+#include <sys/ioctl.h> /* ioctl */
+
+#include "nls.h"
+#include "blkdev.h"
+#include "bitops.h"
+
+#include "fdiskP.h"
+#include "pt-sun.h"
+#include "all-io.h"
+
+
+/**
+ * SECTION: sun
+ * @title: SUN
+ * @short_description: disk label specific functions
+ *
+ */
+
+/*
+ * in-memory fdisk SUN stuff
+ */
+struct fdisk_sun_label {
+ struct fdisk_label head; /* generic part */
+ struct sun_disklabel *header; /* on-disk data (pointer to cxt->firstsector) */
+};
+
+static struct fdisk_parttype sun_parttypes[] = {
+ {SUN_TAG_UNASSIGNED, N_("Unassigned")},
+ {SUN_TAG_BOOT, N_("Boot")},
+ {SUN_TAG_ROOT, N_("SunOS root")},
+ {SUN_TAG_SWAP, N_("SunOS swap")},
+ {SUN_TAG_USR, N_("SunOS usr")},
+ {SUN_TAG_WHOLEDISK, N_("Whole disk")},
+ {SUN_TAG_STAND, N_("SunOS stand")},
+ {SUN_TAG_VAR, N_("SunOS var")},
+ {SUN_TAG_HOME, N_("SunOS home")},
+ {SUN_TAG_ALTSCTR, N_("SunOS alt sectors")},
+ {SUN_TAG_CACHE, N_("SunOS cachefs")},
+ {SUN_TAG_RESERVED, N_("SunOS reserved")},
+ {SUN_TAG_LINUX_SWAP, N_("Linux swap")},
+ {SUN_TAG_LINUX_NATIVE, N_("Linux native")},
+ {SUN_TAG_LINUX_LVM, N_("Linux LVM")},
+ {SUN_TAG_LINUX_RAID, N_("Linux raid autodetect")},
+ { 0, NULL }
+};
+
+/* return poiter buffer with on-disk data */
+static inline struct sun_disklabel *self_disklabel(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SUN));
+
+ return ((struct fdisk_sun_label *) cxt->label)->header;
+}
+
+/* return in-memory sun fdisk data */
+static inline struct fdisk_sun_label *self_label(struct fdisk_context *cxt)
+{
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SUN));
+
+ return (struct fdisk_sun_label *) cxt->label;
+}
+
+static void set_partition(struct fdisk_context *cxt, size_t i,
+ uint32_t start,uint32_t stop, uint16_t sysid)
+{
+ struct sun_disklabel *sunlabel = self_disklabel(cxt);
+ struct fdisk_parttype *t =
+ fdisk_label_get_parttype_from_code(cxt->label, sysid);
+
+ sunlabel->vtoc.infos[i].id = cpu_to_be16(sysid);
+ sunlabel->vtoc.infos[i].flags = cpu_to_be16(0);
+ sunlabel->partitions[i].start_cylinder =
+ cpu_to_be32(start / (cxt->geom.heads * cxt->geom.sectors));
+ sunlabel->partitions[i].num_sectors = cpu_to_be32(stop - start);
+ fdisk_label_set_changed(cxt->label, 1);
+
+ fdisk_info_new_partition(cxt, i + 1, start, stop, t);
+}
+
+static size_t count_used_partitions(struct fdisk_context *cxt)
+{
+ struct sun_disklabel *sunlabel = self_disklabel(cxt);
+ size_t ct = 0, i;
+
+ assert(sunlabel);
+
+ for (i = 0; i < cxt->label->nparts_max; i++) {
+ if (sunlabel->partitions[i].num_sectors)
+ ct++;
+ }
+ return ct;
+}
+
+static int sun_probe_label(struct fdisk_context *cxt)
+{
+ struct fdisk_sun_label *sun;
+ struct sun_disklabel *sunlabel;
+ unsigned short *ush;
+ int csum;
+ int need_fixing = 0;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SUN));
+
+ /* map first sector to header */
+ sun = (struct fdisk_sun_label *) cxt->label;
+ sun->header = (struct sun_disklabel *) cxt->firstsector;
+ sunlabel = sun->header;
+
+ if (be16_to_cpu(sunlabel->magic) != SUN_LABEL_MAGIC) {
+ sun->header = NULL;
+ return 0; /* failed */
+ }
+
+ ush = ((unsigned short *) (sunlabel + 1)) - 1;
+ for (csum = 0; ush >= (unsigned short *)sunlabel;)
+ csum ^= *ush--;
+
+ if (csum) {
+ fdisk_warnx(cxt, _("Detected sun disklabel with wrong checksum. "
+ "Probably you'll have to set all the values, "
+ "e.g. heads, sectors, cylinders and partitions "
+ "or force a fresh label (s command in main menu)"));
+ return 1;
+ }
+
+ cxt->label->nparts_max = SUN_MAXPARTITIONS;
+ cxt->geom.heads = be16_to_cpu(sunlabel->nhead);
+ cxt->geom.cylinders = be16_to_cpu(sunlabel->ncyl);
+ cxt->geom.sectors = be16_to_cpu(sunlabel->nsect);
+
+ if (be32_to_cpu(sunlabel->vtoc.version) != SUN_VTOC_VERSION) {
+ fdisk_warnx(cxt, _("Detected sun disklabel with wrong version [%d]."),
+ be32_to_cpu(sunlabel->vtoc.version));
+ need_fixing = 1;
+ }
+ if (be32_to_cpu(sunlabel->vtoc.sanity) != SUN_VTOC_SANITY) {
+ fdisk_warnx(cxt, _("Detected sun disklabel with wrong vtoc.sanity [0x%08x]."),
+ be32_to_cpu(sunlabel->vtoc.sanity));
+ need_fixing = 1;
+ }
+ if (be16_to_cpu(sunlabel->vtoc.nparts) != SUN_MAXPARTITIONS) {
+ fdisk_warnx(cxt, _("Detected sun disklabel with wrong vtoc.nparts [%u]."),
+ be16_to_cpu(sunlabel->vtoc.nparts));
+ need_fixing = 1;
+ }
+ if (need_fixing) {
+ fdisk_warnx(cxt, _("Warning: Wrong values need to be fixed up and "
+ "will be corrected by w(rite)"));
+
+ sunlabel->vtoc.version = cpu_to_be32(SUN_VTOC_VERSION);
+ sunlabel->vtoc.sanity = cpu_to_be32(SUN_VTOC_SANITY);
+ sunlabel->vtoc.nparts = cpu_to_be16(SUN_MAXPARTITIONS);
+
+ ush = (unsigned short *)sunlabel;
+ csum = 0;
+ while(ush < (unsigned short *)(&sunlabel->csum))
+ csum ^= *ush++;
+ sunlabel->csum = csum;
+
+ fdisk_label_set_changed(cxt->label, 1);
+ }
+
+ cxt->label->nparts_cur = count_used_partitions(cxt);
+
+ return 1;
+}
+
+static void ask_geom(struct fdisk_context *cxt)
+{
+ uintmax_t res;
+
+ assert(cxt);
+
+ if (fdisk_ask_number(cxt, 1, 1, 1024, _("Heads"), &res) == 0)
+ cxt->geom.heads = res;
+ if (fdisk_ask_number(cxt, 1, 1, 1024, _("Sectors/track"), &res) == 0)
+ cxt->geom.sectors = res;
+ if (fdisk_ask_number(cxt, 1, 1, USHRT_MAX, _("Cylinders"), &res) == 0)
+ cxt->geom.cylinders = res;
+}
+
+static int sun_create_disklabel(struct fdisk_context *cxt)
+{
+ unsigned int ndiv;
+ struct fdisk_sun_label *sun; /* libfdisk sun handler */
+ struct sun_disklabel *sunlabel; /* on disk data */
+ int rc = 0;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SUN));
+
+ /* map first sector to header */
+ rc = fdisk_init_firstsector_buffer(cxt);
+ if (rc)
+ return rc;
+
+ sun = (struct fdisk_sun_label *) cxt->label;
+ sun->header = (struct sun_disklabel *) cxt->firstsector;
+
+ sunlabel = sun->header;
+
+ cxt->label->nparts_max = SUN_MAXPARTITIONS;
+
+ sunlabel->magic = cpu_to_be16(SUN_LABEL_MAGIC);
+ sunlabel->vtoc.version = cpu_to_be32(SUN_VTOC_VERSION);
+ sunlabel->vtoc.sanity = cpu_to_be32(SUN_VTOC_SANITY);
+ sunlabel->vtoc.nparts = cpu_to_be16(SUN_MAXPARTITIONS);
+
+#ifdef HDIO_GETGEO
+ if (cxt->geom.heads && cxt->geom.sectors) {
+ fdisk_sector_t llsectors;
+
+ if (blkdev_get_sectors(cxt->dev_fd, (unsigned long long *) &llsectors) == 0) {
+ int sec_fac = cxt->sector_size / 512;
+ fdisk_sector_t llcyls;
+
+ llcyls = llsectors / (cxt->geom.heads * cxt->geom.sectors * sec_fac);
+ cxt->geom.cylinders = llcyls;
+ if (cxt->geom.cylinders != llcyls)
+ cxt->geom.cylinders = ~0;
+ } else {
+ fdisk_warnx(cxt,
+ _("BLKGETSIZE ioctl failed on %s. "
+ "Using geometry cylinder value of %llu. "
+ "This value may be truncated for devices "
+ "> 33.8 GB."),
+ cxt->dev_path, cxt->geom.cylinders);
+ }
+ } else
+#endif
+ ask_geom(cxt);
+
+ sunlabel->acyl = cpu_to_be16(0);
+ sunlabel->pcyl = cpu_to_be16(cxt->geom.cylinders);
+ sunlabel->rpm = cpu_to_be16(5400);
+ sunlabel->intrlv = cpu_to_be16(1);
+ sunlabel->apc = cpu_to_be16(0);
+
+ sunlabel->nhead = cpu_to_be16(cxt->geom.heads);
+ sunlabel->nsect = cpu_to_be16(cxt->geom.sectors);
+ sunlabel->ncyl = cpu_to_be16(cxt->geom.cylinders);
+
+ snprintf((char *) sunlabel->label_id, sizeof(sunlabel->label_id),
+ "Linux cyl %ju alt %u hd %u sec %ju",
+ (uintmax_t) cxt->geom.cylinders,
+ be16_to_cpu(sunlabel->acyl),
+ cxt->geom.heads,
+ (uintmax_t) cxt->geom.sectors);
+
+ if (cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors >= 150 * 2048) {
+ ndiv = cxt->geom.cylinders - (50 * 2048 / (cxt->geom.heads * cxt->geom.sectors)); /* 50M swap */
+ } else
+ ndiv = cxt->geom.cylinders * 2 / 3;
+
+ /* create the default layout only if no-script defined */
+ if (!cxt->script) {
+ set_partition(cxt, 0, 0, ndiv * cxt->geom.heads * cxt->geom.sectors,
+ SUN_TAG_LINUX_NATIVE);
+ set_partition(cxt, 1, ndiv * cxt->geom.heads * cxt->geom.sectors,
+ cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors,
+ SUN_TAG_LINUX_SWAP);
+ sunlabel->vtoc.infos[1].flags |= cpu_to_be16(SUN_FLAG_UNMNT);
+
+ set_partition(cxt, 2, 0,
+ cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors,
+ SUN_TAG_WHOLEDISK);
+ }
+
+ {
+ unsigned short *ush = (unsigned short *)sunlabel;
+ unsigned short csum = 0;
+ while(ush < (unsigned short *)(&sunlabel->csum))
+ csum ^= *ush++;
+ sunlabel->csum = csum;
+ }
+
+ fdisk_label_set_changed(cxt->label, 1);
+ cxt->label->nparts_cur = count_used_partitions(cxt);
+
+ fdisk_info(cxt, _("Created a new Sun disklabel."));
+ return 0;
+}
+
+static int sun_toggle_partition_flag(struct fdisk_context *cxt, size_t i, unsigned long flag)
+{
+ struct sun_disklabel *sunlabel;
+ struct sun_info *p;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SUN));
+
+ if (i >= cxt->label->nparts_max)
+ return -EINVAL;
+
+ sunlabel = self_disklabel(cxt);
+ p = &sunlabel->vtoc.infos[i];
+
+ switch (flag) {
+ case SUN_FLAG_UNMNT:
+ p->flags ^= cpu_to_be16(SUN_FLAG_UNMNT);
+ fdisk_label_set_changed(cxt->label, 1);
+ break;
+ case SUN_FLAG_RONLY:
+ p->flags ^= cpu_to_be16(SUN_FLAG_RONLY);
+ fdisk_label_set_changed(cxt->label, 1);
+ break;
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+static void fetch_sun(struct fdisk_context *cxt,
+ uint32_t *starts,
+ uint32_t *lens,
+ uint32_t *start,
+ uint32_t *stop)
+{
+ struct sun_disklabel *sunlabel;
+ int continuous = 1;
+ size_t i;
+
+ assert(cxt);
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SUN));
+
+ sunlabel = self_disklabel(cxt);
+
+ *start = 0;
+ *stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors;
+
+ for (i = 0; i < cxt->label->nparts_max; i++) {
+ struct sun_partition *part = &sunlabel->partitions[i];
+ struct sun_info *info = &sunlabel->vtoc.infos[i];
+
+ if (part->num_sectors &&
+ be16_to_cpu(info->id) != SUN_TAG_UNASSIGNED &&
+ be16_to_cpu(info->id) != SUN_TAG_WHOLEDISK) {
+ starts[i] = be32_to_cpu(part->start_cylinder) *
+ cxt->geom.heads * cxt->geom.sectors;
+ lens[i] = be32_to_cpu(part->num_sectors);
+ if (continuous) {
+ if (starts[i] == *start)
+ *start += lens[i];
+ else if (starts[i] + lens[i] >= *stop)
+ *stop = starts[i];
+ else
+ continuous = 0;
+ /* There will be probably more gaps
+ than one, so lets check afterwards */
+ }
+ } else {
+ starts[i] = 0;
+ lens[i] = 0;
+ }
+ }
+}
+
+#ifdef HAVE_QSORT_R
+static int verify_sun_cmp(int *a, int *b, void *data)
+{
+ unsigned int *verify_sun_starts = (unsigned int *) data;
+
+ if (*a == -1)
+ return 1;
+ if (*b == -1)
+ return -1;
+ if (verify_sun_starts[*a] > verify_sun_starts[*b])
+ return 1;
+ return -1;
+}
+#endif
+
+static int sun_verify_disklabel(struct fdisk_context *cxt)
+{
+ uint32_t starts[SUN_MAXPARTITIONS], lens[SUN_MAXPARTITIONS], start, stop;
+ uint32_t i,j,k,starto,endo;
+#ifdef HAVE_QSORT_R
+ int array[SUN_MAXPARTITIONS];
+ unsigned int *verify_sun_starts;
+#endif
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SUN));
+
+ fetch_sun(cxt, starts, lens, &start, &stop);
+
+ for (k = 0; k < 7; k++) {
+ for (i = 0; i < SUN_MAXPARTITIONS; i++) {
+ if (k && (lens[i] % (cxt->geom.heads * cxt->geom.sectors)))
+ fdisk_warnx(cxt, _("Partition %u doesn't end on cylinder boundary."), i+1);
+ if (lens[i]) {
+ for (j = 0; j < i; j++)
+ if (lens[j]) {
+ if (starts[j] == starts[i]+lens[i]) {
+ starts[j] = starts[i]; lens[j] += lens[i];
+ lens[i] = 0;
+ } else if (starts[i] == starts[j]+lens[j]){
+ lens[j] += lens[i];
+ lens[i] = 0;
+ } else if (!k) {
+ if (starts[i] < starts[j]+lens[j] &&
+ starts[j] < starts[i]+lens[i]) {
+ starto = starts[i];
+ if (starts[j] > starto)
+ starto = starts[j];
+ endo = starts[i]+lens[i];
+ if (starts[j]+lens[j] < endo)
+ endo = starts[j]+lens[j];
+ fdisk_warnx(cxt, _("Partition %u overlaps with others in "
+ "sectors %u-%u."), i+1, starto, endo);
+ }
+ }
+ }
+ }
+ }
+ }
+
+#ifdef HAVE_QSORT_R
+ for (i = 0; i < SUN_MAXPARTITIONS; i++) {
+ if (lens[i])
+ array[i] = i;
+ else
+ array[i] = -1;
+ }
+ verify_sun_starts = starts;
+
+ qsort_r(array,ARRAY_SIZE(array),sizeof(array[0]),
+ (int (*)(const void *,const void *,void *)) verify_sun_cmp,
+ verify_sun_starts);
+
+ if (array[0] == -1) {
+ fdisk_info(cxt, _("No partitions defined."));
+ return 0;
+ }
+ stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors;
+ if (starts[array[0]])
+ fdisk_warnx(cxt, _("Unused gap - sectors 0-%u."), starts[array[0]]);
+ for (i = 0; i < 7 && array[i+1] != -1; i++) {
+ fdisk_warnx(cxt, _("Unused gap - sectors %u-%u."),
+ (starts[array[i]] + lens[array[i]]),
+ starts[array[i+1]]);
+ }
+ start = (starts[array[i]] + lens[array[i]]);
+ if (start < stop)
+ fdisk_warnx(cxt, _("Unused gap - sectors %u-%u."), start, stop);
+#endif
+ return 0;
+}
+
+
+static int is_free_sector(struct fdisk_context *cxt,
+ fdisk_sector_t s, uint32_t starts[], uint32_t lens[])
+{
+ size_t i;
+
+ for (i = 0; i < cxt->label->nparts_max; i++) {
+ if (lens[i] && starts[i] <= s
+ && starts[i] + lens[i] > s)
+ return 0;
+ }
+ return 1;
+}
+
+static int sun_add_partition(
+ struct fdisk_context *cxt,
+ struct fdisk_partition *pa,
+ size_t *partno)
+{
+ struct sun_disklabel *sunlabel = self_disklabel(cxt);
+ uint32_t starts[SUN_MAXPARTITIONS], lens[SUN_MAXPARTITIONS];
+ struct sun_partition *part;
+ struct sun_info *info;
+ uint32_t start, stop, stop2;
+ int whole_disk = 0;
+ int sys = pa && pa->type ? pa->type->code : SUN_TAG_LINUX_NATIVE;
+ int rc;
+ size_t n;
+
+ char mesg[256];
+ size_t i;
+ unsigned int first, last;
+
+ rc = fdisk_partition_next_partno(pa, cxt, &n);
+ if (rc)
+ return rc;
+
+ part = &sunlabel->partitions[n];
+ info = &sunlabel->vtoc.infos[n];
+
+ if (part->num_sectors && be16_to_cpu(info->id) != SUN_TAG_UNASSIGNED) {
+ fdisk_info(cxt, _("Partition %zu is already defined. Delete "
+ "it before re-adding it."), n + 1);
+ return -EINVAL;
+ }
+
+ fetch_sun(cxt, starts, lens, &start, &stop);
+
+ if (stop <= start) {
+ if (n == 2)
+ whole_disk = 1;
+ else {
+ fdisk_info(cxt, _("Other partitions already cover the "
+ "whole disk. Delete some/shrink them before retry."));
+ return -EINVAL;
+ }
+ }
+
+ if (pa && pa->start_follow_default)
+ first = start;
+ else if (pa && fdisk_partition_has_start(pa)) {
+ first = pa->start;
+
+ if (!whole_disk && !is_free_sector(cxt, first, starts, lens))
+ return -ERANGE;
+ } else {
+ struct fdisk_ask *ask;
+
+ snprintf(mesg, sizeof(mesg), _("First %s"),
+ fdisk_get_unit(cxt, FDISK_SINGULAR));
+ for (;;) {
+ ask = fdisk_new_ask();
+ if (!ask)
+ return -ENOMEM;
+
+ fdisk_ask_set_query(ask, mesg);
+ fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
+
+ if (whole_disk) {
+ fdisk_ask_number_set_low(ask, 0); /* minimal */
+ fdisk_ask_number_set_default(ask, 0); /* default */
+ fdisk_ask_number_set_high(ask, 0); /* maximal */
+ } else {
+ fdisk_ask_number_set_low(ask, fdisk_scround(cxt, start)); /* minimal */
+ fdisk_ask_number_set_default(ask, fdisk_scround(cxt, start)); /* default */
+ fdisk_ask_number_set_high(ask, fdisk_scround(cxt, stop)); /* maximal */
+ }
+ rc = fdisk_do_ask(cxt, ask);
+ first = fdisk_ask_number_get_result(ask);
+ fdisk_unref_ask(ask);
+ if (rc)
+ return rc;
+
+ if (fdisk_use_cylinders(cxt))
+ first *= fdisk_get_units_per_sector(cxt);
+
+ /* ewt asks to add: "don't start a partition at cyl 0"
+ However, edmundo@rano.demon.co.uk writes:
+ "In addition to having a Sun partition table, to be able to
+ boot from the disc, the first partition, /dev/sdX1, must
+ start at cylinder 0. This means that /dev/sdX1 contains
+ the partition table and the boot block, as these are the
+ first two sectors of the disc. Therefore you must be
+ careful what you use /dev/sdX1 for. In particular, you must
+ not use a partition starting at cylinder 0 for Linux swap,
+ as that would overwrite the partition table and the boot
+ block. You may, however, use such a partition for a UFS
+ or EXT2 file system, as these file systems leave the first
+ 1024 bytes undisturbed. */
+ /* On the other hand, one should not use partitions
+ starting at block 0 in an md, or the label will
+ be trashed. */
+ if (!is_free_sector(cxt, first, starts, lens) && !whole_disk) {
+ if (n == 2 && !first) {
+ whole_disk = 1;
+ break;
+ }
+ fdisk_warnx(cxt, _("Sector %d is already allocated"), first);
+ } else
+ break;
+ }
+ }
+
+ if (n == 2 && first != 0)
+ fdisk_warnx(cxt, _("It is highly recommended that the "
+ "third partition covers the whole disk "
+ "and is of type `Whole disk'"));
+
+ if (!fdisk_use_cylinders(cxt)) {
+ /* Starting sector has to be properly aligned */
+ int cs = cxt->geom.heads * cxt->geom.sectors;
+ int x = first % cs;
+
+ if (x) {
+ fdisk_info(cxt, _("Aligning the first sector from %u to %u "
+ "to be on cylinder boundary."),
+ first, first + cs - x);
+ first += cs - x;
+ }
+ }
+
+ stop = cxt->geom.cylinders * cxt->geom.heads * cxt->geom.sectors; /* ancient */
+ stop2 = stop;
+ for (i = 0; i < cxt->label->nparts_max; i++) {
+ if (starts[i] > first && starts[i] < stop)
+ stop = starts[i];
+ }
+
+ /* last */
+ if (pa && pa->end_follow_default)
+ last = whole_disk || (n == 2 && !first) ? stop2 : stop;
+ else if (pa && fdisk_partition_has_size(pa)) {
+ last = first + pa->size - 1ULL;
+
+ if (!whole_disk && last > stop)
+ return -ERANGE;
+ } else {
+ struct fdisk_ask *ask = fdisk_new_ask();
+
+ if (!ask)
+ return -ENOMEM;
+
+ snprintf(mesg, sizeof(mesg),
+ _("Last %s or +%s or +size{K,M,G,T,P}"),
+ fdisk_get_unit(cxt, FDISK_SINGULAR),
+ fdisk_get_unit(cxt, FDISK_PLURAL));
+ fdisk_ask_set_query(ask, mesg);
+ fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
+
+ if (whole_disk) {
+ fdisk_ask_number_set_low(ask, fdisk_scround(cxt, stop2)); /* minimal */
+ fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop2)); /* default */
+ fdisk_ask_number_set_high(ask, fdisk_scround(cxt, stop2)); /* maximal */
+ fdisk_ask_number_set_base(ask, 0);
+ } else if (n == 2 && !first) {
+ fdisk_ask_number_set_low(ask, fdisk_scround(cxt, first)); /* minimal */
+ fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop2)); /* default */
+ fdisk_ask_number_set_high(ask, fdisk_scround(cxt, stop2)); /* maximal */
+ fdisk_ask_number_set_base(ask, fdisk_scround(cxt, first));
+ } else {
+ fdisk_ask_number_set_low(ask, fdisk_scround(cxt, first)); /* minimal */
+ fdisk_ask_number_set_default(ask, fdisk_scround(cxt, stop)); /* default */
+ fdisk_ask_number_set_high(ask, fdisk_scround(cxt, stop)); /* maximal */
+ fdisk_ask_number_set_base(ask, fdisk_scround(cxt, first));
+ }
+
+ if (fdisk_use_cylinders(cxt))
+ fdisk_ask_number_set_unit(ask,
+ cxt->sector_size *
+ fdisk_get_units_per_sector(cxt));
+ else
+ fdisk_ask_number_set_unit(ask, cxt->sector_size);
+
+ rc = fdisk_do_ask(cxt, ask);
+ last = fdisk_ask_number_get_result(ask);
+
+ fdisk_unref_ask(ask);
+ if (rc)
+ return rc;
+ if (fdisk_use_cylinders(cxt))
+ last *= fdisk_get_units_per_sector(cxt);
+ }
+
+ if (n == 2 && !first) {
+ if (last >= stop2) {
+ whole_disk = 1;
+ last = stop2;
+ } else if (last > stop) {
+ fdisk_warnx(cxt,
+ _("You haven't covered the whole disk with the 3rd partition, but your value\n"
+ "%lu %s covers some other partition. Your entry has been changed\n"
+ "to %lu %s"),
+ (unsigned long) fdisk_scround(cxt, last), fdisk_get_unit(cxt, FDISK_SINGULAR),
+ (unsigned long) fdisk_scround(cxt, stop), fdisk_get_unit(cxt, FDISK_SINGULAR));
+ last = stop;
+ }
+ } else if (!whole_disk && last > stop)
+ last = stop;
+
+ if (whole_disk)
+ sys = SUN_TAG_WHOLEDISK;
+
+ set_partition(cxt, n, first, last, sys);
+ cxt->label->nparts_cur = count_used_partitions(cxt);
+ if (partno)
+ *partno = n;
+ return 0;
+}
+
+static int sun_delete_partition(struct fdisk_context *cxt,
+ size_t partnum)
+{
+ struct sun_disklabel *sunlabel;
+ struct sun_partition *part;
+ struct sun_info *info;
+ unsigned int nsec;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SUN));
+
+ sunlabel = self_disklabel(cxt);
+ part = &sunlabel->partitions[partnum];
+ info = &sunlabel->vtoc.infos[partnum];
+
+ if (partnum == 2 &&
+ be16_to_cpu(info->id) == SUN_TAG_WHOLEDISK &&
+ !part->start_cylinder &&
+ (nsec = be32_to_cpu(part->num_sectors))
+ == cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders)
+ fdisk_info(cxt, _("If you want to maintain SunOS/Solaris compatibility, "
+ "consider leaving this "
+ "partition as Whole disk (5), starting at 0, with %u "
+ "sectors"), nsec);
+ info->id = cpu_to_be16(SUN_TAG_UNASSIGNED);
+ part->num_sectors = 0;
+ cxt->label->nparts_cur = count_used_partitions(cxt);
+ fdisk_label_set_changed(cxt->label, 1);
+ return 0;
+}
+
+
+static int sun_list_disklabel(struct fdisk_context *cxt)
+{
+ struct sun_disklabel *sunlabel;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SUN));
+
+ sunlabel = self_disklabel(cxt);
+
+ if (fdisk_is_details(cxt)) {
+ fdisk_info(cxt,
+ _("Label geometry: %d rpm, %d alternate and %d physical cylinders,\n"
+ " %d extra sects/cyl, interleave %d:1"),
+ be16_to_cpu(sunlabel->rpm),
+ be16_to_cpu(sunlabel->acyl),
+ be16_to_cpu(sunlabel->pcyl),
+ be16_to_cpu(sunlabel->apc),
+ be16_to_cpu(sunlabel->intrlv));
+ fdisk_info(cxt, _("Label ID: %s"), sunlabel->label_id);
+ fdisk_info(cxt, _("Volume ID: %s"),
+ *sunlabel->vtoc.volume_id ? sunlabel->vtoc.volume_id : _("<none>"));
+ }
+
+ return 0;
+}
+
+static struct fdisk_parttype *sun_get_parttype(
+ struct fdisk_context *cxt,
+ size_t n)
+{
+ struct sun_disklabel *sunlabel = self_disklabel(cxt);
+ struct fdisk_parttype *t;
+
+ if (n >= cxt->label->nparts_max)
+ return NULL;
+
+ t = fdisk_label_get_parttype_from_code(cxt->label,
+ be16_to_cpu(sunlabel->vtoc.infos[n].id));
+ return t ? : fdisk_new_unknown_parttype(be16_to_cpu(sunlabel->vtoc.infos[n].id), NULL);
+}
+
+
+static int sun_get_partition(struct fdisk_context *cxt, size_t n,
+ struct fdisk_partition *pa)
+{
+ struct sun_disklabel *sunlabel;
+ struct sun_partition *part;
+ uint16_t flags;
+ uint32_t start, len;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SUN));
+
+ if (n >= cxt->label->nparts_max)
+ return -EINVAL;
+
+ sunlabel = self_disklabel(cxt);
+ part = &sunlabel->partitions[n];
+
+ pa->used = part->num_sectors ? 1 : 0;
+ if (!pa->used)
+ return 0;
+
+ flags = be16_to_cpu(sunlabel->vtoc.infos[n].flags);
+ start = be32_to_cpu(part->start_cylinder)
+ * cxt->geom.heads * cxt->geom.sectors;
+ len = be32_to_cpu(part->num_sectors);
+
+ pa->type = sun_get_parttype(cxt, n);
+ if (pa->type && pa->type->code == SUN_TAG_WHOLEDISK)
+ pa->wholedisk = 1;
+
+ if (flags & SUN_FLAG_UNMNT || flags & SUN_FLAG_RONLY) {
+ if (asprintf(&pa->attrs, "%c%c",
+ flags & SUN_FLAG_UNMNT ? 'u' : ' ',
+ flags & SUN_FLAG_RONLY ? 'r' : ' ') < 0)
+ return -ENOMEM;
+ }
+
+ pa->start = start;
+ pa->size = len;
+
+ return 0;
+}
+
+/**
+ * fdisk_sun_set_alt_cyl:
+ * @cxt: context
+ *
+ * Sets number of alternative cylinders. This function uses libfdisk Ask API
+ * for dialog with user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_alt_cyl(struct fdisk_context *cxt)
+{
+ struct sun_disklabel *sunlabel = self_disklabel(cxt);
+ uintmax_t res;
+ int rc = fdisk_ask_number(cxt, 0, /* low */
+ be16_to_cpu(sunlabel->acyl), /* default */
+ 65535, /* high */
+ _("Number of alternate cylinders"), /* query */
+ &res); /* result */
+ if (rc)
+ return rc;
+
+ sunlabel->acyl = cpu_to_be16(res);
+ return 0;
+}
+
+/**
+ * fdisk_sun_set_xcyl:
+ * @cxt: context
+ *
+ * Sets number of extra sectors per cylinder. This function uses libfdisk Ask API
+ * for dialog with user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_xcyl(struct fdisk_context *cxt)
+{
+ struct sun_disklabel *sunlabel = self_disklabel(cxt);
+ uintmax_t res;
+ int rc = fdisk_ask_number(cxt, 0, /* low */
+ be16_to_cpu(sunlabel->apc), /* default */
+ cxt->geom.sectors, /* high */
+ _("Extra sectors per cylinder"), /* query */
+ &res); /* result */
+ if (rc)
+ return rc;
+ sunlabel->apc = cpu_to_be16(res);
+ return 0;
+}
+
+/**
+ * fdisk_sun_set_ilfact:
+ * @cxt: context
+ *
+ * Sets interleave factor. This function uses libfdisk Ask API for dialog with
+ * user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_ilfact(struct fdisk_context *cxt)
+{
+ struct sun_disklabel *sunlabel = self_disklabel(cxt);
+ uintmax_t res;
+ int rc = fdisk_ask_number(cxt, 1, /* low */
+ be16_to_cpu(sunlabel->intrlv), /* default */
+ 32, /* high */
+ _("Interleave factor"), /* query */
+ &res); /* result */
+ if (rc)
+ return rc;
+ sunlabel->intrlv = cpu_to_be16(res);
+ return 0;
+}
+
+/**
+ * fdisk_sun_set_rspeed
+ * @cxt: context
+ *
+ * Sets rotation speed. This function uses libfdisk Ask API for dialog with
+ * user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_rspeed(struct fdisk_context *cxt)
+{
+ struct sun_disklabel *sunlabel = self_disklabel(cxt);
+ uintmax_t res;
+ int rc = fdisk_ask_number(cxt, 1, /* low */
+ be16_to_cpu(sunlabel->rpm), /* default */
+ USHRT_MAX, /* high */
+ _("Rotation speed (rpm)"), /* query */
+ &res); /* result */
+ if (rc)
+ return rc;
+ sunlabel->rpm = cpu_to_be16(res);
+ return 0;
+}
+
+/**
+ * fdisk_sun_set_pcylcount
+ * @cxt: context
+ *
+ * Sets number of physical cylinders. This function uses libfdisk Ask API for
+ * dialog with user.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_sun_set_pcylcount(struct fdisk_context *cxt)
+{
+ struct sun_disklabel *sunlabel = self_disklabel(cxt);
+ uintmax_t res;
+ int rc = fdisk_ask_number(cxt, 0, /* low */
+ be16_to_cpu(sunlabel->pcyl), /* default */
+ USHRT_MAX, /* high */
+ _("Number of physical cylinders"), /* query */
+ &res); /* result */
+ if (!rc)
+ return rc;
+ sunlabel->pcyl = cpu_to_be16(res);
+ return 0;
+}
+
+static int sun_write_disklabel(struct fdisk_context *cxt)
+{
+ struct sun_disklabel *sunlabel;
+ unsigned short *ush;
+ unsigned short csum = 0;
+ const size_t sz = sizeof(struct sun_disklabel);
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SUN));
+
+ sunlabel = self_disklabel(cxt);
+
+ /* Maybe geometry has been modified */
+ sunlabel->nhead = cpu_to_be16(cxt->geom.heads);
+ sunlabel->nsect = cpu_to_be16(cxt->geom.sectors);
+
+ if (cxt->geom.cylinders != be16_to_cpu(sunlabel->ncyl))
+ sunlabel->ncyl = cpu_to_be16( cxt->geom.cylinders
+ - be16_to_cpu(sunlabel->acyl) );
+
+ ush = (unsigned short *) sunlabel;
+
+ while(ush < (unsigned short *)(&sunlabel->csum))
+ csum ^= *ush++;
+ sunlabel->csum = csum;
+ if (lseek(cxt->dev_fd, 0, SEEK_SET) < 0)
+ return -errno;
+ if (write_all(cxt->dev_fd, sunlabel, sz) != 0)
+ return -errno;
+
+ return 0;
+}
+
+static int sun_set_partition(
+ struct fdisk_context *cxt,
+ size_t i,
+ struct fdisk_partition *pa)
+{
+ struct sun_disklabel *sunlabel;
+ struct sun_partition *part;
+ struct sun_info *info;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SUN));
+
+ sunlabel = self_disklabel(cxt);
+
+ if (i >= cxt->label->nparts_max)
+ return -EINVAL;
+
+ if (pa->type) {
+ struct fdisk_parttype *t = pa->type;
+
+ if (t->code > UINT16_MAX)
+ return -EINVAL;
+
+ if (i == 2 && t->code != SUN_TAG_WHOLEDISK)
+ fdisk_info(cxt, _("Consider leaving partition 3 as Whole disk (5),\n"
+ "as SunOS/Solaris expects it and even Linux likes it.\n"));
+
+ part = &sunlabel->partitions[i];
+ info = &sunlabel->vtoc.infos[i];
+
+ if (cxt->script == NULL &&
+ t->code == SUN_TAG_LINUX_SWAP && !part->start_cylinder) {
+ int yes, rc;
+
+ rc = fdisk_ask_yesno(cxt,
+ _("It is highly recommended that the partition at offset 0\n"
+ "is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n"
+ "there may destroy your partition table and bootblock.\n"
+ "Are you sure you want to tag the partition as Linux swap?"), &yes);
+ if (rc)
+ return rc;
+ if (!yes)
+ return 1;
+ }
+
+ switch (t->code) {
+ case SUN_TAG_SWAP:
+ case SUN_TAG_LINUX_SWAP:
+ /* swaps are not mountable by default */
+ info->flags |= cpu_to_be16(SUN_FLAG_UNMNT);
+ break;
+ default:
+ /* assume other types are mountable;
+ user can change it anyway */
+ info->flags &= ~cpu_to_be16(SUN_FLAG_UNMNT);
+ break;
+ }
+ info->id = cpu_to_be16(t->code);
+ }
+
+ if (fdisk_partition_has_start(pa))
+ sunlabel->partitions[i].start_cylinder =
+ cpu_to_be32(pa->start / (cxt->geom.heads * cxt->geom.sectors));
+ if (fdisk_partition_has_size(pa))
+ sunlabel->partitions[i].num_sectors = cpu_to_be32(pa->size);
+
+ fdisk_label_set_changed(cxt->label, 1);
+ return 0;
+}
+
+
+static int sun_reset_alignment(struct fdisk_context *cxt __attribute__((__unused__)))
+{
+ return 0;
+}
+
+
+static int sun_partition_is_used(
+ struct fdisk_context *cxt,
+ size_t i)
+{
+ struct sun_disklabel *sunlabel;
+
+ assert(cxt);
+ assert(cxt->label);
+ assert(fdisk_is_label(cxt, SUN));
+
+ if (i >= cxt->label->nparts_max)
+ return 0;
+
+ sunlabel = self_disklabel(cxt);
+ return sunlabel->partitions[i].num_sectors ? 1 : 0;
+}
+
+static const struct fdisk_field sun_fields[] =
+{
+ { FDISK_FIELD_DEVICE, N_("Device"), 10, 0 },
+ { FDISK_FIELD_START, N_("Start"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_END, N_("End"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_SECTORS, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_CYLINDERS,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_SIZE, N_("Size"), 5, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_TYPEID, N_("Id"), 2, FDISK_FIELDFL_NUMBER },
+ { FDISK_FIELD_TYPE, N_("Type"), 0.1, 0 },
+ { FDISK_FIELD_ATTR, N_("Flags"), 0, FDISK_FIELDFL_NUMBER }
+};
+
+const struct fdisk_label_operations sun_operations =
+{
+ .probe = sun_probe_label,
+ .write = sun_write_disklabel,
+ .verify = sun_verify_disklabel,
+ .create = sun_create_disklabel,
+ .list = sun_list_disklabel,
+
+ .get_part = sun_get_partition,
+ .set_part = sun_set_partition,
+ .add_part = sun_add_partition,
+ .del_part = sun_delete_partition,
+
+ .part_is_used = sun_partition_is_used,
+ .part_toggle_flag = sun_toggle_partition_flag,
+
+ .reset_alignment = sun_reset_alignment,
+};
+
+/*
+ * allocates SUN label driver
+ */
+struct fdisk_label *fdisk_new_sun_label(struct fdisk_context *cxt)
+{
+ struct fdisk_label *lb;
+ struct fdisk_sun_label *sun;
+
+ assert(cxt);
+
+ sun = calloc(1, sizeof(*sun));
+ if (!sun)
+ return NULL;
+
+ /* initialize generic part of the driver */
+ lb = (struct fdisk_label *) sun;
+ lb->name = "sun";
+ lb->id = FDISK_DISKLABEL_SUN;
+ lb->op = &sun_operations;
+ lb->parttypes = sun_parttypes;
+ lb->nparttypes = ARRAY_SIZE(sun_parttypes) - 1;
+ lb->fields = sun_fields;
+ lb->nfields = ARRAY_SIZE(sun_fields);
+ lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
+
+ return lb;
+}
diff --git a/libblkid/libfdisk/src/table.c b/libblkid/libfdisk/src/table.c
new file mode 100644
index 000000000..1add09fca
--- /dev/null
+++ b/libblkid/libfdisk/src/table.c
@@ -0,0 +1,664 @@
+
+#include "fdiskP.h"
+
+/**
+ * SECTION: table
+ * @title: Table
+ * @short_description: container for fdisk partitions
+ *
+ * The fdisk_table is simple container for fdisk_partitions. The table is no
+ * directly connected to label data (partition table), and table changes don't
+ * affect in-memory or on-disk data.
+ */
+
+/**
+ * fdisk_new_table:
+ *
+ * The table is a container for struct fdisk_partition entries. The container
+ * does not have any real connection with label (partition table) and with
+ * real on-disk data.
+ *
+ * Returns: newly allocated table struct.
+ */
+struct fdisk_table *fdisk_new_table(void)
+{
+ struct fdisk_table *tb = NULL;
+
+ tb = calloc(1, sizeof(*tb));
+ if (!tb)
+ return NULL;
+
+ DBG(TAB, ul_debugobj(tb, "alloc"));
+ tb->refcount = 1;
+ INIT_LIST_HEAD(&tb->parts);
+ return tb;
+}
+
+/**
+ * fdisk_reset_table:
+ * @tb: tab pointer
+ *
+ * Removes all entries (partitions) from the table. The parititons with zero
+ * reference count will be deallocated. This function does not modify partition
+ * table.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int fdisk_reset_table(struct fdisk_table *tb)
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "reset"));
+
+ while (!list_empty(&tb->parts)) {
+ struct fdisk_partition *pa = list_entry(tb->parts.next,
+ struct fdisk_partition, parts);
+ fdisk_table_remove_partition(tb, pa);
+ }
+
+ tb->nents = 0;
+ return 0;
+}
+
+/**
+ * fdisk_ref_table:
+ * @tb: table pointer
+ *
+ * Incremparts reference counter.
+ */
+void fdisk_ref_table(struct fdisk_table *tb)
+{
+ if (tb)
+ tb->refcount++;
+}
+
+/**
+ * fdisk_unref_table:
+ * @tb: table pointer
+ *
+ * De-incremparts reference counter, on zero the @tb is automatically
+ * deallocated.
+ */
+void fdisk_unref_table(struct fdisk_table *tb)
+{
+ if (!tb)
+ return;
+
+ tb->refcount--;
+ if (tb->refcount <= 0) {
+ fdisk_reset_table(tb);
+
+ DBG(TAB, ul_debugobj(tb, "free"));
+ free(tb);
+ }
+}
+
+/**
+ * fdisk_table_is_empty:
+ * @tb: pointer to tab
+ *
+ * Returns: 1 if the table is without filesystems, or 0.
+ */
+int fdisk_table_is_empty(struct fdisk_table *tb)
+{
+ return tb == NULL || list_empty(&tb->parts) ? 1 : 0;
+}
+
+/**
+ * fdisk_table_get_nents:
+ * @tb: pointer to tab
+ *
+ * Returns: number of entries in table.
+ */
+size_t fdisk_table_get_nents(struct fdisk_table *tb)
+{
+ return tb ? tb->nents : 0;
+}
+
+/**
+ * fdisk_table_next_partition:
+ * @tb: tab pointer
+ * @itr: iterator
+ * @pa: returns the next tab entry
+ *
+ * Returns: 0 on success, negative number in case of error or 1 at the end of list.
+ *
+ * Example:
+ * <informalexample>
+ * <programlisting>
+ * while(fdisk_table_next_partition(tb, itr, &pa) == 0) {
+ * ...
+ * }
+ * </programlisting>
+ * </informalexample>
+ */
+int fdisk_table_next_partition(
+ struct fdisk_table *tb,
+ struct fdisk_iter *itr,
+ struct fdisk_partition **pa)
+{
+ int rc = 1;
+
+ assert(tb);
+ assert(itr);
+ assert(pa);
+
+ if (!tb || !itr || !pa)
+ return -EINVAL;
+ *pa = NULL;
+
+ if (!itr->head)
+ FDISK_ITER_INIT(itr, &tb->parts);
+ if (itr->p != itr->head) {
+ FDISK_ITER_ITERATE(itr, *pa, struct fdisk_partition, parts);
+ rc = 0;
+ }
+
+ return rc;
+}
+
+struct fdisk_partition *fdisk_table_get_partition(
+ struct fdisk_table *tb,
+ size_t n)
+{
+ struct fdisk_partition *pa = NULL;
+ struct fdisk_iter itr;
+
+ if (!tb)
+ return NULL;
+
+ fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+
+ while (fdisk_table_next_partition(tb, &itr, &pa) == 0) {
+ if (n == 0)
+ return pa;
+ n--;
+ }
+
+ return NULL;
+}
+
+/**
+ * fdisk_table_add_partition
+ * @tb: tab pointer
+ * @pa: new entry
+ *
+ * Adds a new entry to table and increment @pa reference counter. Don't forget to
+ * use fdisk_unref_pa() after fdisk_table_add_partition() if you want to keep
+ * the @pa referenced by the table only.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int fdisk_table_add_partition(struct fdisk_table *tb, struct fdisk_partition *pa)
+{
+ assert(tb);
+ assert(pa);
+
+ if (!tb || !pa)
+ return -EINVAL;
+
+ fdisk_ref_partition(pa);
+ list_add_tail(&pa->parts, &tb->parts);
+ tb->nents++;
+
+ DBG(TAB, ul_debugobj(tb, "add entry %p [start=%ju, end=%ju, size=%ju, %s %s %s]",
+ pa,
+ (uintmax_t) fdisk_partition_get_start(pa),
+ (uintmax_t) fdisk_partition_get_end(pa),
+ (uintmax_t) fdisk_partition_get_size(pa),
+ fdisk_partition_is_freespace(pa) ? "freespace" : "",
+ fdisk_partition_is_nested(pa) ? "nested" : "",
+ fdisk_partition_is_container(pa) ? "container" : "primary"));
+ return 0;
+}
+
+/* inserts @pa after @poz */
+static int table_insert_partition(
+ struct fdisk_table *tb,
+ struct fdisk_partition *poz,
+ struct fdisk_partition *pa)
+{
+ assert(tb);
+ assert(pa);
+
+ fdisk_ref_partition(pa);
+ if (poz)
+ list_add(&pa->parts, &poz->parts);
+ else
+ list_add(&pa->parts, &tb->parts);
+ tb->nents++;
+
+ DBG(TAB, ul_debugobj(tb, "insert entry %p pre=%p [start=%ju, end=%ju, size=%ju, %s %s %s]",
+ pa, poz ? poz : NULL,
+ (uintmax_t) fdisk_partition_get_start(pa),
+ (uintmax_t) fdisk_partition_get_end(pa),
+ (uintmax_t) fdisk_partition_get_size(pa),
+ fdisk_partition_is_freespace(pa) ? "freespace" : "",
+ fdisk_partition_is_nested(pa) ? "nested" : "",
+ fdisk_partition_is_container(pa) ? "container" : ""));
+ return 0;
+}
+
+/**
+ * fdisk_table_remove_partition
+ * @tb: tab pointer
+ * @pa: new entry
+ *
+ * Removes the @pa from the table and de-increment reference counter of the @pa. The
+ * partition with zero reference counter will be deallocated. Don't forget to use
+ * fdisk_ref_partition() before call fdisk_table_remove_partition() if you want
+ * to use @pa later.
+ *
+ * Returns: 0 on success or negative number in case of error.
+ */
+int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_partition *pa)
+{
+ assert(tb);
+ assert(pa);
+
+ if (!tb || !pa)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "remove entry %p", pa));
+ list_del(&pa->parts);
+ INIT_LIST_HEAD(&pa->parts);
+
+ fdisk_unref_partition(pa);
+ tb->nents--;
+
+ return 0;
+}
+
+/**
+ * fdisk_get_partitions
+ * @cxt: fdisk context
+ * @tb: returns table
+ *
+ * This function adds partitions from disklabel to @table, it allocates a new
+ * table if if @table points to NULL.
+ *
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb)
+{
+ size_t i;
+
+ if (!cxt || !cxt->label || !tb)
+ return -EINVAL;
+ if (!cxt->label->op->get_part)
+ return -ENOSYS;
+
+ DBG(CXT, ul_debugobj(cxt, "get table"));
+
+ if (!*tb && !(*tb = fdisk_new_table()))
+ return -ENOMEM;
+
+ for (i = 0; i < cxt->label->nparts_max; i++) {
+ struct fdisk_partition *pa = NULL;
+
+ if (fdisk_get_partition(cxt, i, &pa) != 0)
+ continue;
+ if (fdisk_partition_is_used(pa))
+ fdisk_table_add_partition(*tb, pa);
+ fdisk_unref_partition(pa);
+ }
+
+ return 0;
+}
+
+static void debug_print_table(struct fdisk_table *tb)
+{
+ struct fdisk_iter itr;
+ struct fdisk_partition *pa;
+
+ fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+ while (fdisk_table_next_partition(tb, &itr, &pa) == 0)
+ ul_debugobj(tb, "partition %p [partno=%zu, start=%ju, end=%ju, size=%ju] ",
+ pa, pa->partno,
+ (uintmax_t) fdisk_partition_get_start(pa),
+ (uintmax_t) fdisk_partition_get_end(pa),
+ (uintmax_t) fdisk_partition_get_size(pa));
+
+}
+
+
+typedef int (*fdisk_partcmp_t)(struct fdisk_partition *, struct fdisk_partition *);
+
+static int cmp_parts_wrapper(struct list_head *a, struct list_head *b, void *data)
+{
+ struct fdisk_partition *pa = list_entry(a, struct fdisk_partition, parts),
+ *pb = list_entry(b, struct fdisk_partition, parts);
+
+ fdisk_partcmp_t cmp = (fdisk_partcmp_t) data;
+
+ return cmp(pa, pb);
+}
+
+
+/**
+ * fdisk_table_sort_partitions:
+ * @tb: table
+ * @cmp: compare function
+ *
+ * Sort partition in the table.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_table_sort_partitions(struct fdisk_table *tb,
+ int (*cmp)(struct fdisk_partition *,
+ struct fdisk_partition *))
+{
+ if (!tb)
+ return -EINVAL;
+
+ DBG(TAB, ul_debugobj(tb, "Before sort:"));
+ ON_DBG(TAB, debug_print_table(tb));
+
+ list_sort(&tb->parts, cmp_parts_wrapper, (void *) cmp);
+
+ DBG(TAB, ul_debugobj(tb, "After sort:"));
+ ON_DBG(TAB, debug_print_table(tb));
+
+ return 0;
+}
+
+/* allocates a new freespace description */
+static int new_freespace(struct fdisk_context *cxt,
+ fdisk_sector_t start,
+ fdisk_sector_t end,
+ struct fdisk_partition *parent,
+ struct fdisk_partition **pa)
+{
+ assert(cxt);
+ assert(pa);
+
+ *pa = NULL;
+
+ if (start == end)
+ return 0;
+ *pa = fdisk_new_partition();
+ if (!*pa)
+ return -ENOMEM;
+
+ assert(start);
+ assert(end);
+ assert(end > start);
+
+ (*pa)->freespace = 1;
+ (*pa)->start = fdisk_align_lba_in_range(cxt, start, start, end);
+ (*pa)->size = end - (*pa)->start + 1ULL;
+
+ if (parent)
+ (*pa)->parent_partno = parent->partno;
+ return 0;
+}
+
+/* add freespace description to the right place within @tb */
+static int table_add_freespace(
+ struct fdisk_context *cxt,
+ struct fdisk_table *tb,
+ fdisk_sector_t start,
+ fdisk_sector_t end,
+ struct fdisk_partition *parent)
+{
+ struct fdisk_partition *pa, *x, *real_parent = NULL, *best = NULL;
+ struct fdisk_iter itr;
+ int rc = 0;
+
+ assert(tb);
+
+ rc = new_freespace(cxt, start, end, parent, &pa);
+ if (rc)
+ return -ENOMEM;
+ if (!pa)
+ return 0;
+
+ assert(fdisk_partition_has_start(pa));
+ assert(fdisk_partition_has_end(pa));
+
+ DBG(TAB, ul_debugobj(tb, "adding freespace"));
+
+ fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+ if (parent && fdisk_partition_has_partno(parent)) {
+ while (fdisk_table_next_partition(tb, &itr, &x) == 0) {
+ if (!fdisk_partition_has_partno(x))
+ continue;
+ if (x->partno == parent->partno) {
+ real_parent = x;
+ break;
+ }
+ }
+ if (!real_parent) {
+ DBG(TAB, ul_debugobj(tb, "not found freespace parent (partno=%zu)",
+ parent->partno));
+ fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+ }
+ }
+
+ while (fdisk_table_next_partition(tb, &itr, &x) == 0) {
+ fdisk_sector_t end, best_end = 0;
+
+ if (!fdisk_partition_has_end(x))
+ continue;
+
+ end = fdisk_partition_get_end(x);
+ if (best)
+ best_end = fdisk_partition_get_end(best);
+
+ if (end < pa->start && (!best || best_end < end))
+ best = x;
+ }
+
+ if (!best && real_parent)
+ best = real_parent;
+ rc = table_insert_partition(tb, best, pa);
+
+ fdisk_unref_partition(pa);
+
+ DBG(TAB, ul_debugobj(tb, "adding freespace DONE [rc=%d]", rc));
+ return rc;
+}
+
+/* analyze @cont(ainer) in @parts and add all detected freespace into @tb, note
+ * that @parts has to be sorted by partition starts */
+static int check_container_freespace(struct fdisk_context *cxt,
+ struct fdisk_table *parts,
+ struct fdisk_table *tb,
+ struct fdisk_partition *cont)
+{
+ struct fdisk_iter itr;
+ struct fdisk_partition *pa;
+ fdisk_sector_t x, last, grain, lastplusoff;
+ int rc = 0;
+
+ assert(cxt);
+ assert(parts);
+ assert(tb);
+ assert(cont);
+ assert(fdisk_partition_has_start(cont));
+
+ DBG(TAB, ul_debugobj(tb, "analyze container 0x%p", cont));
+
+ last = fdisk_partition_get_start(cont);
+ grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1;
+ fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+
+ DBG(CXT, ul_debugobj(cxt, "initialized: last=%ju, grain=%ju", last, grain));
+
+ while (fdisk_table_next_partition(parts, &itr, &pa) == 0) {
+
+ DBG(CXT, ul_debugobj(cxt, "partno=%zu, start=%ju", pa->partno, pa->start));
+
+ if (!pa->used || !fdisk_partition_is_nested(pa)
+ || !fdisk_partition_has_start(pa))
+ continue;
+
+ DBG(CXT, ul_debugobj(cxt, "freespace container analyze: partno=%zu, start=%ju, end=%ju",
+ pa->partno,
+ (uintmax_t) fdisk_partition_get_start(pa),
+ (uintmax_t) fdisk_partition_get_end(pa)));
+
+ lastplusoff = last + cxt->first_lba;
+ if (pa->start > lastplusoff && pa->start - lastplusoff > grain)
+ rc = table_add_freespace(cxt, tb, lastplusoff, pa->start, cont);
+ if (rc)
+ goto done;
+ last = fdisk_partition_get_end(pa);
+ }
+
+ /* free-space remaining in extended partition */
+ x = fdisk_partition_get_start(cont) + fdisk_partition_get_size(cont) - 1;
+ lastplusoff = last + cxt->first_lba;
+ if (lastplusoff < x && x - lastplusoff > grain) {
+ DBG(TAB, ul_debugobj(tb, "add remaining space in container 0x%p", cont));
+ rc = table_add_freespace(cxt, tb, lastplusoff, x, cont);
+ }
+
+done:
+ DBG(TAB, ul_debugobj(tb, "analyze container 0x%p DONE [rc=%d]", cont, rc));
+ return rc;
+}
+
+
+/**
+ * fdisk_get_freespaces
+ * @cxt: fdisk context
+ * @tb: returns table
+ *
+ * This function adds freespace (described by fdisk_partition) to @table, it
+ * allocates a new table if the @table points to NULL.
+ *
+ * Note that free space smaller than grain (see fdisk_get_grain()) is ignored.
+
+ * Returns: 0 on success, otherwise, a corresponding error.
+ */
+int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb)
+{
+ int rc = 0;
+ fdisk_sector_t last, grain;
+ struct fdisk_table *parts = NULL;
+ struct fdisk_partition *pa;
+ struct fdisk_iter itr;
+
+ DBG(CXT, ul_debugobj(cxt, "get freespace"));
+
+ if (!cxt || !cxt->label || !tb)
+ return -EINVAL;
+ if (!*tb && !(*tb = fdisk_new_table()))
+ return -ENOMEM;
+
+ rc = fdisk_get_partitions(cxt, &parts);
+ if (rc)
+ goto done;
+
+ fdisk_table_sort_partitions(parts, fdisk_partition_cmp_start);
+ fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+ last = cxt->first_lba;
+ grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1;
+
+ DBG(CXT, ul_debugobj(cxt, "initialized: last=%ju, grain=%ju", last, grain));
+
+ /* analyze gaps between partitions */
+ while (rc == 0 && fdisk_table_next_partition(parts, &itr, &pa) == 0) {
+
+ DBG(CXT, ul_debugobj(cxt, "partno=%zu, start=%ju", pa->partno, pa->start));
+
+ if (!pa->used || pa->wholedisk || fdisk_partition_is_nested(pa)
+ || !fdisk_partition_has_start(pa))
+ continue;
+ DBG(CXT, ul_debugobj(cxt, "freespace analyze: partno=%zu, start=%ju, end=%ju",
+ pa->partno,
+ (uintmax_t) fdisk_partition_get_start(pa),
+ (uintmax_t) fdisk_partition_get_end(pa)));
+ if (last + grain <= pa->start) {
+ rc = table_add_freespace(cxt, *tb,
+ last + (last > cxt->first_lba ? 1 : 0),
+ pa->start - 1, NULL);
+ }
+ /* add gaps between logical partitions */
+ if (fdisk_partition_is_container(pa))
+ rc = check_container_freespace(cxt, parts, *tb, pa);
+ last = fdisk_partition_get_end(pa);
+ }
+
+ /* add free-space behind last partition to the end of the table (so
+ * don't use table_add_freespace()) */
+ if (rc == 0 && last + grain < cxt->total_sectors - 1) {
+ DBG(CXT, ul_debugobj(cxt, "freespace behind last partition detected"));
+ rc = new_freespace(cxt,
+ last + (last > cxt->first_lba ? 1 : 0),
+ cxt->last_lba, NULL, &pa);
+ if (pa) {
+ fdisk_table_add_partition(*tb, pa);
+ fdisk_unref_partition(pa);
+ }
+ }
+
+done:
+ fdisk_unref_table(parts);
+
+ DBG(CXT, ul_debugobj(cxt, "get freespace DONE [rc=%d]", rc));
+ return rc;
+}
+
+/**
+ * fdisk_table_wrong_order:
+ * @tb: table
+ *
+ * Returns: 1 of the table is not in disk order
+ */
+int fdisk_table_wrong_order(struct fdisk_table *tb)
+{
+ struct fdisk_partition *pa;
+ struct fdisk_iter itr;
+ fdisk_sector_t last = 0;
+
+ DBG(TAB, ul_debugobj(tb, "wrong older check"));
+
+ fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+ while (tb && fdisk_table_next_partition(tb, &itr, &pa) == 0) {
+ if (!fdisk_partition_has_start(pa))
+ continue;
+ if (pa->start < last)
+ return 1;
+ last = pa->start;
+ }
+ return 0;
+}
+
+/**
+ * fdisk_apply_table:
+ * @cxt: context
+ * @tb: table
+ *
+ * Add partitions from table @tb to the in-memory disk label. See
+ * fdisk_add_partition(), fdisk_delete_all_partitions(). The partitons
+ * that does not define start (or does not follow the default start)
+ * are ingored.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int fdisk_apply_table(struct fdisk_context *cxt, struct fdisk_table *tb)
+{
+ struct fdisk_partition *pa;
+ struct fdisk_iter itr;
+ int rc = 0;
+
+ assert(cxt);
+ assert(tb);
+
+ DBG(TAB, ul_debugobj(tb, "applying to context %p", cxt));
+
+ fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
+ while (tb && fdisk_table_next_partition(tb, &itr, &pa) == 0) {
+ if (!fdisk_partition_has_start(pa) && !pa->start_follow_default)
+ continue;
+ rc = fdisk_add_partition(cxt, pa, NULL);
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
diff --git a/libblkid/libfdisk/src/test.c b/libblkid/libfdisk/src/test.c
new file mode 100644
index 000000000..31ed7e03a
--- /dev/null
+++ b/libblkid/libfdisk/src/test.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Routines for TEST_PROGRAMs
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#ifndef TEST_PROGRAM
+#define TEST_PROGRAM
+#endif
+
+#include "fdiskP.h"
+
+int fdisk_run_test(struct fdisk_test *tests, int argc, char *argv[])
+{
+ int rc = -1;
+ struct fdisk_test *ts;
+
+ assert(tests);
+ assert(argc);
+ assert(argv);
+
+ if (argc < 2 ||
+ strcmp(argv[1], "--help") == 0 ||
+ strcmp(argv[1], "-h") == 0)
+ goto usage;
+
+ fdisk_init_debug(0);
+
+ for (ts = tests; ts->name; ts++) {
+ if (strcmp(ts->name, argv[1]) == 0) {
+ rc = ts->body(ts, argc - 1, argv + 1);
+ if (rc)
+ printf("FAILED [rc=%d]", rc);
+ break;
+ }
+ }
+
+ if (rc < 0 && ts->name == NULL)
+ goto usage;
+
+ return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+usage:
+ printf("\nUsage:\n\t%s <test> [testoptions]\nTests:\n",
+ program_invocation_short_name);
+ for (ts = tests; ts->name; ts++) {
+ printf("\t%-15s", ts->name);
+ if (ts->usage)
+ printf(" %s\n", ts->usage);
+ }
+ printf("\n");
+ return EXIT_FAILURE;
+}
diff --git a/libblkid/libfdisk/src/utils.c b/libblkid/libfdisk/src/utils.c
new file mode 100644
index 000000000..482a3062d
--- /dev/null
+++ b/libblkid/libfdisk/src/utils.c
@@ -0,0 +1,154 @@
+
+#include "fdiskP.h"
+#include "pathnames.h"
+
+#include <ctype.h>
+
+/**
+ * SECTION: utils
+ * @title: Utils
+ * @short_description: misc fdisk functions
+ */
+
+/*
+ * Zeros in-memory first sector buffer
+ */
+int fdisk_init_firstsector_buffer(struct fdisk_context *cxt)
+{
+ if (!cxt)
+ return -EINVAL;
+
+ if (!cxt->firstsector || cxt->firstsector_bufsz != cxt->sector_size) {
+ /* Let's allocate a new buffer if no allocated yet, or the
+ * current buffer has incorrect size */
+ if (!cxt->parent || cxt->parent->firstsector != cxt->firstsector)
+ free(cxt->firstsector);
+
+ DBG(CXT, ul_debugobj(cxt, "initialize in-memory first sector "
+ "buffer [sector_size=%lu]", cxt->sector_size));
+ cxt->firstsector = calloc(1, cxt->sector_size);
+ if (!cxt->firstsector)
+ return -ENOMEM;
+
+ cxt->firstsector_bufsz = cxt->sector_size;
+ return 0;
+ }
+
+ DBG(CXT, ul_debugobj(cxt, "zeroize in-memory first sector buffer"));
+ memset(cxt->firstsector, 0, cxt->firstsector_bufsz);
+ return 0;
+}
+
+int fdisk_read_firstsector(struct fdisk_context *cxt)
+{
+ ssize_t r;
+ int rc;
+
+ assert(cxt);
+ assert(cxt->sector_size);
+
+ rc = fdisk_init_firstsector_buffer(cxt);
+ if (rc)
+ return rc;
+
+ assert(cxt->sector_size == cxt->firstsector_bufsz);
+
+ DBG(CXT, ul_debugobj(cxt, "reading first sector "
+ "buffer [sector_size=%lu]", cxt->sector_size));
+
+ r = lseek(cxt->dev_fd, 0, SEEK_SET);
+ if (r == -1)
+ {
+ DBG(CXT, ul_debugobj(cxt, "failed to seek to first sector %m"));
+ return -errno;
+ }
+
+ r = read(cxt->dev_fd, cxt->firstsector, cxt->sector_size);
+
+ if (r != cxt->sector_size) {
+ if (!errno)
+ errno = EINVAL; /* probably too small file/device */
+ DBG(CXT, ul_debugobj(cxt, "failed to read first sector %m"));
+ return -errno;
+ }
+
+ return 0;
+}
+
+/**
+ * fdisk_partname:
+ * @dev: device name
+ * @partno: partition name
+ *
+ * Return: allocated buffer with partition name, use free() to deallocate.
+ */
+char *fdisk_partname(const char *dev, size_t partno)
+{
+ char *res = NULL;
+ const char *p = "";
+ int w = 0;
+
+ if (!dev || !*dev) {
+ if (asprintf(&res, "%zd", partno) > 0)
+ return res;
+ return NULL;
+ }
+
+ w = strlen(dev);
+ if (isdigit(dev[w - 1]))
+#ifdef __GNU__
+ p = "s";
+#else
+ p = "p";
+#endif
+
+ /* devfs kludge - note: fdisk partition names are not supposed
+ to equal kernel names, so there is no reason to do this */
+ if (strcmp(dev + w - 4, "disc") == 0) {
+ w -= 4;
+ p = "part";
+ }
+
+ /* udev names partitions by appending -partN
+ e.g. ata-SAMSUNG_SV8004H_0357J1FT712448-part1 */
+ if ((strncmp(dev, _PATH_DEV_BYID, sizeof(_PATH_DEV_BYID) - 1) == 0) ||
+ strncmp(dev, _PATH_DEV_BYPATH, sizeof(_PATH_DEV_BYPATH) - 1) == 0) {
+ p = "-part";
+ }
+
+ if (asprintf(&res, "%.*s%s%zu", w, dev, p, partno) > 0)
+ return res;
+
+ return NULL;
+}
+
+#ifdef TEST_PROGRAM
+struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt) { return NULL; }
+struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt) { return NULL; }
+
+int test_partnames(struct fdisk_test *ts, int argc, char *argv[])
+{
+ size_t i;
+ const char *disk = argv[1];
+
+ for (i = 0; i < 5; i++) {
+ char *p = fdisk_partname(disk, i + 1);
+ if (p)
+ printf("%zu: '%s'\n", i + 1, p);
+ free(p);
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct fdisk_test tss[] = {
+ { "--partnames", test_partnames, "<diskname>" },
+ { NULL }
+ };
+
+ return fdisk_run_test(tss, argc, argv);
+}
+
+#endif
diff --git a/libblkid/libuuid/COPYING b/libblkid/libuuid/COPYING
new file mode 100644
index 000000000..0e902cf8c
--- /dev/null
+++ b/libblkid/libuuid/COPYING
@@ -0,0 +1,5 @@
+This library is free software; you can redistribute it and/or
+modify it under the terms of the Modified BSD License.
+
+The complete text of the license is available in the
+../Documentation/licenses/COPYING.BSD-3 file.
diff --git a/libblkid/libuuid/Makemodule.am b/libblkid/libuuid/Makemodule.am
new file mode 100644
index 000000000..166be5c26
--- /dev/null
+++ b/libblkid/libuuid/Makemodule.am
@@ -0,0 +1,10 @@
+if BUILD_LIBUUID
+
+include libuuid/man/Makemodule.am
+include libuuid/src/Makemodule.am
+
+pkgconfig_DATA += libuuid/uuid.pc
+PATHFILES += libuuid/uuid.pc
+EXTRA_DIST += libuuid/COPYING
+
+endif # BUILD_LIBUUID
diff --git a/libblkid/libuuid/man/.gitignore b/libblkid/libuuid/man/.gitignore
new file mode 100644
index 000000000..7957ad2cc
--- /dev/null
+++ b/libblkid/libuuid/man/.gitignore
@@ -0,0 +1,3 @@
+uuid_generate_random.3
+uuid_generate_time.3
+uuid_generate_time_safe.3
diff --git a/libblkid/libuuid/man/Makemodule.am b/libblkid/libuuid/man/Makemodule.am
new file mode 100644
index 000000000..81287d5c7
--- /dev/null
+++ b/libblkid/libuuid/man/Makemodule.am
@@ -0,0 +1,14 @@
+
+dist_man_MANS += \
+ libuuid/man/uuid.3 \
+ libuuid/man/uuid_clear.3 \
+ libuuid/man/uuid_compare.3 \
+ libuuid/man/uuid_copy.3 \
+ libuuid/man/uuid_generate.3 \
+ libuuid/man/uuid_is_null.3 \
+ libuuid/man/uuid_parse.3 \
+ libuuid/man/uuid_time.3 \
+ libuuid/man/uuid_unparse.3 \
+ libuuid/man/uuid_generate_random.3 \
+ libuuid/man/uuid_generate_time.3 \
+ libuuid/man/uuid_generate_time_safe.3
diff --git a/libblkid/libuuid/man/uuid.3 b/libblkid/libuuid/man/uuid.3
new file mode 100644
index 000000000..37b04995e
--- /dev/null
+++ b/libblkid/libuuid/man/uuid.3
@@ -0,0 +1,65 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote
+.\" products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+.\" USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid \- DCE compatible Universally Unique Identifier library
+.SH SYNOPSIS
+.B #include <uuid.h>
+.SH DESCRIPTION
+The UUID library is used to generate unique identifiers for objects
+that may be accessible beyond the local system. This library
+generates UUIDs compatible with those created by the Open Software
+Foundation (OSF) Distributed Computing Environment (DCE) utility
+.BR uuidgen .
+.sp
+The UUIDs generated by this library can be reasonably expected to be
+unique within a system, and unique across all systems. They could
+be used, for instance, to generate unique HTTP cookies across multiple
+web servers without communication between the servers, and without fear
+of a name clash.
+.SH "CONFORMING TO"
+OSF DCE 1.1
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.SH "SEE ALSO"
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_time (3),
+.BR uuid_unparse (3)
diff --git a/libblkid/libuuid/man/uuid_clear.3 b/libblkid/libuuid/man/uuid_clear.3
new file mode 100644
index 000000000..70fca02b7
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_clear.3
@@ -0,0 +1,62 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote
+.\" products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+.\" USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_CLEAR 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_clear \- reset value of UUID variable to the NULL value
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "void uuid_clear(uuid_t " uu );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_clear
+function sets the value of the supplied uuid variable
+.I uu
+to the NULL value.
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.na
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
+.ad
diff --git a/libblkid/libuuid/man/uuid_compare.3 b/libblkid/libuuid/man/uuid_compare.3
new file mode 100644
index 000000000..f91181a4e
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_compare.3
@@ -0,0 +1,68 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote
+.\" products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+.\" USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_COMPARE 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_compare \- compare whether two UUIDs are the same
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "int uuid_compare(uuid_t " uu1 ", uuid_t " uu2)
+.fi
+.SH DESCRIPTION
+The
+.B uuid_compare
+function compares the two supplied uuid variables
+.IR uu1 " and " uu2
+to each other.
+.SH RETURN VALUE
+Returns an integer less than, equal to, or greater than zero if
+.I uu1
+is found, respectively, to be lexicographically less than, equal, or
+greater than
+.IR uu2 .
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.na
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
+.ad
diff --git a/libblkid/libuuid/man/uuid_copy.3 b/libblkid/libuuid/man/uuid_copy.3
new file mode 100644
index 000000000..5159fa662
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_copy.3
@@ -0,0 +1,64 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote
+.\" products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+.\" USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_COPY 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_copy \- copy a UUID value
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "void uuid_copy(uuid_t " dst ", uuid_t " src);
+.fi
+.SH DESCRIPTION
+The
+.B uuid_copy
+function copies the UUID variable
+.IR src " to " dst .
+.SH RETURN VALUE
+The copied UUID is returned in the location pointed to by
+.IR dst .
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.na
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
+.ad
diff --git a/libblkid/libuuid/man/uuid_generate.3 b/libblkid/libuuid/man/uuid_generate.3
new file mode 100644
index 000000000..19904d7de
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_generate.3
@@ -0,0 +1,126 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote
+.\" products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+.\" USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_GENERATE 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_generate, uuid_generate_random, uuid_generate_time,
+uuid_generate_time_safe \- create a new unique UUID value
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "void uuid_generate(uuid_t " out );
+.BI "void uuid_generate_random(uuid_t " out );
+.BI "void uuid_generate_time(uuid_t " out );
+.BI "int uuid_generate_time_safe(uuid_t " out );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_generate
+function creates a new universally unique identifier (UUID). The uuid will
+be generated based on high-quality randomness from
+.IR /dev/urandom ,
+if available. If it is not available, then
+.B uuid_generate
+will use an alternative algorithm which uses the current time, the
+local ethernet MAC address (if available), and random data generated
+using a pseudo-random generator.
+.sp
+The
+.B uuid_generate_random
+function forces the use of the all-random UUID format, even if
+a high-quality random number generator (i.e.,
+.IR /dev/urandom )
+is not available, in which case a pseudo-random
+generator will be substituted. Note that the use of a pseudo-random
+generator may compromise the uniqueness of UUIDs
+generated in this fashion.
+.sp
+The
+.B uuid_generate_time
+function forces the use of the alternative algorithm which uses the
+current time and the local ethernet MAC address (if available).
+This algorithm used to be the default one used to generate UUID, but
+because of the use of the ethernet MAC address, it can leak
+information about when and where the UUID was generated. This can cause
+privacy problems in some applications, so the
+.B uuid_generate
+function only uses this algorithm if a high-quality source of
+randomness is not available. To guarantee uniqueness of UUIDs generated
+by concurrently running processes, the uuid library uses global
+clock state counter (if the process has permissions to gain exclusive access
+to this file) and/or the
+.B uuidd
+daemon, if it is running already or can be spawned by the process (if
+installed and the process has enough permissions to run it). If neither of
+these two synchronization mechanisms can be used, it is theoretically possible
+that two concurrently running processes obtain the same UUID(s). To tell
+whether the UUID has been generated in a safe manner, use
+.BR uuid_generate_time_safe .
+.sp
+The
+.B uuid_generate_time_safe
+is similar to
+.BR uuid_generate_time ,
+except that it returns a value which denotes whether any of the synchronization
+mechanisms (see above) has been used.
+.sp
+The UUID is 16 bytes (128 bits) long, which gives approximately 3.4x10^38
+unique values (there are approximately 10^80 elementary particles in
+the universe according to Carl Sagan's
+.IR Cosmos ).
+The new UUID can reasonably be considered unique among all UUIDs created
+on the local system, and among UUIDs created on other systems in the past
+and in the future.
+.SH RETURN VALUE
+The newly created UUID is returned in the memory location pointed to by
+.IR out .
+.B uuid_generate_time_safe
+returns zero if the UUID has been generated in a safe manner, \-1 otherwise.
+.SH "CONFORMING TO"
+OSF DCE 1.1
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuidgen (1),
+.BR uuidd (8),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_time (3),
+.BR uuid_unparse (3)
diff --git a/libblkid/libuuid/man/uuid_is_null.3 b/libblkid/libuuid/man/uuid_is_null.3
new file mode 100644
index 000000000..86a7a50fe
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_is_null.3
@@ -0,0 +1,64 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote
+.\" products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+.\" USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_IS_NULL 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_is_null \- compare the value of the UUID to the NULL value
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "int uuid_is_null(uuid_t " uu );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_is_null
+function compares the value of the supplied UUID variable
+.I uu
+to the NULL value. If the value is equal to the NULL UUID, 1 is returned,
+otherwise 0 is returned.
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.na
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_time (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
+.ad
diff --git a/libblkid/libuuid/man/uuid_parse.3 b/libblkid/libuuid/man/uuid_parse.3
new file mode 100644
index 000000000..31a59267a
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_parse.3
@@ -0,0 +1,73 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote
+.\" products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+.\" USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_PARSE 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_parse \- convert an input UUID string into binary representation
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "int uuid_parse( char *" in ", uuid_t " uu );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_parse
+function converts the UUID string given by
+.I in
+into the binary representation. The input UUID is a string of the form
+1b4e28ba\-2fa1\-11d2\-883f\-b9a761bde3fb (in
+.BR printf (3)
+format "%08x\-%04x\-%04x\-%04x\-%012x", 36 bytes plus the trailing '\e0').
+.SH RETURN VALUE
+Upon successfully parsing the input string, 0 is returned, and the UUID is
+stored in the location pointed to by
+.IR uu ,
+otherwise \-1 is returned.
+.SH "CONFORMING TO"
+OSF DCE 1.1
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.na
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_time (3),
+.BR uuid_unparse (3)
+.ad
diff --git a/libblkid/libuuid/man/uuid_time.3 b/libblkid/libuuid/man/uuid_time.3
new file mode 100644
index 000000000..483676b5b
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_time.3
@@ -0,0 +1,78 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote
+.\" products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+.\" USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_TIME 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_time \- extract the time at which the UUID was created
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "time_t uuid_time(uuid_t " uu ", struct timeval *" ret_tv )
+.fi
+.SH DESCRIPTION
+The
+.B uuid_time
+function extracts the time at which the supplied time-based UUID
+.I uu
+was created. Note that the UUID creation time is only encoded within
+certain types of UUIDs. This function can only reasonably expect to
+extract the creation time for UUIDs created with the
+.BR uuid_generate_time (3)
+and
+.BR uuid_generate_time_safe (3)
+functions. It may or may not work with UUIDs created by other mechanisms.
+.SH "RETURN VALUES"
+The time at which the UUID was created, in seconds since January 1, 1970 GMT
+(the epoch), is returned (see
+.BR time "(2))."
+The time at which the UUID was created, in seconds and microseconds since
+the epoch, is also stored in the location pointed to by
+.I ret_tv
+(see
+.BR gettimeofday "(2))."
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.na
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3),
+.BR uuid_unparse (3)
+.ad
diff --git a/libblkid/libuuid/man/uuid_unparse.3 b/libblkid/libuuid/man/uuid_unparse.3
new file mode 100644
index 000000000..1e0116d7a
--- /dev/null
+++ b/libblkid/libuuid/man/uuid_unparse.3
@@ -0,0 +1,81 @@
+.\" Copyright 1999 Andreas Dilger (adilger@enel.ucalgary.ca)
+.\"
+.\" %Begin-Header%
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote
+.\" products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+.\" WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+.\" USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\" %End-Header%
+.\"
+.\" Created Wed Mar 10 17:42:12 1999, Andreas Dilger
+.TH UUID_UNPARSE 3 "May 2009" "util-linux" "Libuuid API"
+.SH NAME
+uuid_unparse \- convert an UUID from binary representation to a string
+.SH SYNOPSIS
+.nf
+.B #include <uuid.h>
+.sp
+.BI "void uuid_unparse(uuid_t " uu ", char *" out );
+.BI "void uuid_unparse_upper(uuid_t " uu ", char *" out );
+.BI "void uuid_unparse_lower(uuid_t " uu ", char *" out );
+.fi
+.SH DESCRIPTION
+The
+.B uuid_unparse
+function converts the supplied UUID
+.I uu
+from the binary representation into a 36-byte string (plus tailing '\e0')
+of the form 1b4e28ba\-2fa1\-11d2\-883f\-0016d3cca427 and stores this
+value in the character string pointed to by
+.IR out .
+The case of the hex digits returned by
+.B uuid_unparse
+may be upper or lower case, and is
+dependent on the system-dependent local default.
+.PP
+If the case of the
+hex digits is important then the functions
+.B uuid_unparse_upper
+and
+.B uuid_unparse_lower
+may be used.
+.SH "CONFORMING TO"
+OSF DCE 1.1
+.SH AUTHOR
+Theodore Y.\& Ts'o
+.SH AVAILABILITY
+.B libuuid
+is part of the util-linux package since version 2.15.1 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.na
+.SH "SEE ALSO"
+.BR uuid (3),
+.BR uuid_clear (3),
+.BR uuid_compare (3),
+.BR uuid_copy (3),
+.BR uuid_generate (3),
+.BR uuid_time (3),
+.BR uuid_is_null (3),
+.BR uuid_parse (3)
+.ad
diff --git a/libblkid/libuuid/src/Makemodule.am b/libblkid/libuuid/src/Makemodule.am
new file mode 100644
index 000000000..061aff21d
--- /dev/null
+++ b/libblkid/libuuid/src/Makemodule.am
@@ -0,0 +1,61 @@
+
+check_PROGRAMS += test_uuid
+test_uuid_SOURCES = libuuid/src/test_uuid.c
+test_uuid_LDADD = libuuid.la $(SOCKET_LIBS)
+test_uuid_CFLAGS = -I$(ul_libuuid_incdir)
+
+# includes
+uuidincdir = $(includedir)/uuid
+uuidinc_HEADERS = libuuid/src/uuid.h
+
+usrlib_exec_LTLIBRARIES += libuuid.la
+
+libuuid_la_SOURCES = \
+ libuuid/src/clear.c \
+ libuuid/src/compare.c \
+ libuuid/src/copy.c \
+ libuuid/src/gen_uuid.c \
+ libuuid/src/isnull.c \
+ libuuid/src/pack.c \
+ libuuid/src/parse.c \
+ libuuid/src/unpack.c \
+ libuuid/src/unparse.c \
+ libuuid/src/uuidd.h \
+ libuuid/src/uuidd.h \
+ libuuid/src/uuidP.h \
+ libuuid/src/uuid_time.c \
+ $(uuidinc_HEADERS) \
+ lib/randutils.c
+
+libuuid_la_DEPENDENCIES = libuuid/src/libuuid.sym
+libuuid_la_LIBADD = $(SOCKET_LIBS)
+
+libuuid_la_CFLAGS = \
+ $(SOLIB_CFLAGS) \
+ -I$(ul_libuuid_incdir) \
+ -Ilibuuid/src
+
+libuuid_la_LDFLAGS = \
+ $(SOLIB_LDFLAGS) \
+ -Wl,--version-script=$(top_srcdir)/libuuid/src/libuuid.sym \
+ -version-info $(LIBUUID_VERSION_INFO)
+
+EXTRA_DIST += libuuid/src/libuuid.sym
+
+# move lib from $(usrlib_execdir) to $(libdir) if needed
+install-exec-hook-libuuid:
+ if test "$(usrlib_execdir)" != "$(libdir)" -a -f "$(DESTDIR)$(usrlib_execdir)/libuuid.so"; then \
+ mkdir -p $(DESTDIR)$(libdir); \
+ mv $(DESTDIR)$(usrlib_execdir)/libuuid.so.* $(DESTDIR)$(libdir); \
+ so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/libuuid.so); \
+ so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \
+ (cd $(DESTDIR)$(usrlib_execdir) && \
+ rm -f libuuid.so && \
+ $(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name libuuid.so); \
+ fi
+
+uninstall-hook-libuuid:
+ rm -f $(DESTDIR)$(libdir)/libuuid.so*
+
+INSTALL_EXEC_HOOKS += install-exec-hook-libuuid
+UNINSTALL_HOOKS += uninstall-hook-libuuid
diff --git a/libblkid/libuuid/src/clear.c b/libblkid/libuuid/src/clear.c
new file mode 100644
index 000000000..2d91fee93
--- /dev/null
+++ b/libblkid/libuuid/src/clear.c
@@ -0,0 +1,43 @@
+/*
+ * clear.c -- Clear a UUID
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "string.h"
+
+#include "uuidP.h"
+
+void uuid_clear(uuid_t uu)
+{
+ memset(uu, 0, 16);
+}
+
diff --git a/libblkid/libuuid/src/compare.c b/libblkid/libuuid/src/compare.c
new file mode 100644
index 000000000..8f3437a2d
--- /dev/null
+++ b/libblkid/libuuid/src/compare.c
@@ -0,0 +1,55 @@
+/*
+ * compare.c --- compare whether or not two UUIDs are the same
+ *
+ * Returns 0 if the two UUIDs are different, and 1 if they are the same.
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "uuidP.h"
+#include <string.h>
+
+#define UUCMP(u1,u2) if (u1 != u2) return((u1 < u2) ? -1 : 1);
+
+int uuid_compare(const uuid_t uu1, const uuid_t uu2)
+{
+ struct uuid uuid1, uuid2;
+
+ uuid_unpack(uu1, &uuid1);
+ uuid_unpack(uu2, &uuid2);
+
+ UUCMP(uuid1.time_low, uuid2.time_low);
+ UUCMP(uuid1.time_mid, uuid2.time_mid);
+ UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version);
+ UUCMP(uuid1.clock_seq, uuid2.clock_seq);
+ return memcmp(uuid1.node, uuid2.node, 6);
+}
+
diff --git a/libblkid/libuuid/src/copy.c b/libblkid/libuuid/src/copy.c
new file mode 100644
index 000000000..ead33aa26
--- /dev/null
+++ b/libblkid/libuuid/src/copy.c
@@ -0,0 +1,45 @@
+/*
+ * copy.c --- copy UUIDs
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "uuidP.h"
+
+void uuid_copy(uuid_t dst, const uuid_t src)
+{
+ unsigned char *cp1;
+ const unsigned char *cp2;
+ int i;
+
+ for (i=0, cp1 = dst, cp2 = src; i < 16; i++)
+ *cp1++ = *cp2++;
+}
diff --git a/libblkid/libuuid/src/gen_uuid.c b/libblkid/libuuid/src/gen_uuid.c
new file mode 100644
index 000000000..eb793391c
--- /dev/null
+++ b/libblkid/libuuid/src/gen_uuid.c
@@ -0,0 +1,545 @@
+/*
+ * gen_uuid.c --- generate a DCE-compatible uuid
+ *
+ * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0500
+#include <windows.h>
+#define UUID MYUUID
+#endif
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/stat.h>
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NET_IF_DL_H
+#include <net/if_dl.h>
+#endif
+#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)
+#include <sys/syscall.h>
+#endif
+
+#include "all-io.h"
+#include "uuidP.h"
+#include "uuidd.h"
+#include "randutils.h"
+#include "c.h"
+
+#ifdef HAVE_TLS
+#define THREAD_LOCAL static __thread
+#else
+#define THREAD_LOCAL static
+#endif
+
+#ifdef _WIN32
+static void gettimeofday (struct timeval *tv, void *dummy)
+{
+ FILETIME ftime;
+ uint64_t n;
+
+ GetSystemTimeAsFileTime (&ftime);
+ n = (((uint64_t) ftime.dwHighDateTime << 32)
+ + (uint64_t) ftime.dwLowDateTime);
+ if (n) {
+ n /= 10;
+ n -= ((369 * 365 + 89) * (uint64_t) 86400) * 1000000;
+ }
+
+ tv->tv_sec = n / 1000000;
+ tv->tv_usec = n % 1000000;
+}
+
+static int getuid (void)
+{
+ return 1;
+}
+#endif
+
+/*
+ * Get the ethernet hardware address, if we can find it...
+ *
+ * XXX for a windows version, probably should use GetAdaptersInfo:
+ * http://www.codeguru.com/cpp/i-n/network/networkinformation/article.php/c5451
+ * commenting out get_node_id just to get gen_uuid to compile under windows
+ * is not the right way to go!
+ */
+static int get_node_id(unsigned char *node_id)
+{
+#ifdef HAVE_NET_IF_H
+ int sd;
+ struct ifreq ifr, *ifrp;
+ struct ifconf ifc;
+ char buf[1024];
+ int n, i;
+ unsigned char *a;
+#ifdef HAVE_NET_IF_DL_H
+ struct sockaddr_dl *sdlp;
+#endif
+
+/*
+ * BSD 4.4 defines the size of an ifreq to be
+ * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
+ * However, under earlier systems, sa_len isn't present, so the size is
+ * just sizeof(struct ifreq)
+ */
+#ifdef HAVE_SA_LEN
+#define ifreq_size(i) max(sizeof(struct ifreq),\
+ sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
+#else
+#define ifreq_size(i) sizeof(struct ifreq)
+#endif /* HAVE_SA_LEN */
+
+ sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sd < 0) {
+ return -1;
+ }
+ memset(buf, 0, sizeof(buf));
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) {
+ close(sd);
+ return -1;
+ }
+ n = ifc.ifc_len;
+ for (i = 0; i < n; i+= ifreq_size(*ifrp) ) {
+ ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
+ strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ);
+#ifdef SIOCGIFHWADDR
+ if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
+ continue;
+ a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
+#else
+#ifdef SIOCGENADDR
+ if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
+ continue;
+ a = (unsigned char *) ifr.ifr_enaddr;
+#else
+#ifdef HAVE_NET_IF_DL_H
+ sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr;
+ if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6))
+ continue;
+ a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen];
+#else
+ /*
+ * XXX we don't have a way of getting the hardware
+ * address
+ */
+ close(sd);
+ return 0;
+#endif /* HAVE_NET_IF_DL_H */
+#endif /* SIOCGENADDR */
+#endif /* SIOCGIFHWADDR */
+ if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
+ continue;
+ if (node_id) {
+ memcpy(node_id, a, 6);
+ close(sd);
+ return 1;
+ }
+ }
+ close(sd);
+#endif
+ return 0;
+}
+
+/* Assume that the gettimeofday() has microsecond granularity */
+#define MAX_ADJUSTMENT 10
+
+/*
+ * Get clock from global sequence clock counter.
+ *
+ * Return -1 if the clock counter could not be opened/locked (in this case
+ * pseudorandom value is returned in @ret_clock_seq), otherwise return 0.
+ */
+static int get_clock(uint32_t *clock_high, uint32_t *clock_low,
+ uint16_t *ret_clock_seq, int *num)
+{
+ THREAD_LOCAL int adjustment = 0;
+ THREAD_LOCAL struct timeval last = {0, 0};
+ THREAD_LOCAL int state_fd = -2;
+ THREAD_LOCAL FILE *state_f;
+ THREAD_LOCAL uint16_t clock_seq;
+ struct timeval tv;
+ uint64_t clock_reg;
+ mode_t save_umask;
+ int len;
+ int ret = 0;
+
+ if (state_fd == -2) {
+ save_umask = umask(0);
+ state_fd = open(LIBUUID_CLOCK_FILE, O_RDWR|O_CREAT|O_CLOEXEC, 0660);
+ (void) umask(save_umask);
+ if (state_fd != -1) {
+ state_f = fdopen(state_fd, "r+" UL_CLOEXECSTR);
+ if (!state_f) {
+ close(state_fd);
+ state_fd = -1;
+ ret = -1;
+ }
+ }
+ else
+ ret = -1;
+ }
+ if (state_fd >= 0) {
+ rewind(state_f);
+ while (flock(state_fd, LOCK_EX) < 0) {
+ if ((errno == EAGAIN) || (errno == EINTR))
+ continue;
+ fclose(state_f);
+ close(state_fd);
+ state_fd = -1;
+ ret = -1;
+ break;
+ }
+ }
+ if (state_fd >= 0) {
+ unsigned int cl;
+ unsigned long tv1, tv2;
+ int a;
+
+ if (fscanf(state_f, "clock: %04x tv: %lu %lu adj: %d\n",
+ &cl, &tv1, &tv2, &a) == 4) {
+ clock_seq = cl & 0x3FFF;
+ last.tv_sec = tv1;
+ last.tv_usec = tv2;
+ adjustment = a;
+ }
+ }
+
+ if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
+ random_get_bytes(&clock_seq, sizeof(clock_seq));
+ clock_seq &= 0x3FFF;
+ gettimeofday(&last, 0);
+ last.tv_sec--;
+ }
+
+try_again:
+ gettimeofday(&tv, 0);
+ if ((tv.tv_sec < last.tv_sec) ||
+ ((tv.tv_sec == last.tv_sec) &&
+ (tv.tv_usec < last.tv_usec))) {
+ clock_seq = (clock_seq+1) & 0x3FFF;
+ adjustment = 0;
+ last = tv;
+ } else if ((tv.tv_sec == last.tv_sec) &&
+ (tv.tv_usec == last.tv_usec)) {
+ if (adjustment >= MAX_ADJUSTMENT)
+ goto try_again;
+ adjustment++;
+ } else {
+ adjustment = 0;
+ last = tv;
+ }
+
+ clock_reg = tv.tv_usec*10 + adjustment;
+ clock_reg += ((uint64_t) tv.tv_sec)*10000000;
+ clock_reg += (((uint64_t) 0x01B21DD2) << 32) + 0x13814000;
+
+ if (num && (*num > 1)) {
+ adjustment += *num - 1;
+ last.tv_usec += adjustment / 10;
+ adjustment = adjustment % 10;
+ last.tv_sec += last.tv_usec / 1000000;
+ last.tv_usec = last.tv_usec % 1000000;
+ }
+
+ if (state_fd >= 0) {
+ rewind(state_f);
+ len = fprintf(state_f,
+ "clock: %04x tv: %016lu %08lu adj: %08d\n",
+ clock_seq, last.tv_sec, last.tv_usec, adjustment);
+ fflush(state_f);
+ if (ftruncate(state_fd, len) < 0) {
+ fprintf(state_f, " \n");
+ fflush(state_f);
+ }
+ rewind(state_f);
+ flock(state_fd, LOCK_UN);
+ }
+
+ *clock_high = clock_reg >> 32;
+ *clock_low = clock_reg;
+ *ret_clock_seq = clock_seq;
+ return ret;
+}
+
+#if defined(HAVE_UUIDD) && defined(HAVE_SYS_UN_H)
+/*
+ * Try using the uuidd daemon to generate the UUID
+ *
+ * Returns 0 on success, non-zero on failure.
+ */
+static int get_uuid_via_daemon(int op, uuid_t out, int *num)
+{
+ char op_buf[64];
+ int op_len;
+ int s;
+ ssize_t ret;
+ int32_t reply_len = 0, expected = 16;
+ struct sockaddr_un srv_addr;
+
+ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ return -1;
+
+ srv_addr.sun_family = AF_UNIX;
+ strcpy(srv_addr.sun_path, UUIDD_SOCKET_PATH);
+
+ if (connect(s, (const struct sockaddr *) &srv_addr,
+ sizeof(struct sockaddr_un)) < 0)
+ goto fail;
+
+ op_buf[0] = op;
+ op_len = 1;
+ if (op == UUIDD_OP_BULK_TIME_UUID) {
+ memcpy(op_buf+1, num, sizeof(*num));
+ op_len += sizeof(*num);
+ expected += sizeof(*num);
+ }
+
+ ret = write(s, op_buf, op_len);
+ if (ret < 1)
+ goto fail;
+
+ ret = read_all(s, (char *) &reply_len, sizeof(reply_len));
+ if (ret < 0)
+ goto fail;
+
+ if (reply_len != expected)
+ goto fail;
+
+ ret = read_all(s, op_buf, reply_len);
+
+ if (op == UUIDD_OP_BULK_TIME_UUID)
+ memcpy(op_buf+16, num, sizeof(int));
+
+ memcpy(out, op_buf, 16);
+
+ close(s);
+ return ((ret == expected) ? 0 : -1);
+
+fail:
+ close(s);
+ return -1;
+}
+
+#else /* !defined(HAVE_UUIDD) && defined(HAVE_SYS_UN_H) */
+static int get_uuid_via_daemon(int op, uuid_t out, int *num)
+{
+ return -1;
+}
+#endif
+
+int __uuid_generate_time(uuid_t out, int *num)
+{
+ static unsigned char node_id[6];
+ static int has_init = 0;
+ struct uuid uu;
+ uint32_t clock_mid;
+ int ret;
+
+ if (!has_init) {
+ if (get_node_id(node_id) <= 0) {
+ random_get_bytes(node_id, 6);
+ /*
+ * Set multicast bit, to prevent conflicts
+ * with IEEE 802 addresses obtained from
+ * network cards
+ */
+ node_id[0] |= 0x01;
+ }
+ has_init = 1;
+ }
+ ret = get_clock(&clock_mid, &uu.time_low, &uu.clock_seq, num);
+ uu.clock_seq |= 0x8000;
+ uu.time_mid = (uint16_t) clock_mid;
+ uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000;
+ memcpy(uu.node, node_id, 6);
+ uuid_pack(&uu, out);
+ return ret;
+}
+
+/*
+ * Generate time-based UUID and store it to @out
+ *
+ * Tries to guarantee uniqueness of the generated UUIDs by obtaining them from the uuidd daemon,
+ * or, if uuidd is not usable, by using the global clock state counter (see get_clock()).
+ * If neither of these is possible (e.g. because of insufficient permissions), it generates
+ * the UUID anyway, but returns -1. Otherwise, returns 0.
+ */
+static int uuid_generate_time_generic(uuid_t out) {
+#ifdef HAVE_TLS
+ THREAD_LOCAL int num = 0;
+ THREAD_LOCAL struct uuid uu;
+ THREAD_LOCAL time_t last_time = 0;
+ time_t now;
+
+ if (num > 0) {
+ now = time(0);
+ if (now > last_time+1)
+ num = 0;
+ }
+ if (num <= 0) {
+ num = 1000;
+ if (get_uuid_via_daemon(UUIDD_OP_BULK_TIME_UUID,
+ out, &num) == 0) {
+ last_time = time(0);
+ uuid_unpack(out, &uu);
+ num--;
+ return 0;
+ }
+ num = 0;
+ }
+ if (num > 0) {
+ uu.time_low++;
+ if (uu.time_low == 0) {
+ uu.time_mid++;
+ if (uu.time_mid == 0)
+ uu.time_hi_and_version++;
+ }
+ num--;
+ uuid_pack(&uu, out);
+ return 0;
+ }
+#else
+ if (get_uuid_via_daemon(UUIDD_OP_TIME_UUID, out, 0) == 0)
+ return 0;
+#endif
+
+ return __uuid_generate_time(out, 0);
+}
+
+/*
+ * Generate time-based UUID and store it to @out.
+ *
+ * Discards return value from uuid_generate_time_generic()
+ */
+void uuid_generate_time(uuid_t out)
+{
+ (void)uuid_generate_time_generic(out);
+}
+
+
+int uuid_generate_time_safe(uuid_t out)
+{
+ return uuid_generate_time_generic(out);
+}
+
+
+void __uuid_generate_random(uuid_t out, int *num)
+{
+ uuid_t buf;
+ struct uuid uu;
+ int i, n;
+
+ if (!num || !*num)
+ n = 1;
+ else
+ n = *num;
+
+ for (i = 0; i < n; i++) {
+ random_get_bytes(buf, sizeof(buf));
+ uuid_unpack(buf, &uu);
+
+ uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
+ uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF)
+ | 0x4000;
+ uuid_pack(&uu, out);
+ out += sizeof(uuid_t);
+ }
+}
+
+void uuid_generate_random(uuid_t out)
+{
+ int num = 1;
+ /* No real reason to use the daemon for random uuid's -- yet */
+
+ __uuid_generate_random(out, &num);
+}
+
+/*
+ * Check whether good random source (/dev/random or /dev/urandom)
+ * is available.
+ */
+static int have_random_source(void)
+{
+ struct stat s;
+
+ return (!stat("/dev/random", &s) || !stat("/dev/urandom", &s));
+}
+
+
+/*
+ * This is the generic front-end to uuid_generate_random and
+ * uuid_generate_time. It uses uuid_generate_random only if
+ * /dev/urandom is available, since otherwise we won't have
+ * high-quality randomness.
+ */
+void uuid_generate(uuid_t out)
+{
+ if (have_random_source())
+ uuid_generate_random(out);
+ else
+ uuid_generate_time(out);
+}
diff --git a/libblkid/libuuid/src/isnull.c b/libblkid/libuuid/src/isnull.c
new file mode 100644
index 000000000..931e7e7db
--- /dev/null
+++ b/libblkid/libuuid/src/isnull.c
@@ -0,0 +1,48 @@
+/*
+ * isnull.c --- Check whether or not the UUID is null
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "uuidP.h"
+
+/* Returns 1 if the uuid is the NULL uuid */
+int uuid_is_null(const uuid_t uu)
+{
+ const unsigned char *cp;
+ int i;
+
+ for (i=0, cp = uu; i < 16; i++)
+ if (*cp++)
+ return 0;
+ return 1;
+}
+
diff --git a/libblkid/libuuid/src/libuuid.sym b/libblkid/libuuid/src/libuuid.sym
new file mode 100644
index 000000000..28a207684
--- /dev/null
+++ b/libblkid/libuuid/src/libuuid.sym
@@ -0,0 +1,48 @@
+/*
+ * The symbol versioning ensures that a new application requiring symbol 'foo'
+ * can't run with old libbrary.so not providing 'foo' - the global SONAME
+ * version info can't enforce this since we never change the SONAME.
+ *
+ * The original libuuid from e2fsprogs (<=1.41.5) does not to use
+ * symbol versioning -- all the original symbols are in UUID_1.0 now.
+ *
+ * Copyright (C) 2011-2014 Karel Zak <kzak@redhat.com>
+ */
+UUID_1.0 {
+global:
+ uuid_clear;
+ uuid_compare;
+ uuid_copy;
+ uuid_generate;
+ uuid_generate_random;
+ uuid_generate_time;
+ uuid_is_null;
+ uuid_parse;
+ uuid_unparse;
+ uuid_unparse_lower;
+ uuid_unparse_upper;
+ uuid_time;
+ uuid_type;
+ uuid_variant;
+};
+
+/*
+ * version(s) since util-linux 2.20
+ */
+UUID_2.20 {
+global:
+ uuid_generate_time_safe;
+} UUID_1.0;
+
+
+/*
+ * __uuid_* this is not part of the official API, this is
+ * uuidd (uuid daemon) specific stuff. Hell.
+ */
+UUIDD_PRIVATE {
+global:
+ __uuid_generate_time;
+ __uuid_generate_random;
+local:
+ *;
+};
diff --git a/libblkid/libuuid/src/pack.c b/libblkid/libuuid/src/pack.c
new file mode 100644
index 000000000..6e1247669
--- /dev/null
+++ b/libblkid/libuuid/src/pack.c
@@ -0,0 +1,69 @@
+/*
+ * Internal routine for packing UUIDs
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <string.h>
+#include "uuidP.h"
+
+void uuid_pack(const struct uuid *uu, uuid_t ptr)
+{
+ uint32_t tmp;
+ unsigned char *out = ptr;
+
+ tmp = uu->time_low;
+ out[3] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[2] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[1] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[0] = (unsigned char) tmp;
+
+ tmp = uu->time_mid;
+ out[5] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[4] = (unsigned char) tmp;
+
+ tmp = uu->time_hi_and_version;
+ out[7] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[6] = (unsigned char) tmp;
+
+ tmp = uu->clock_seq;
+ out[9] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[8] = (unsigned char) tmp;
+
+ memcpy(out+10, uu->node, 6);
+}
+
diff --git a/libblkid/libuuid/src/parse.c b/libblkid/libuuid/src/parse.c
new file mode 100644
index 000000000..074383efa
--- /dev/null
+++ b/libblkid/libuuid/src/parse.c
@@ -0,0 +1,79 @@
+/*
+ * parse.c --- UUID parsing
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "uuidP.h"
+
+int uuid_parse(const char *in, uuid_t uu)
+{
+ struct uuid uuid;
+ int i;
+ const char *cp;
+ char buf[3];
+
+ if (strlen(in) != 36)
+ return -1;
+ for (i=0, cp = in; i <= 36; i++,cp++) {
+ if ((i == 8) || (i == 13) || (i == 18) ||
+ (i == 23)) {
+ if (*cp == '-')
+ continue;
+ else
+ return -1;
+ }
+ if (i== 36)
+ if (*cp == 0)
+ continue;
+ if (!isxdigit(*cp))
+ return -1;
+ }
+ uuid.time_low = strtoul(in, NULL, 16);
+ uuid.time_mid = strtoul(in+9, NULL, 16);
+ uuid.time_hi_and_version = strtoul(in+14, NULL, 16);
+ uuid.clock_seq = strtoul(in+19, NULL, 16);
+ cp = in+24;
+ buf[2] = 0;
+ for (i=0; i < 6; i++) {
+ buf[0] = *cp++;
+ buf[1] = *cp++;
+ uuid.node[i] = strtoul(buf, NULL, 16);
+ }
+
+ uuid_pack(&uuid, uu);
+ return 0;
+}
diff --git a/libblkid/libuuid/src/test_uuid.c b/libblkid/libuuid/src/test_uuid.c
new file mode 100644
index 000000000..e03138f7d
--- /dev/null
+++ b/libblkid/libuuid/src/test_uuid.c
@@ -0,0 +1,180 @@
+/*
+ * tst_uuid.c --- test program from the UUID library
+ *
+ * Copyright (C) 1996, 1997, 1998 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0500
+#include <windows.h>
+#define UUID MYUUID
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "uuid.h"
+
+static int test_uuid(const char * uuid, int isValid)
+{
+ static const char * validStr[2] = {"invalid", "valid"};
+ uuid_t uuidBits;
+ int parsedOk;
+
+ parsedOk = uuid_parse(uuid, uuidBits) == 0;
+
+ printf("%s is %s", uuid, validStr[isValid]);
+ if (parsedOk != isValid) {
+ printf(" but uuid_parse says %s\n", validStr[parsedOk]);
+ return 1;
+ }
+ printf(", OK\n");
+ return 0;
+}
+
+#ifdef __GNUC__
+#define ATTR(x) __attribute__(x)
+#else
+#define ATTR(x)
+#endif
+
+int
+main(int argc ATTR((unused)) , char **argv ATTR((unused)))
+{
+ uuid_t buf, tst;
+ char str[100];
+ struct timeval tv;
+ time_t time_reg;
+ unsigned char *cp;
+ int i;
+ int failed = 0;
+ int type, variant;
+
+ uuid_generate(buf);
+ uuid_unparse(buf, str);
+ printf("UUID generate = %s\n", str);
+ printf("UUID: ");
+ for (i=0, cp = (unsigned char *) &buf; i < 16; i++) {
+ printf("%02x", *cp++);
+ }
+ printf("\n");
+ type = uuid_type(buf); variant = uuid_variant(buf);
+ printf("UUID type = %d, UUID variant = %d\n", type, variant);
+ if (variant != UUID_VARIANT_DCE) {
+ printf("Incorrect UUID Variant; was expecting DCE!\n");
+ failed++;
+ }
+ printf("\n");
+
+ uuid_generate_random(buf);
+ uuid_unparse(buf, str);
+ printf("UUID random string = %s\n", str);
+ printf("UUID: ");
+ for (i=0, cp = (unsigned char *) &buf; i < 16; i++) {
+ printf("%02x", *cp++);
+ }
+ printf("\n");
+ type = uuid_type(buf); variant = uuid_variant(buf);
+ printf("UUID type = %d, UUID variant = %d\n", type, variant);
+ if (variant != UUID_VARIANT_DCE) {
+ printf("Incorrect UUID Variant; was expecting DCE!\n");
+ failed++;
+ }
+ if (type != 4) {
+ printf("Incorrect UUID type; was expecting "
+ "4 (random type)!\n");
+ failed++;
+ }
+ printf("\n");
+
+ uuid_generate_time(buf);
+ uuid_unparse(buf, str);
+ printf("UUID string = %s\n", str);
+ printf("UUID time: ");
+ for (i=0, cp = (unsigned char *) &buf; i < 16; i++) {
+ printf("%02x", *cp++);
+ }
+ printf("\n");
+ type = uuid_type(buf); variant = uuid_variant(buf);
+ printf("UUID type = %d, UUID variant = %d\n", type, variant);
+ if (variant != UUID_VARIANT_DCE) {
+ printf("Incorrect UUID Variant; was expecting DCE!\n");
+ failed++;
+ }
+ if (type != 1) {
+ printf("Incorrect UUID type; was expecting "
+ "1 (time-based type)!\\n");
+ failed++;
+ }
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ time_reg = uuid_time(buf, &tv);
+ printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, tv.tv_usec,
+ ctime(&time_reg));
+ uuid_parse(str, tst);
+ if (!uuid_compare(buf, tst))
+ printf("UUID parse and compare succeeded.\n");
+ else {
+ printf("UUID parse and compare failed!\n");
+ failed++;
+ }
+ uuid_clear(tst);
+ if (uuid_is_null(tst))
+ printf("UUID clear and is null succeeded.\n");
+ else {
+ printf("UUID clear and is null failed!\n");
+ failed++;
+ }
+ uuid_copy(buf, tst);
+ if (!uuid_compare(buf, tst))
+ printf("UUID copy and compare succeeded.\n");
+ else {
+ printf("UUID copy and compare failed!\n");
+ failed++;
+ }
+ failed += test_uuid("84949cc5-4701-4a84-895b-354c584a981b", 1);
+ failed += test_uuid("84949CC5-4701-4A84-895B-354C584A981B", 1);
+ failed += test_uuid("84949cc5-4701-4a84-895b-354c584a981bc", 0);
+ failed += test_uuid("84949cc5-4701-4a84-895b-354c584a981", 0);
+ failed += test_uuid("84949cc5x4701-4a84-895b-354c584a981b", 0);
+ failed += test_uuid("84949cc504701-4a84-895b-354c584a981b", 0);
+ failed += test_uuid("84949cc5-470104a84-895b-354c584a981b", 0);
+ failed += test_uuid("84949cc5-4701-4a840895b-354c584a981b", 0);
+ failed += test_uuid("84949cc5-4701-4a84-895b0354c584a981b", 0);
+ failed += test_uuid("g4949cc5-4701-4a84-895b-354c584a981b", 0);
+ failed += test_uuid("84949cc5-4701-4a84-895b-354c584a981g", 0);
+
+ if (failed) {
+ printf("%d failures.\n", failed);
+ exit(1);
+ }
+ return 0;
+}
diff --git a/libblkid/libuuid/src/unpack.c b/libblkid/libuuid/src/unpack.c
new file mode 100644
index 000000000..beaaff3ca
--- /dev/null
+++ b/libblkid/libuuid/src/unpack.c
@@ -0,0 +1,63 @@
+/*
+ * Internal routine for unpacking UUID
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <string.h>
+#include "uuidP.h"
+
+void uuid_unpack(const uuid_t in, struct uuid *uu)
+{
+ const uint8_t *ptr = in;
+ uint32_t tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_low = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_mid = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_hi_and_version = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->clock_seq = tmp;
+
+ memcpy(uu->node, ptr, 6);
+}
+
diff --git a/libblkid/libuuid/src/unparse.c b/libblkid/libuuid/src/unparse.c
new file mode 100644
index 000000000..a95bbb042
--- /dev/null
+++ b/libblkid/libuuid/src/unparse.c
@@ -0,0 +1,76 @@
+/*
+ * unparse.c -- convert a UUID to string
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+
+#include "uuidP.h"
+
+static const char *fmt_lower =
+ "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x";
+
+static const char *fmt_upper =
+ "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X";
+
+#ifdef UUID_UNPARSE_DEFAULT_UPPER
+#define FMT_DEFAULT fmt_upper
+#else
+#define FMT_DEFAULT fmt_lower
+#endif
+
+static void uuid_unparse_x(const uuid_t uu, char *out, const char *fmt)
+{
+ struct uuid uuid;
+
+ uuid_unpack(uu, &uuid);
+ sprintf(out, fmt,
+ uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
+ uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
+ uuid.node[0], uuid.node[1], uuid.node[2],
+ uuid.node[3], uuid.node[4], uuid.node[5]);
+}
+
+void uuid_unparse_lower(const uuid_t uu, char *out)
+{
+ uuid_unparse_x(uu, out, fmt_lower);
+}
+
+void uuid_unparse_upper(const uuid_t uu, char *out)
+{
+ uuid_unparse_x(uu, out, fmt_upper);
+}
+
+void uuid_unparse(const uuid_t uu, char *out)
+{
+ uuid_unparse_x(uu, out, FMT_DEFAULT);
+}
diff --git a/libblkid/libuuid/src/uuid.h b/libblkid/libuuid/src/uuid.h
new file mode 100644
index 000000000..30bd4c0e0
--- /dev/null
+++ b/libblkid/libuuid/src/uuid.h
@@ -0,0 +1,104 @@
+/*
+ * Public include file for the UUID library
+ *
+ * Copyright (C) 1996, 1997, 1998 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#ifndef _UUID_UUID_H
+#define _UUID_UUID_H
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+typedef unsigned char uuid_t[16];
+
+/* UUID Variant definitions */
+#define UUID_VARIANT_NCS 0
+#define UUID_VARIANT_DCE 1
+#define UUID_VARIANT_MICROSOFT 2
+#define UUID_VARIANT_OTHER 3
+
+/* UUID Type definitions */
+#define UUID_TYPE_DCE_TIME 1
+#define UUID_TYPE_DCE_RANDOM 4
+
+/* Allow UUID constants to be defined */
+#ifdef __GNUC__
+#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \
+ static const uuid_t name __attribute__ ((unused)) = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15}
+#else
+#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \
+ static const uuid_t name = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15}
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* clear.c */
+extern void uuid_clear(uuid_t uu);
+
+/* compare.c */
+extern int uuid_compare(const uuid_t uu1, const uuid_t uu2);
+
+/* copy.c */
+extern void uuid_copy(uuid_t dst, const uuid_t src);
+
+/* gen_uuid.c */
+extern void uuid_generate(uuid_t out);
+extern void uuid_generate_random(uuid_t out);
+extern void uuid_generate_time(uuid_t out);
+extern int uuid_generate_time_safe(uuid_t out);
+
+/* isnull.c */
+extern int uuid_is_null(const uuid_t uu);
+
+/* parse.c */
+extern int uuid_parse(const char *in, uuid_t uu);
+
+/* unparse.c */
+extern void uuid_unparse(const uuid_t uu, char *out);
+extern void uuid_unparse_lower(const uuid_t uu, char *out);
+extern void uuid_unparse_upper(const uuid_t uu, char *out);
+
+/* uuid_time.c */
+extern time_t uuid_time(const uuid_t uu, struct timeval *ret_tv);
+extern int uuid_type(const uuid_t uu);
+extern int uuid_variant(const uuid_t uu);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UUID_UUID_H */
diff --git a/libblkid/libuuid/src/uuidP.h b/libblkid/libuuid/src/uuidP.h
new file mode 100644
index 000000000..86a5e266f
--- /dev/null
+++ b/libblkid/libuuid/src/uuidP.h
@@ -0,0 +1,61 @@
+/*
+ * uuid.h -- private header file for uuids
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "uuid.h"
+
+#define LIBUUID_CLOCK_FILE "/var/lib/libuuid/clock.txt"
+
+/*
+ * Offset between 15-Oct-1582 and 1-Jan-70
+ */
+#define TIME_OFFSET_HIGH 0x01B21DD2
+#define TIME_OFFSET_LOW 0x13814000
+
+struct uuid {
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint16_t clock_seq;
+ uint8_t node[6];
+};
+
+
+/*
+ * prototypes
+ */
+void uuid_pack(const struct uuid *uu, uuid_t ptr);
+void uuid_unpack(const uuid_t in, struct uuid *uu);
diff --git a/libblkid/libuuid/src/uuid_time.c b/libblkid/libuuid/src/uuid_time.c
new file mode 100644
index 000000000..f25f5c90f
--- /dev/null
+++ b/libblkid/libuuid/src/uuid_time.c
@@ -0,0 +1,171 @@
+/*
+ * uuid_time.c --- Interpret the time field from a uuid. This program
+ * violates the UUID abstraction barrier by reaching into the guts
+ * of a UUID and interpreting it.
+ *
+ * Copyright (C) 1998, 1999 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0500
+#include <windows.h>
+#define UUID MYUUID
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+#include "uuidP.h"
+
+time_t uuid_time(const uuid_t uu, struct timeval *ret_tv)
+{
+ struct timeval tv;
+ struct uuid uuid;
+ uint32_t high;
+ uint64_t clock_reg;
+
+ uuid_unpack(uu, &uuid);
+
+ high = uuid.time_mid | ((uuid.time_hi_and_version & 0xFFF) << 16);
+ clock_reg = uuid.time_low | ((uint64_t) high << 32);
+
+ clock_reg -= (((uint64_t) 0x01B21DD2) << 32) + 0x13814000;
+ tv.tv_sec = clock_reg / 10000000;
+ tv.tv_usec = (clock_reg % 10000000) / 10;
+
+ if (ret_tv)
+ *ret_tv = tv;
+
+ return tv.tv_sec;
+}
+
+int uuid_type(const uuid_t uu)
+{
+ struct uuid uuid;
+
+ uuid_unpack(uu, &uuid);
+ return ((uuid.time_hi_and_version >> 12) & 0xF);
+}
+
+int uuid_variant(const uuid_t uu)
+{
+ struct uuid uuid;
+ int var;
+
+ uuid_unpack(uu, &uuid);
+ var = uuid.clock_seq;
+
+ if ((var & 0x8000) == 0)
+ return UUID_VARIANT_NCS;
+ if ((var & 0x4000) == 0)
+ return UUID_VARIANT_DCE;
+ if ((var & 0x2000) == 0)
+ return UUID_VARIANT_MICROSOFT;
+ return UUID_VARIANT_OTHER;
+}
+
+#ifdef DEBUG
+static const char *variant_string(int variant)
+{
+ switch (variant) {
+ case UUID_VARIANT_NCS:
+ return "NCS";
+ case UUID_VARIANT_DCE:
+ return "DCE";
+ case UUID_VARIANT_MICROSOFT:
+ return "Microsoft";
+ default:
+ return "Other";
+ }
+}
+
+
+int
+main(int argc, char **argv)
+{
+ uuid_t buf;
+ time_t time_reg;
+ struct timeval tv;
+ int type, variant;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s uuid\n", argv[0]);
+ exit(1);
+ }
+ if (uuid_parse(argv[1], buf)) {
+ fprintf(stderr, "Invalid UUID: %s\n", argv[1]);
+ exit(1);
+ }
+ variant = uuid_variant(buf);
+ type = uuid_type(buf);
+ time_reg = uuid_time(buf, &tv);
+
+ printf("UUID variant is %d (%s)\n", variant, variant_string(variant));
+ if (variant != UUID_VARIANT_DCE) {
+ printf("Warning: This program only knows how to interpret "
+ "DCE UUIDs.\n\tThe rest of the output is likely "
+ "to be incorrect!!\n");
+ }
+ printf("UUID type is %d", type);
+ switch (type) {
+ case 1:
+ printf(" (time based)\n");
+ break;
+ case 2:
+ printf(" (DCE)\n");
+ break;
+ case 3:
+ printf(" (name-based)\n");
+ break;
+ case 4:
+ printf(" (random)\n");
+ break;
+ default:
+ printf("\n");
+ }
+ if (type != 1) {
+ printf("Warning: not a time-based UUID, so UUID time "
+ "decoding will likely not work!\n");
+ }
+ printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, tv.tv_usec,
+ ctime(&time_reg));
+
+ return 0;
+}
+#endif
diff --git a/libblkid/libuuid/src/uuidd.h b/libblkid/libuuid/src/uuidd.h
new file mode 100644
index 000000000..2f709680b
--- /dev/null
+++ b/libblkid/libuuid/src/uuidd.h
@@ -0,0 +1,54 @@
+/*
+ * Definitions used by the uuidd daemon
+ *
+ * Copyright (C) 2007 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#ifndef _UUID_UUIDD_H
+#define _UUID_UUIDD_H
+
+#define UUIDD_DIR _PATH_LOCALSTATEDIR "/uuidd"
+#define UUIDD_SOCKET_PATH UUIDD_DIR "/request"
+#define UUIDD_PIDFILE_PATH UUIDD_DIR "/uuidd.pid"
+#define UUIDD_PATH "/usr/sbin/uuidd"
+
+#define UUIDD_OP_GETPID 0
+#define UUIDD_OP_GET_MAXOP 1
+#define UUIDD_OP_TIME_UUID 2
+#define UUIDD_OP_RANDOM_UUID 3
+#define UUIDD_OP_BULK_TIME_UUID 4
+#define UUIDD_OP_BULK_RANDOM_UUID 5
+#define UUIDD_MAX_OP UUIDD_OP_BULK_RANDOM_UUID
+
+extern int __uuid_generate_time(uuid_t out, int *num);
+extern void __uuid_generate_random(uuid_t out, int *num);
+
+#endif /* _UUID_UUID_H */
diff --git a/libblkid/libuuid/uuid.pc.in b/libblkid/libuuid/uuid.pc.in
new file mode 100644
index 000000000..875de19bc
--- /dev/null
+++ b/libblkid/libuuid/uuid.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@usrlib_execdir@
+includedir=@includedir@
+
+Name: uuid
+Description: Universally unique id library
+Version: @LIBUUID_VERSION@
+Requires:
+Cflags: -I${includedir}/uuid
+Libs: -L${libdir} -luuid
diff --git a/libblkid/samples/.gitignore b/libblkid/samples/.gitignore
new file mode 100644
index 000000000..4efeb622f
--- /dev/null
+++ b/libblkid/samples/.gitignore
@@ -0,0 +1,4 @@
+mkfs
+partitions
+superblocks
+topology
diff --git a/libblkid/samples/Makemodule.am b/libblkid/samples/Makemodule.am
new file mode 100644
index 000000000..0ffbf1477
--- /dev/null
+++ b/libblkid/samples/Makemodule.am
@@ -0,0 +1,22 @@
+
+check_PROGRAMS += \
+ sample-mkfs \
+ sample-partitions \
+ sample-superblocks \
+ sample-topology
+
+sample_mkfs_SOURCES = libblkid/samples/mkfs.c
+sample_mkfs_LDADD = libblkid.la
+sample_mkfs_CFLAGS = -I$(ul_libblkid_incdir)
+
+sample_partitions_SOURCES = libblkid/samples/partitions.c
+sample_partitions_LDADD = libblkid.la
+sample_partitions_CFLAGS = -I$(ul_libblkid_incdir)
+
+sample_superblocks_SOURCES = libblkid/samples/superblocks.c
+sample_superblocks_LDADD = libblkid.la
+sample_superblocks_CFLAGS = -I$(ul_libblkid_incdir)
+
+sample_topology_SOURCES = libblkid/samples/topology.c
+sample_topology_LDADD = libblkid.la
+sample_topology_CFLAGS = -I$(ul_libblkid_incdir)
diff --git a/libblkid/samples/mkfs.c b/libblkid/samples/mkfs.c
new file mode 100644
index 000000000..5c3ebe79e
--- /dev/null
+++ b/libblkid/samples/mkfs.c
@@ -0,0 +1,78 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <blkid.h>
+
+#include "c.h"
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ char *devname;
+ blkid_probe pr;
+ blkid_topology tp;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <device> "
+ "-- checks based on libblkid for mkfs-like programs.\n",
+ program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+
+ devname = argv[1];
+ pr = blkid_new_probe_from_filename(devname);
+ if (!pr)
+ err(EXIT_FAILURE, "%s: faild to create a new libblkid probe",
+ devname);
+
+ /*
+ * check Filesystems / Partitions overwrite
+ */
+
+ /* enable partitions probing (superblocks are enabled by default) */
+ blkid_probe_enable_partitions(pr, TRUE);
+
+ rc = blkid_do_fullprobe(pr);
+ if (rc == -1)
+ errx(EXIT_FAILURE, "%s: blkid_do_fullprobe() failed", devname);
+ else if (rc == 0) {
+ const char *type;
+
+ if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL))
+ errx(EXIT_FAILURE, "%s: appears to contain an existing "
+ "%s superblock", devname, type);
+
+ if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL))
+ errx(EXIT_FAILURE, "%s: appears to contain an partition "
+ "table (%s)", devname, type);
+ }
+
+ /*
+ * get topology details
+ */
+ tp = blkid_probe_get_topology(pr);
+ if (!tp)
+ errx(EXIT_FAILURE, "%s: failed to read topology", devname);
+
+
+ /* ... your mkfs.<type> code or so ...
+
+ off = blkid_topology_get_alignment_offset(tp);
+
+ */
+
+ blkid_free_probe(pr);
+
+ return EXIT_SUCCESS;
+}
diff --git a/libblkid/samples/partitions.c b/libblkid/samples/partitions.c
new file mode 100644
index 000000000..fe0ad4827
--- /dev/null
+++ b/libblkid/samples/partitions.c
@@ -0,0 +1,96 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <blkid.h>
+#include "c.h"
+
+int main(int argc, char *argv[])
+{
+ int i, nparts;
+ char *devname;
+ blkid_probe pr;
+ blkid_partlist ls;
+ blkid_parttable root_tab;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <device|file> "
+ "-- prints partitions\n",
+ program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+
+ devname = argv[1];
+ pr = blkid_new_probe_from_filename(devname);
+ if (!pr)
+ err(EXIT_FAILURE, "%s: faild to create a new libblkid probe",
+ devname);
+ /* Binary interface */
+ ls = blkid_probe_get_partitions(pr);
+ if (!ls)
+ errx(EXIT_FAILURE, "%s: failed to read partitions\n", devname);
+
+ /*
+ * Print info about the primary (root) partition table
+ */
+ root_tab = blkid_partlist_get_table(ls);
+ if (!root_tab)
+ errx(EXIT_FAILURE, "%s: does not contains any "
+ "known partition table\n", devname);
+
+ printf("size: %jd, sector size: %u, PT: %s, offset: %jd, id=%s\n---\n",
+ blkid_probe_get_size(pr),
+ blkid_probe_get_sectorsize(pr),
+ blkid_parttable_get_type(root_tab),
+ blkid_parttable_get_offset(root_tab),
+ blkid_parttable_get_id(root_tab));
+
+ /*
+ * List partitions
+ */
+ nparts = blkid_partlist_numof_partitions(ls);
+ if (!nparts)
+ goto done;
+
+ for (i = 0; i < nparts; i++) {
+ const char *p;
+ blkid_partition par = blkid_partlist_get_partition(ls, i);
+ blkid_parttable tab = blkid_partition_get_table(par);
+
+ printf("#%d: %10llu %10llu 0x%x",
+ blkid_partition_get_partno(par),
+ (unsigned long long) blkid_partition_get_start(par),
+ (unsigned long long) blkid_partition_get_size(par),
+ blkid_partition_get_type(par));
+
+ if (root_tab != tab)
+ /* subpartition (BSD, Minix, ...) */
+ printf(" (%s)", blkid_parttable_get_type(tab));
+
+ p = blkid_partition_get_name(par);
+ if (p)
+ printf(" name='%s'", p);
+ p = blkid_partition_get_uuid(par);
+ if (p)
+ printf(" uuid='%s'", p);
+ p = blkid_partition_get_type_string(par);
+ if (p)
+ printf(" type='%s'", p);
+
+ putc('\n', stdout);
+ }
+
+done:
+ blkid_free_probe(pr);
+ return EXIT_SUCCESS;
+}
diff --git a/libblkid/samples/superblocks.c b/libblkid/samples/superblocks.c
new file mode 100644
index 000000000..20e39c97e
--- /dev/null
+++ b/libblkid/samples/superblocks.c
@@ -0,0 +1,67 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <blkid.h>
+
+#include "c.h"
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ char *devname;
+ blkid_probe pr;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <device> "
+ "-- prints superblocks details about the device\n",
+ program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+
+ devname = argv[1];
+ pr = blkid_new_probe_from_filename(devname);
+ if (!pr)
+ err(EXIT_FAILURE, "%s: faild to create a new libblkid probe",
+ devname);
+
+ /* enable topology probing */
+ blkid_probe_enable_superblocks(pr, TRUE);
+
+ /* set all flags */
+ blkid_probe_set_superblocks_flags(pr,
+ BLKID_SUBLKS_LABEL | BLKID_SUBLKS_LABELRAW |
+ BLKID_SUBLKS_UUID | BLKID_SUBLKS_UUIDRAW |
+ BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE |
+ BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION |
+ BLKID_SUBLKS_MAGIC);
+
+ rc = blkid_do_safeprobe(pr);
+ if (rc == -1)
+ errx(EXIT_FAILURE, "%s: blkid_do_safeprobe() failed", devname);
+ else if (rc == 1)
+ warnx("%s: cannot gather information about superblocks", devname);
+ else {
+ int i, nvals = blkid_probe_numof_values(pr);
+
+ for (i = 0; i < nvals; i++) {
+ const char *name, *data;
+
+ blkid_probe_get_value(pr, i, &name, &data, NULL);
+ printf("\t%s = %s\n", name, data);
+ }
+ }
+
+ blkid_free_probe(pr);
+ return EXIT_SUCCESS;
+}
diff --git a/libblkid/samples/topology.c b/libblkid/samples/topology.c
new file mode 100644
index 000000000..de1c3a5e3
--- /dev/null
+++ b/libblkid/samples/topology.c
@@ -0,0 +1,86 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <blkid.h>
+
+#include "c.h"
+
+int main(int argc, char *argv[])
+{
+ int rc;
+ char *devname;
+ blkid_probe pr;
+ blkid_topology tp;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <device> "
+ "-- prints topology details about the device\n",
+ program_invocation_short_name);
+ return EXIT_FAILURE;
+ }
+
+ devname = argv[1];
+ pr = blkid_new_probe_from_filename(devname);
+ if (!pr)
+ err(EXIT_FAILURE, "%s: faild to create a new libblkid probe",
+ devname);
+ /*
+ * Binary interface
+ */
+ tp = blkid_probe_get_topology(pr);
+ if (tp) {
+ printf("----- binary interface:\n");
+ printf("\talignment offset : %lu\n",
+ blkid_topology_get_alignment_offset(tp));
+ printf("\tminimum io size : %lu\n",
+ blkid_topology_get_minimum_io_size(tp));
+ printf("\toptimal io size : %lu\n",
+ blkid_topology_get_optimal_io_size(tp));
+ printf("\tlogical sector size : %lu\n",
+ blkid_topology_get_logical_sector_size(tp));
+ printf("\tphysical sector size : %lu\n",
+ blkid_topology_get_physical_sector_size(tp));
+ }
+
+ /*
+ * NAME=value interface
+ */
+
+ /* enable topology probing */
+ blkid_probe_enable_topology(pr, TRUE);
+
+ /* disable superblocks probing (enabled by default) */
+ blkid_probe_enable_superblocks(pr, FALSE);
+
+ rc = blkid_do_fullprobe(pr);
+ if (rc == -1)
+ errx(EXIT_FAILURE, "%s: blkid_do_fullprobe() failed", devname);
+ else if (rc == 1)
+ warnx("%s: missing topology information", devname);
+ else {
+ int i, nvals = blkid_probe_numof_values(pr);
+
+ printf("----- NAME=value interface (values: %d):\n", nvals);
+
+ for (i = 0; i < nvals; i++) {
+ const char *name, *data;
+
+ blkid_probe_get_value(pr, i, &name, &data, NULL);
+ printf("\t%s = %s\n", name, data);
+ }
+ }
+
+ blkid_free_probe(pr);
+ return EXIT_SUCCESS;
+}
diff --git a/libblkid/src/.gitignore b/libblkid/src/.gitignore
new file mode 100644
index 000000000..af34f58e0
--- /dev/null
+++ b/libblkid/src/.gitignore
@@ -0,0 +1 @@
+blkid.h
diff --git a/libblkid/src/bitops.h b/libblkid/src/bitops.h
new file mode 100644
index 000000000..f5974e971
--- /dev/null
+++ b/libblkid/src/bitops.h
@@ -0,0 +1,126 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#ifndef BITOPS_H
+#define BITOPS_H
+
+#include <stdint.h>
+#include <sys/param.h>
+
+#if defined(HAVE_BYTESWAP_H)
+# include <byteswap.h>
+#endif
+
+#define HAVE_SYS_ENDIAN_H
+#if defined(HAVE_ENDIAN_H)
+# include <endian.h>
+#elif defined(HAVE_SYS_ENDIAN_H) /* BSDs have them here */
+# include <sys/endian.h>
+#endif
+
+#if defined(__OpenBSD__)
+# include <sys/types.h>
+# define be16toh(x) betoh16(x)
+# define be32toh(x) betoh32(x)
+# define be64toh(x) betoh64(x)
+#endif
+
+/*
+ * Fallbacks
+ */
+#ifndef bswap_16
+# define bswap_16(x) ((((x) & 0x00FF) << 8) | \
+ (((x) & 0xFF00) >> 8))
+#endif
+
+#ifndef bswap_32
+# define bswap_32(x) ((((x) & 0x000000FF) << 24) | \
+ (((x) & 0x0000FF00) << 8) | \
+ (((x) & 0x00FF0000) >> 8) | \
+ (((x) & 0xFF000000) >> 24))
+#endif
+
+#ifndef bswap_64
+# define bswap_64(x) ((((x) & 0x00000000000000FFULL) << 56) | \
+ (((x) & 0x000000000000FF00ULL) << 40) | \
+ (((x) & 0x0000000000FF0000ULL) << 24) | \
+ (((x) & 0x00000000FF000000ULL) << 8) | \
+ (((x) & 0x000000FF00000000ULL) >> 8) | \
+ (((x) & 0x0000FF0000000000ULL) >> 24) | \
+ (((x) & 0x00FF000000000000ULL) >> 40) | \
+ (((x) & 0xFF00000000000000ULL) >> 56))
+#endif
+
+#ifndef htobe16
+//# if !defined(WORDS_BIGENDIAN)
+//# define htobe16(x) bswap_16 (x)
+# define htole16(x) (x)
+# define be16toh(x) bswap_16 (x)
+# define le16toh(x) (x)
+//# define htobe32(x) bswap_32 (x)
+# define htole32(x) (x)
+# define be32toh(x) bswap_32 (x)
+# define le32toh(x) (x)
+//# define htobe64(x) bswap_64 (x)
+# define htole64(x) (x)
+# define be64toh(x) bswap_64 (x)
+# define le64toh(x) (x)
+/*
+# else
+# define htobe16(x) (x)
+# define htole16(x) bswap_16 (x)
+# define be16toh(x) (x)
+# define le16toh(x) bswap_16 (x)
+# define htobe32(x) (x)
+# define htole32(x) bswap_32 (x)
+# define be32toh(x) (x)
+# define le32toh(x) bswap_32 (x)
+# define htobe64(x) (x)
+# define htole64(x) bswap_64 (x)
+# define be64toh(x) (x)
+# define le64toh(x) bswap_64 (x)
+# endif
+*/
+#endif
+/*
+ * Byte swab macros (based on linux/byteorder/swab.h)
+ */
+#define swab16(x) bswap_16(x)
+#define swab32(x) bswap_32(x)
+#define swab64(x) bswap_64(x)
+
+#define cpu_to_le16(x) ((uint16_t) htole16(x))
+#define cpu_to_le32(x) ((uint32_t) htole32(x))
+#define cpu_to_le64(x) ((uint64_t) htole64(x))
+
+#define cpu_to_be16(x) ((uint16_t) htobe16(x))
+#define cpu_to_be32(x) ((uint32_t) htobe32(x))
+#define cpu_to_be64(x) ((uint64_t) htobe64(x))
+
+#define le16_to_cpu(x) ((uint16_t) le16toh(x))
+#define le32_to_cpu(x) ((uint32_t) le32toh(x))
+#define le64_to_cpu(x) ((uint64_t) le64toh(x))
+
+#define be16_to_cpu(x) ((uint16_t) be16toh(x))
+#define be32_to_cpu(x) ((uint32_t) be32toh(x))
+#define be64_to_cpu(x) ((uint64_t) be64toh(x))
+
+/*
+ * Bit map related macros. Usually provided by libc.
+ */
+#ifndef NBBY
+# define NBBY CHAR_BIT
+#endif
+
+#ifndef setbit
+# define setbit(a,i) ((a)[(i)/NBBY] |= 1<<((i)%NBBY))
+# define clrbit(a,i) ((a)[(i)/NBBY] &= ~(1<<((i)%NBBY)))
+# define isset(a,i) ((a)[(i)/NBBY] & (1<<((i)%NBBY)))
+# define isclr(a,i) (((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0)
+#endif
+
+#endif /* BITOPS_H */
+
diff --git a/libblkid/src/blkdev.h b/libblkid/src/blkdev.h
new file mode 100644
index 000000000..ade08878d
--- /dev/null
+++ b/libblkid/src/blkdev.h
@@ -0,0 +1,146 @@
+/*
+ * No copyright is claimed. This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#ifndef BLKDEV_H
+#define BLKDEV_H
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_IOCCOM_H
+# include <sys/ioccom.h> /* for _IO macro on e.g. Solaris */
+#endif
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifdef HAVE_SYS_MKDEV_H
+# include <sys/mkdev.h> /* major and minor on Solaris */
+#endif
+
+#define DEFAULT_SECTOR_SIZE 512
+
+#ifdef __linux__
+/* very basic ioclts, should be available everywhere */
+# ifndef BLKROSET
+# define BLKROSET _IO(0x12,93) /* set device read-only (0 = read-write) */
+# define BLKROGET _IO(0x12,94) /* get read-only status (0 = read_write) */
+# define BLKRRPART _IO(0x12,95) /* re-read partition table */
+# define BLKGETSIZE _IO(0x12,96) /* return device size /512 (long *arg) */
+# define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */
+# define BLKRASET _IO(0x12,98) /* set read ahead for block device */
+# define BLKRAGET _IO(0x12,99) /* get current read ahead setting */
+# define BLKFRASET _IO(0x12,100) /* set filesystem (mm/filemap.c) read-ahead */
+# define BLKFRAGET _IO(0x12,101) /* get filesystem (mm/filemap.c) read-ahead */
+# define BLKSECTSET _IO(0x12,102) /* set max sectors per request (ll_rw_blk.c) */
+# define BLKSECTGET _IO(0x12,103) /* get max sectors per request (ll_rw_blk.c) */
+# define BLKSSZGET _IO(0x12,104) /* get block device sector size */
+
+/* ioctls introduced in 2.2.16, removed in 2.5.58 */
+# define BLKELVGET _IOR(0x12,106,size_t) /* elevator get */
+# define BLKELVSET _IOW(0x12,107,size_t) /* elevator set */
+
+# define BLKBSZGET _IOR(0x12,112,size_t)
+# define BLKBSZSET _IOW(0x12,113,size_t)
+# endif /* !BLKROSET */
+
+# ifndef BLKGETSIZE64
+# define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */
+# endif
+
+/* block device topology ioctls, introduced in 2.6.32 (commit ac481c20) */
+# ifndef BLKIOMIN
+# define BLKIOMIN _IO(0x12,120)
+# define BLKIOOPT _IO(0x12,121)
+# define BLKALIGNOFF _IO(0x12,122)
+# define BLKPBSZGET _IO(0x12,123)
+# endif
+
+/* discard zeroes support, introduced in 2.6.33 (commit 98262f27) */
+# ifndef BLKDISCARDZEROES
+# define BLKDISCARDZEROES _IO(0x12,124)
+# endif
+
+/* filesystem freeze, introduced in 2.6.29 (commit fcccf502) */
+# ifndef FIFREEZE
+# define FIFREEZE _IOWR('X', 119, int) /* Freeze */
+# define FITHAW _IOWR('X', 120, int) /* Thaw */
+# endif
+
+/* uniform CD-ROM information */
+# ifndef CDROM_GET_CAPABILITY
+# define CDROM_GET_CAPABILITY 0x5331
+# endif
+
+#endif /* __linux */
+
+
+#ifdef APPLE_DARWIN
+# define BLKGETSIZE DKIOCGETBLOCKCOUNT32
+#endif
+
+#ifndef HDIO_GETGEO
+# ifdef __linux__
+# define HDIO_GETGEO 0x0301
+# endif
+
+struct hd_geometry {
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders; /* truncated */
+ unsigned long start;
+};
+#endif /* HDIO_GETGEO */
+
+
+/* are we working with block device? */
+int is_blkdev(int fd);
+
+/* Determine size in bytes */
+off_t blkdev_find_size (int fd);
+
+/* get size in bytes */
+int blkdev_get_size(int fd, unsigned long long *bytes);
+
+/* get 512-byte sector count */
+int blkdev_get_sectors(int fd, unsigned long long *sectors);
+
+/* get hardware sector size */
+int blkdev_get_sector_size(int fd, int *sector_size);
+
+/* specifies whether or not the device is misaligned */
+int blkdev_is_misaligned(int fd);
+
+/* get physical block device size */
+int blkdev_get_physector_size(int fd, int *sector_size);
+
+/* is the device cdrom capable? */
+int blkdev_is_cdrom(int fd);
+
+/* get device's geometry - legacy */
+int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s);
+
+/* SCSI device types. Copied almost as-is from kernel header.
+ * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=include/scsi/scsi.h */
+#define SCSI_TYPE_DISK 0x00
+#define SCSI_TYPE_TAPE 0x01
+#define SCSI_TYPE_PRINTER 0x02
+#define SCSI_TYPE_PROCESSOR 0x03 /* HP scanners use this */
+#define SCSI_TYPE_WORM 0x04 /* Treated as ROM by our system */
+#define SCSI_TYPE_ROM 0x05
+#define SCSI_TYPE_SCANNER 0x06
+#define SCSI_TYPE_MOD 0x07 /* Magneto-optical disk - treated as SCSI_TYPE_DISK */
+#define SCSI_TYPE_MEDIUM_CHANGER 0x08
+#define SCSI_TYPE_COMM 0x09 /* Communications device */
+#define SCSI_TYPE_RAID 0x0c
+#define SCSI_TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */
+#define SCSI_TYPE_RBC 0x0e
+#define SCSI_TYPE_OSD 0x11
+#define SCSI_TYPE_NO_LUN 0x7f
+
+/* convert scsi type code to name */
+const char *blkdev_scsi_type_to_name(int type);
+
+
+#endif /* BLKDEV_H */
diff --git a/libblkid/src/blkid.h.in b/libblkid/src/blkid.h.in
new file mode 100644
index 000000000..4f5fe2ae5
--- /dev/null
+++ b/libblkid/src/blkid.h.in
@@ -0,0 +1,414 @@
+/*
+ * 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
+
+/* cache.c */
+extern void blkid_init_debug(int mask);
+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,
+ char *search_type, 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));
+extern void blkid_free_probe(blkid_probe pr);
+
+extern void blkid_reset_probe(blkid_probe pr);
+
+extern int blkid_probe_set_device(blkid_probe pr, int fd,
+ blkid_loff_t off, blkid_loff_t size);
+
+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);
+extern blkid_loff_t blkid_probe_get_offset(blkid_probe pr);
+extern unsigned int blkid_probe_get_sectorsize(blkid_probe pr);
+extern blkid_loff_t blkid_probe_get_sectors(blkid_probe pr);
+
+extern int blkid_probe_get_fd(blkid_probe pr);
+
+/*
+ * superblocks probing
+ */
+extern int blkid_known_fstype(const char *fstype);
+
+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);
+
+#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_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);
+extern int blkid_probe_reset_superblocks_filter(blkid_probe pr);
+extern int blkid_probe_invert_superblocks_filter(blkid_probe pr);
+
+/**
+ * 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[]);
+
+#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);
+
+/*
+ * topology probing
+ */
+extern int blkid_probe_enable_topology(blkid_probe pr, int enable);
+
+/* binary interface */
+extern blkid_topology blkid_probe_get_topology(blkid_probe pr);
+
+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));
+
+/*
+ * partitions probing
+ */
+extern int blkid_known_pttype(const char *pttype);
+
+extern int blkid_probe_enable_partitions(blkid_probe pr, int enable);
+
+extern int blkid_probe_reset_partitions_filter(blkid_probe pr);
+extern int blkid_probe_invert_partitions_filter(blkid_probe pr);
+extern int blkid_probe_filter_partitions_type(blkid_probe pr, int flag, char *names[]);
+
+/* 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);
+
+/* binary interface */
+extern blkid_partlist blkid_probe_get_partitions(blkid_probe pr);
+
+extern int blkid_partlist_numof_partitions(blkid_partlist ls);
+extern blkid_parttable blkid_partlist_get_table(blkid_partlist ls);
+extern blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n);
+extern blkid_partition blkid_partlist_get_partition_by_partno(blkid_partlist ls, int n);
+extern blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno);
+extern blkid_parttable blkid_partition_get_table(blkid_partition par);
+
+extern const char *blkid_partition_get_name(blkid_partition par);
+extern const char *blkid_partition_get_uuid(blkid_partition par);
+extern int blkid_partition_get_partno(blkid_partition par);
+extern blkid_loff_t blkid_partition_get_start(blkid_partition par);
+extern blkid_loff_t blkid_partition_get_size(blkid_partition par);
+
+extern int blkid_partition_get_type(blkid_partition par)
+ __ul_attribute__((nonnull));
+
+extern const char *blkid_partition_get_type_string(blkid_partition par);
+
+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);
+extern const char *blkid_parttable_get_id(blkid_parttable tab);
+
+extern blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab);
+extern blkid_partition blkid_parttable_get_parent(blkid_parttable tab);
+
+/*
+ * NAME=value low-level interface
+ */
+extern int blkid_do_probe(blkid_probe pr);
+extern int blkid_do_safeprobe(blkid_probe pr);
+extern int blkid_do_fullprobe(blkid_probe pr);
+
+extern int blkid_probe_numof_values(blkid_probe pr);
+extern int blkid_probe_get_value(blkid_probe pr, int num, const char **name,
+ const char **data, size_t *len);
+extern int blkid_probe_lookup_value(blkid_probe pr, const char *name,
+ const char **data, size_t *len);
+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);
+extern int blkid_probe_step_back(blkid_probe pr);
+
+/*
+ * 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 000000000..fbf4e719b
--- /dev/null
+++ b/libblkid/src/blkidP.h
@@ -0,0 +1,545 @@
+/*
+ * 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>
+
+#include "c.h"
+#include "bitops.h" /* $(top_srcdir)/include/ */
+#include "blkdev.h"
+
+#include "debug.h"
+#include "blkid.h"
+#include "list.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 inode pathname */
+ 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 *);
+};
+
+/*
+ * Low-level probe result
+ */
+#define BLKID_PROBVAL_BUFSIZ 128
+
+#define BLKID_NVALS_SUBLKS 18
+#define BLKID_NVALS_TOPLGY 5
+#define BLKID_NVALS_PARTS 13
+
+/* Max number of all values in probing result */
+#define BLKID_NVALS (BLKID_NVALS_SUBLKS + \
+ BLKID_NVALS_TOPLGY + \
+ BLKID_NVALS_PARTS)
+
+struct blkid_prval
+{
+ const char *name; /* value name */
+ unsigned char data[BLKID_PROBVAL_BUFSIZ]; /* value data */
+ size_t len; /* length of value data */
+
+ struct blkid_chain *chain; /* owner */
+};
+
+/*
+ * Filesystem / Raid magic strings
+ */
+struct blkid_idmag
+{
+ const char *magic; /* magic string */
+ unsigned int len; /* length of magic */
+
+ long kboff; /* kilobyte offset of superblock */
+ unsigned int sboff; /* byte offset within superblock */
+};
+
+/*
+ * 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;
+ blkid_loff_t off;
+ blkid_loff_t len;
+ struct list_head bufs; /* list of buffers */
+};
+
+/*
+ * Low-level probing control struct
+ */
+struct blkid_struct_probe
+{
+ int fd; /* device file descriptor */
+ blkid_loff_t off; /* begin of data on the device */
+ blkid_loff_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 */
+
+ int flags; /* private libray flags */
+ int prob_flags; /* always zeroized by blkid_do_*() */
+
+ blkid_loff_t wipe_off; /* begin of the wiped area */
+ blkid_loff_t wipe_size; /* size of the wiped area */
+ struct blkid_chain *wipe_chain; /* superblock, partition, ... */
+
+ struct list_head buffers; /* list of buffers */
+
+ struct blkid_chain chains[BLKID_NCHAINS]; /* array of chains */
+ struct blkid_chain *cur_chain; /* current chain */
+
+ struct blkid_prval vals[BLKID_NVALS]; /* results */
+ int nvals; /* number of assigned vals */
+
+ 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 */
+
+/* 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_PROBE_OK 0
+#define BLKID_PROBE_NONE 1
+
+#define BLKID_ERR_IO 5
+#define BLKID_ERR_PROC 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_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)
+
+extern void blkid_debug_dump_dev(blkid_dev dev);
+extern void blkid_debug_dump_tag(blkid_tag tag);
+
+
+/* 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 major)
+ __attribute__((warn_unused_result));
+
+/* lseek.c */
+extern blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence);
+
+/* 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 unsigned char *blkid_probe_get_buffer(blkid_probe pr,
+ blkid_loff_t off, blkid_loff_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,
+ blkid_loff_t *off, blkid_loff_t *size)
+ __attribute__((nonnull));
+
+extern int blkid_probe_set_dimension(blkid_probe pr,
+ blkid_loff_t off, blkid_loff_t size)
+ __attribute__((nonnull));
+
+extern int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
+ blkid_loff_t *offset, const struct blkid_idmag **res)
+ __attribute__((nonnull(1)));
+
+/* returns superblok according to 'struct blkid_idmag' */
+#define blkid_probe_get_sb(_pr, _mag, type) \
+ ((type *) blkid_probe_get_buffer((_pr),\
+ (_mag)->kboff << 10, 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,
+ blkid_loff_t offset, blkid_loff_t size)
+ __attribute__((warn_unused_result));
+
+extern void blkid_probe_chain_reset_vals(blkid_probe pr, struct blkid_chain *chn)
+ __attribute__((nonnull));
+extern int blkid_probe_chain_copy_vals(blkid_probe pr,
+ struct blkid_chain *chn,
+ struct blkid_prval *vals,
+ int nvals)
+ __attribute__((nonnull));
+
+extern struct blkid_prval *blkid_probe_assign_value(blkid_probe pr,
+ const char *name)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+
+extern int blkid_probe_reset_last_value(blkid_probe pr)
+ __attribute__((nonnull));
+extern void blkid_probe_append_vals(blkid_probe pr,
+ struct blkid_prval *vals,
+ int nvals)
+ __attribute__((nonnull));
+
+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 int blkid_probe_set_value(blkid_probe pr, const char *name,
+ 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, blkid_loff_t offset,
+ size_t len, unsigned char *magic)
+ __attribute__((nonnull));
+
+extern int blkid_probe_verify_csum(blkid_probe pr, uint64_t csum, uint64_t 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, blkid_loff_t off,
+ blkid_loff_t size)
+ __attribute__((nonnull));
+extern int blkid_probe_is_wiped(blkid_probe pr, struct blkid_chain **chn,
+ blkid_loff_t off, blkid_loff_t size)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+extern void blkid_probe_use_wiper(blkid_probe pr, blkid_loff_t off, blkid_loff_t size)
+ __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))
+
+/* encode.c */
+extern size_t blkid_encode_to_utf8(int enc, unsigned char *dest, size_t len,
+ const unsigned char *src, size_t count)
+ __attribute__((nonnull));
+
+#define BLKID_ENC_UTF16BE 0
+#define BLKID_ENC_UTF16LE 1
+
+#endif /* _BLKID_BLKIDP_H */
diff --git a/libblkid/src/c.h b/libblkid/src/c.h
new file mode 100644
index 000000000..6fcbc6da4
--- /dev/null
+++ b/libblkid/src/c.h
@@ -0,0 +1,295 @@
+/*
+ * Fundamental C definitions.
+ */
+
+#ifndef UTIL_LINUX_C_H
+#define UTIL_LINUX_C_H
+
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "blkid.h"
+#ifdef HAVE_ERR_H
+# include <err.h>
+#endif
+
+#ifndef HAVE_USLEEP
+# include <time.h>
+#endif
+
+/*
+ * Compiler specific stuff
+ */
+#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
+
+#ifdef __GNUC__
+
+/* &a[0] degrades to a pointer: a different type from an array */
+# define __must_be_array(a) \
+ UL_BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(__typeof__(a), __typeof__(&a[0])))
+
+# define ignore_result(x) ({ \
+ __typeof__(x) __dummy __attribute__((__unused__)) = (x); (void) __dummy; \
+})
+
+#else /* !__GNUC__ */
+# define __must_be_array(a) 0
+# define __attribute__(_arg_)
+# define ignore_result(x) ((void) (x))
+#endif /* !__GNUC__ */
+
+/*
+ * Function attributes
+ */
+#ifndef __ul_alloc_size
+# if __GNUC_PREREQ (4, 3)
+# define __ul_alloc_size(s) __attribute__((alloc_size(s)))
+# else
+# define __ul_alloc_size(s)
+# endif
+#endif
+
+#ifndef __ul_calloc_size
+# if __GNUC_PREREQ (4, 3)
+# define __ul_calloc_size(n, s) __attribute__((alloc_size(n, s)))
+# else
+# define __ul_calloc_size(n, s)
+# endif
+#endif
+
+/* Force a compilation error if condition is true, but also produce a
+ * result (of value 0 and type size_t), so the expression can be used
+ * e.g. in a structure initializer (or where-ever else comma expressions
+ * aren't permitted).
+ */
+#define UL_BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
+#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
+#endif
+
+#ifndef PATH_MAX
+# define PATH_MAX 4096
+#endif
+
+#ifndef TRUE
+# define TRUE 1
+#endif
+
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#ifndef min
+# define min(x, y) ({ \
+ __typeof__(x) _min1 = (x); \
+ __typeof__(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+#endif
+
+#ifndef max
+# define max(x, y) ({ \
+ __typeof__(x) _max1 = (x); \
+ __typeof__(y) _max2 = (y); \
+ (void) (&_max1 == &_max2); \
+ _max1 > _max2 ? _max1 : _max2; })
+#endif
+
+#ifndef cmp_numbers
+# define cmp_numbers(x, y) __extension__ ({ \
+ __typeof__(x) _a = (x); \
+ __typeof__(y) _b = (y); \
+ (void) (&_a == &_b); \
+ _a == _b ? 0 : _a > _b ? 1 : -1; })
+#endif
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({ \
+ const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
+#ifndef HAVE_PROGRAM_INVOCATION_SHORT_NAME
+# ifdef HAVE___PROGNAME
+extern char *__progname;
+# define program_invocation_short_name __progname
+# else
+# ifdef HAVE_GETEXECNAME
+# define program_invocation_short_name \
+ prog_inv_sh_nm_from_file(getexecname(), 0)
+# else
+# define program_invocation_short_name \
+ prog_inv_sh_nm_from_file(__FILE__, 1)
+# endif
+static char prog_inv_sh_nm_buf[256];
+static inline char *
+prog_inv_sh_nm_from_file(char *f, char stripext)
+{
+ char *t;
+
+ if ((t = strrchr(f, '/')) != NULL)
+ t++;
+ else
+ t = f;
+
+ strncpy(prog_inv_sh_nm_buf, t, sizeof(prog_inv_sh_nm_buf) - 1);
+ prog_inv_sh_nm_buf[sizeof(prog_inv_sh_nm_buf) - 1] = '\0';
+
+ if (stripext && (t = strrchr(prog_inv_sh_nm_buf, '.')) != NULL)
+ *t = '\0';
+
+ return prog_inv_sh_nm_buf;
+}
+# endif
+#endif
+
+
+#ifndef HAVE_ERR_H
+static inline void
+errmsg(char doexit, int excode, char adderr, const char *fmt, ...)
+{
+ fprintf(stderr, "%s: ", program_invocation_short_name);
+ if (fmt != NULL) {
+ va_list argp;
+ va_start(argp, fmt);
+ vfprintf(stderr, fmt, argp);
+ va_end(argp);
+ if (adderr)
+ fprintf(stderr, ": ");
+ }
+ if (adderr)
+ fprintf(stderr, "%m");
+ fprintf(stderr, "\n");
+ if (doexit)
+ exit(excode);
+}
+
+#ifndef HAVE_ERR
+# define err(E, FMT...) errmsg(1, E, 1, FMT)
+#endif
+
+#ifndef HAVE_ERRX
+# define errx(E, FMT...) errmsg(1, E, 0, FMT)
+#endif
+
+#ifndef HAVE_WARN
+# define warn(FMT...) errmsg(0, 0, 1, FMT)
+#endif
+
+#ifndef HAVE_WARNX
+# define warnx(FMT...) errmsg(0, 0, 0, FMT)
+#endif
+#endif /* !HAVE_ERR_H */
+
+
+static inline __attribute__((const)) int is_power_of_2(unsigned long num)
+{
+ return (num != 0 && ((num & (num - 1)) == 0));
+}
+
+#ifndef HAVE_LOFF_T
+typedef int64_t loff_t;
+#endif
+
+#if !defined(HAVE_DIRFD) && (!defined(HAVE_DECL_DIRFD) || HAVE_DECL_DIRFD == 0) && defined(HAVE_DIR_DD_FD)
+#include <sys/types.h>
+#include <dirent.h>
+static inline int dirfd(DIR *d)
+{
+ return d->dd_fd;
+}
+#endif
+
+/*
+ * Fallback defines for old versions of glibc
+ */
+#include <fcntl.h>
+
+#ifdef O_CLOEXEC
+#define UL_CLOEXECSTR "e"
+#else
+#define UL_CLOEXECSTR ""
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0x0020
+#endif
+
+#ifndef IUTF8
+#define IUTF8 0040000
+#endif
+
+/*
+ * MAXHOSTNAMELEN replacement
+ */
+static inline size_t get_hostname_max(void)
+{
+ long len = sysconf(255);
+
+ if (0 < len)
+ return len;
+
+#ifdef MAXHOSTNAMELEN
+ return MAXHOSTNAMELEN;
+#elif HOST_NAME_MAX
+ return HOST_NAME_MAX;
+#endif
+ return 64;
+}
+
+/*
+ * Constant strings for usage() functions. For more info see
+ * Documentation/howto-usage-function.txt and sys-utils/arch.c
+ */
+#define USAGE_HEADER _("\nUsage:\n")
+#define USAGE_OPTIONS _("\nOptions:\n")
+#define USAGE_SEPARATOR _("\n")
+#define USAGE_HELP _(" -h, --help display this help and exit\n")
+#define USAGE_VERSION _(" -V, --version output version information and exit\n")
+#define USAGE_MAN_TAIL(_man) _("\nFor more details see %s.\n"), _man
+
+#define UTIL_LINUX_VERSION _("%s from %s\n"), program_invocation_short_name, PACKAGE_STRING
+
+/*
+ * scanf modifiers for "strings allocation"
+ */
+#ifdef HAVE_SCANF_MS_MODIFIER
+#define UL_SCNsA "%ms"
+#elif defined(HAVE_SCANF_AS_MODIFIER)
+#define UL_SCNsA "%as"
+#endif
+
+/*
+ * seek stuff
+ */
+#ifndef SEEK_DATA
+# define SEEK_DATA 3
+#endif
+#ifndef SEEK_HOLE
+# define SEEK_HOLE 4
+#endif
+
+#endif /* UTIL_LINUX_C_H */
diff --git a/libblkid/src/cache.c b/libblkid/src/cache.c
new file mode 100644
index 000000000..b576df8b4
--- /dev/null
+++ b/libblkid/src/cache.c
@@ -0,0 +1,226 @@
+/*
+ * 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 initialize librray 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;
+
+ blkid_init_debug(0);
+
+ DBG(CACHE, ul_debug("creating blkid cache (using %s)",
+ filename ? filename : "default cache"));
+
+ if (!(cache = (blkid_cache) calloc(1, sizeof(struct blkid_struct_cache))))
+ return -BLKID_ERR_MEM;
+
+ 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_debug("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);
+ }
+
+ 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_debug("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_debug("freeing %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/canonicalize.h b/libblkid/src/canonicalize.h
new file mode 100644
index 000000000..7a18aca09
--- /dev/null
+++ b/libblkid/src/canonicalize.h
@@ -0,0 +1,21 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 Library Public License for more details.
+ */
+#ifndef CANONICALIZE_H
+#define CANONICALIZE_H
+
+#include "c.h" /* for PATH_MAX */
+
+extern char *canonicalize_path(const char *path);
+extern char *canonicalize_path_restricted(const char *path);
+extern char *canonicalize_dm_name(const char *ptname);
+
+#endif /* CANONICALIZE_H */
diff --git a/libblkid/src/closestream.h b/libblkid/src/closestream.h
new file mode 100644
index 000000000..7842456fb
--- /dev/null
+++ b/libblkid/src/closestream.h
@@ -0,0 +1,71 @@
+#ifndef UTIL_LINUX_CLOSESTREAM_H
+#define UTIL_LINUX_CLOSESTREAM_H
+
+#include <stdio.h>
+#ifdef HAVE_STDIO_EXT_H
+#include <stdio_ext.h>
+#endif
+#include <unistd.h>
+
+#include "c.h"
+#include "nls.h"
+
+#ifndef HAVE___FPENDING
+static inline int
+__fpending(FILE *stream __attribute__((__unused__)))
+{
+ return 0;
+}
+#endif
+
+static inline int
+close_stream(FILE * stream)
+{
+ const int some_pending = (__fpending(stream) != 0);
+ const int prev_fail = (ferror(stream) != 0);
+ const int fclose_fail = (fclose(stream) != 0);
+
+ if (prev_fail || (fclose_fail && (some_pending || errno != EBADF))) {
+ if (!fclose_fail && !(errno == EPIPE))
+ errno = 0;
+ return EOF;
+ }
+ return 0;
+}
+
+/* Meant to be used atexit(close_stdout); */
+static inline void
+close_stdout(void)
+{
+ if (close_stream(stdout) != 0 && !(errno == EPIPE)) {
+ if (errno)
+ warn(_("write error"));
+ else
+ warnx(_("write error"));
+ _exit(EXIT_FAILURE);
+ }
+
+ if (close_stream(stderr) != 0)
+ _exit(EXIT_FAILURE);
+}
+
+#ifndef HAVE_FSYNC
+static inline int
+fsync(int fd __attribute__((__unused__)))
+{
+ return 0;
+}
+#endif
+
+static inline int
+close_fd(int fd)
+{
+ const int fsync_fail = (fsync(fd) != 0);
+ const int close_fail = (close(fd) != 0);
+
+ if (fsync_fail || close_fail)
+ return EOF;
+ return 0;
+}
+
+#endif /* UTIL_LINUX_CLOSESTREAM_H */
diff --git a/libblkid/src/colors.h b/libblkid/src/colors.h
new file mode 100644
index 000000000..97efc486a
--- /dev/null
+++ b/libblkid/src/colors.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2012-2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_COLORS_H
+#define UTIL_LINUX_COLORS_H
+
+#include <stdio.h>
+#include <unistd.h>
+
+#define UL_COLOR_RESET "\033[0m"
+#define UL_COLOR_BOLD "\033[1m"
+#define UL_COLOR_HALFBRIGHT "\033[2m"
+#define UL_COLOR_UNDERSCORE "\033[4m"
+#define UL_COLOR_BLINK "\033[5m"
+#define UL_COLOR_REVERSE "\033[7m"
+
+/* Standard colors */
+#define UL_COLOR_BLACK "\033[30m"
+#define UL_COLOR_RED "\033[31m"
+#define UL_COLOR_GREEN "\033[32m"
+#define UL_COLOR_BROWN "\033[33m" /* well, brown */
+#define UL_COLOR_BLUE "\033[34m"
+#define UL_COLOR_MAGENTA "\033[35m"
+#define UL_COLOR_CYAN "\033[36m"
+#define UL_COLOR_GRAY "\033[37m"
+
+/* Bold variants */
+#define UL_COLOR_DARK_GRAY "\033[1;30m"
+#define UL_COLOR_BOLD_RED "\033[1;31m"
+#define UL_COLOR_BOLD_GREEN "\033[1;32m"
+#define UL_COLOR_BOLD_YELLOW "\033[1;33m"
+#define UL_COLOR_BOLD_BLUE "\033[1;34m"
+#define UL_COLOR_BOLD_MAGENTA "\033[1;35m"
+#define UL_COLOR_BOLD_CYAN "\033[1;36m"
+
+#define UL_COLOR_WHITE "\033[1;37m"
+
+/* --color[=WHEN] */
+enum colortmode {
+ UL_COLORMODE_AUTO = 0,
+ UL_COLORMODE_NEVER,
+ UL_COLORMODE_ALWAYS,
+ UL_COLORMODE_UNDEF,
+
+ __UL_NCOLORMODES /* last */
+};
+
+extern int colormode_from_string(const char *str);
+extern int colormode_or_err(const char *str, const char *errmsg);
+
+/* Initialize the global variable UL_COLOR_TERM_OK */
+extern int colors_init(int mode, const char *util_name);
+
+/* Returns 1 or 0 */
+extern int colors_wanted(void);
+
+/* temporary enable/disable colors */
+extern void colors_off(void);
+extern void colors_on(void);
+
+
+/* Set the color */
+extern void color_fenable(const char *seq, FILE *f);
+
+extern void color_scheme_fenable(const char *name, const char *dflt, FILE *f);
+extern const char *color_scheme_get_sequence(const char *name, const char *dflt);
+
+static inline void color_enable(const char *seq)
+{
+ color_fenable(seq, stdout);
+}
+
+static inline void color_scheme_enable(const char *name, const char *dflt)
+{
+ color_scheme_fenable(name, dflt, stdout);
+}
+
+/* Reset colors to default */
+extern void color_fdisable(FILE *f);
+
+static inline void color_disable(void)
+{
+ color_fdisable(stdout);
+}
+
+/* converts "red" to UL_COLOR_RED, etc. */
+extern const char *color_sequence_from_colorname(const char *str);
+
+
+#endif /* UTIL_LINUX_COLORS_H */
diff --git a/libblkid/src/config.c b/libblkid/src/config.c
new file mode 100644
index 000000000..3c7f3124c
--- /dev/null
+++ b/libblkid/src/config.c
@@ -0,0 +1,198 @@
+/*
+ * 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 += 13;
+ if (*s && !strcasecmp(s, "yes"))
+ conf->uevent = TRUE;
+ else if (*s)
+ conf->uevent = FALSE;
+ } else if (!strncmp(s, "CACHE_FILE=", 11)) {
+ s += 11;
+ if (*s)
+ conf->cachefile = strdup(s);
+ } 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 = (struct blkid_config *) 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);
+ 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/crc32.h b/libblkid/src/crc32.h
new file mode 100644
index 000000000..26169109e
--- /dev/null
+++ b/libblkid/src/crc32.h
@@ -0,0 +1,10 @@
+#ifndef UL_NG_CRC32_H
+#define UL_NG_CRC32_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+extern uint32_t crc32(uint32_t seed, const unsigned char *buf, size_t len);
+
+#endif
+
diff --git a/libblkid/src/debug.h b/libblkid/src/debug.h
new file mode 100644
index 000000000..0229ab329
--- /dev/null
+++ b/libblkid/src/debug.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_DEBUG_H
+#define UTIL_LINUX_DEBUG_H
+
+
+/*
+ * util-linux debug macros
+ *
+ * The debug stuff is based on <name>_debug_mask that controls what outputs is
+ * expected. The mask is usually initialized by <NAME>_DEBUG= env.variable
+ *
+ * After successful initialization the flag <PREFIX>_DEBUG_INIT is always set
+ * to the mask (this flag is required). The <PREFIX> is usually library API
+ * prefix (e.g. MNT_) or program name (e.g. CFDISK_)
+ *
+ * In the code is possible to use
+ *
+ * DBG(FOO, ul_debug("this is output for foo"));
+ *
+ * where for the FOO has to be defined <PREFIX>_DEBUG_FOO.
+ *
+ * It's possible to initialize the mask by comma delimited strings with
+ * subsystem names (e.g. "LIBMOUNT_DEBUG=options,tab"). In this case is
+ * necessary to define mask names array. This functionality is optional.
+ *
+ * It's stringly recommended to use UL_* macros to define/declare/use
+ * the debug stuff.
+ *
+ * See disk-utils/cfdisk.c: cfdisk_init_debug() for programs debug
+ * or libmount/src/init.c: mnt_init_debug() for library debug
+ *
+ */
+
+#include <stdarg.h>
+#include <string.h>
+
+struct ul_debug_maskname {
+ const char *name;
+ int mask;
+ const char *help;
+};
+#define UL_DEBUG_EMPTY_MASKNAMES {{ NULL, 0, NULL }}
+#define UL_DEBUG_DEFINE_MASKNAMES(m) static const struct ul_debug_maskname m ## _masknames[]
+#define UL_DEBUG_MASKNAMES(m) m ## _masknames
+
+#define UL_DEBUG_DEFINE_MASK(m) int m ## _debug_mask
+#define UL_DEBUG_DECLARE_MASK(m) extern UL_DEBUG_DEFINE_MASK(m)
+
+/* p - flag prefix, m - flag postfix */
+#define UL_DEBUG_DEFINE_FLAG(p, m) p ## m
+
+/* l - library name, p - flag prefix, m - flag postfix, x - function */
+#define __UL_DBG(l, p, m, x) \
+ do { \
+ if ((p ## m) & l ## _debug_mask) { \
+ fprintf(stderr, "%d: %s: %8s: ", getpid(), # l, # m); \
+ x; \
+ } \
+ } while (0)
+
+#define __UL_DBG_CALL(l, p, m, x) \
+ do { \
+ if ((p ## m) & l ## _debug_mask) { \
+ x; \
+ } \
+ } while (0)
+
+#define __UL_DBG_FLUSH(l, p) \
+ do { \
+ if (l ## _debug_mask && \
+ l ## _debug_mask != p ## INIT) { \
+ fflush(stderr); \
+ } \
+ } while (0)
+
+
+#define __UL_INIT_DEBUG(lib, pref, mask, env) \
+ do { \
+ if (lib ## _debug_mask & pref ## INIT) \
+ ; \
+ else if (!mask) { \
+ char *str = getenv(# env); \
+ if (str) \
+ lib ## _debug_mask = ul_debug_parse_envmask(lib ## _masknames, str); \
+ } else \
+ lib ## _debug_mask = mask; \
+ lib ## _debug_mask |= pref ## INIT; \
+ } while (0)
+
+
+static inline void __attribute__ ((__format__ (__printf__, 1, 2)))
+ul_debug(const char *mesg, ...)
+{
+ va_list ap;
+ va_start(ap, mesg);
+ vfprintf(stderr, mesg, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+}
+
+static inline void __attribute__ ((__format__ (__printf__, 2, 3)))
+ul_debugobj(void *handler, const char *mesg, ...)
+{
+ va_list ap;
+
+ if (handler)
+ fprintf(stderr, "[%p]: ", handler);
+ va_start(ap, mesg);
+ vfprintf(stderr, mesg, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+}
+
+static inline int ul_debug_parse_envmask(
+ const struct ul_debug_maskname flagnames[],
+ const char *mask)
+{
+ int res;
+ char *ptr;
+
+ /* let's check for a numeric mask first */
+ res = strtoul(mask, &ptr, 0);
+
+ /* perhaps it's a comma-separated string? */
+ if (ptr && *ptr && flagnames && flagnames[0].name) {
+ char *msbuf, *ms, *name;
+ res = 0;
+
+ ms = msbuf = strdup(mask);
+ if (!ms)
+ return res;
+
+ while ((name = strtok_r(ms, ",", &ptr))) {
+ const struct ul_debug_maskname *d;
+ ms = ptr;
+
+ for (d = flagnames; d && d->name; d++) {
+ if (strcmp(name, d->name) == 0) {
+ res |= d->mask;
+ break;
+ }
+ }
+ /* nothing else we can do by OR-ing the mask */
+ if (res == 0xffff)
+ break;
+ }
+ free(msbuf);
+ } else if (ptr && strcmp(ptr, "all") == 0)
+ res = 0xffff;
+
+ return res;
+}
+
+static inline void ul_debug_print_masks(
+ const char *env,
+ const struct ul_debug_maskname flagnames[])
+{
+ const struct ul_debug_maskname *d;
+
+ if (!flagnames)
+ return;
+
+ fprintf(stderr, "Available \"%s=<name>[,...]|<mask>\" debug masks:\n",
+ env);
+ for (d = flagnames; d && d->name; d++) {
+ if (!d->help)
+ continue;
+ fprintf(stderr, " %-8s [0x%04x] : %s\n",
+ d->name, d->mask, d->help);
+ }
+}
+
+#endif /* UTIL_LINUX_DEBUG_H */
diff --git a/libblkid/src/dev.c b/libblkid/src/dev.c
new file mode 100644
index 000000000..9491331ab
--- /dev/null
+++ b/libblkid/src/dev.c
@@ -0,0 +1,274 @@
+/*
+ * 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 = (blkid_dev) calloc(1, sizeof(struct blkid_struct_dev))))
+ return NULL;
+
+ 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_debug(" freeing dev %s (%s)", dev->bid_name, dev->bid_type ?
+ dev->bid_type : "(null)"));
+ DBG(DEV, blkid_debug_dump_dev(dev));
+
+ 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_name);
+ free(dev);
+}
+
+/*
+ * Given a blkid device, return its name
+ */
+const char *blkid_dev_devname(blkid_dev dev)
+{
+ return dev ? dev->bid_name : NULL;
+}
+
+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%0llx\"\n", (long long)dev->bid_devno);
+ fprintf(stderr, " dev: TIME=\"%ld.%ld\"\n", (long)dev->bid_time, (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 implemenation. 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 tradeoff 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 = 0;
+ iter->search_value = 0;
+ }
+ return iter;
+}
+
+int blkid_dev_set_search(blkid_dev_iterate iter,
+ char *search_type, 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 = 0;
+ 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
+
+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 000000000..d98af04b1
--- /dev/null
+++ b/libblkid/src/devname.c
@@ -0,0 +1,676 @@
+/*
+ * 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 <sys/sysmacros.h>
+
+#include "blkidP.h"
+
+#include "canonicalize.h" /* $(top_srcdir)/include */
+#include "pathnames.h"
+#include "sysfs.h"
+#include "at.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;
+
+ if (!cache || !devname)
+ return NULL;
+
+ list_for_each(p, &cache->bic_devs) {
+ tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
+ if (strcmp(tmp->bid_name, devname))
+ continue;
+
+ DBG(DEVNAME, ul_debug("found devname %s in cache", tmp->bid_name));
+ dev = tmp;
+ break;
+ }
+
+ if (!dev && (flags & BLKID_DEV_CREATE)) {
+ if (access(devname, F_OK) < 0)
+ return NULL;
+ dev = blkid_new_dev();
+ if (!dev)
+ return NULL;
+ dev->bid_time = INT_MIN;
+ 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))
+ return dev;
+ /*
+ * 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))
+ continue;
+ if (dev->bid_label && dev2->bid_label &&
+ strcmp(dev->bid_label, dev2->bid_label))
+ continue;
+ if (dev->bid_uuid && dev2->bid_uuid &&
+ strcmp(dev->bid_uuid, dev2->bid_uuid))
+ 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);
+ }
+ }
+ return dev;
+}
+
+/* Directories where we will try to search for device names */
+static const char *dirlist[] = { "/dev", "/devfs", "/devices", NULL };
+
+static int is_dm_leaf(const char *devname)
+{
+ struct dirent *de, *d_de;
+ DIR *dir, *d_dir;
+ char path[256];
+ int ret = 1;
+
+ if ((dir = opendir("/sys/block")) == NULL)
+ return 0;
+ while ((de = readdir(dir)) != NULL) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
+ !strcmp(de->d_name, devname) ||
+ strncmp(de->d_name, "dm-", 3) ||
+ strlen(de->d_name) > sizeof(path)-32)
+ continue;
+ sprintf(path, "/sys/block/%s/slaves", de->d_name);
+ if ((d_dir = opendir(path)) == NULL)
+ continue;
+ while ((d_de = readdir(d_dir)) != NULL) {
+ if (!strcmp(d_de->d_name, devname)) {
+ ret = 0;
+ break;
+ }
+ }
+ closedir(d_dir);
+ if (!ret)
+ break;
+ }
+ 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 = 0;
+ }
+ }
+ 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, 0, &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, 0, &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/", 11)) {
+ 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;
+ }
+ return;
+}
+
+#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;
+
+ vg_name = vg_iter->d_name;
+ if (!strcmp(vg_name, ".") || !strcmp(vg_name, ".."))
+ continue;
+ vdirname = malloc(vg_len + strlen(vg_name) + 8);
+ if (!vdirname)
+ goto exit;
+ sprintf(vdirname, "%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;
+
+ lvm_device = malloc(vg_len + strlen(vg_name) +
+ strlen(lv_name) + 8);
+ if (!lvm_device) {
+ closedir(lv_list);
+ goto exit;
+ }
+ sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name,
+ lv_name);
+ dev = lvm_get_devno(lvm_device);
+ sprintf(lvm_device, "%s/%s", vg_name, lv_name);
+ DBG(DEVNAME, ul_debug("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
+
+#define PROC_EVMS_VOLUMES "/proc/evms/volumes"
+
+static int
+evms_probe_all(blkid_cache cache, int only_if_new)
+{
+ char line[100];
+ int ma, mi, sz, num = 0;
+ FILE *procpt;
+ char device[110];
+
+ procpt = fopen(PROC_EVMS_VOLUMES, "r" UL_CLOEXECSTR);
+ if (!procpt)
+ return 0;
+ while (fgets(line, sizeof(line), procpt)) {
+ if (sscanf (line, " %d %d %d %*s %*s %[^\n ]",
+ &ma, &mi, &sz, device) != 4)
+ continue;
+
+ DBG(DEVNAME, ul_debug("Checking partition %s (%d, %d)",
+ device, ma, mi));
+
+ probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS,
+ only_if_new, 0);
+ num++;
+ }
+ fclose(procpt);
+ return num;
+}
+
+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 (fstat_at(dirfd(dir), *dirname, name, &st, 0))
+ continue;
+
+ dev = st.st_rdev;
+
+ if (!S_ISCHR(st.st_mode) || !minor(dev))
+ continue;
+ DBG(DEVNAME, ul_debug("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);
+ }
+}
+
+/*
+ * Read the device data for all available block devices in the system.
+ */
+static int probe_all(blkid_cache cache, int only_if_new)
+{
+ FILE *proc;
+ char line[1024];
+ char ptname0[128 + 1], ptname1[128 + 1], *ptname = 0;
+ char *ptnames[2];
+ dev_t devs[2];
+ int ma, mi;
+ unsigned long long sz;
+ int lens[2] = { 0, 0 };
+ int which = 0, last = 0;
+ struct list_head *p, *pnext;
+
+ ptnames[0] = ptname0;
+ ptnames[1] = ptname1;
+
+ if (!cache)
+ return -BLKID_ERR_PARAM;
+
+ if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
+ time(0) - cache->bic_time < BLKID_PROBE_INTERVAL)
+ return 0;
+
+ blkid_read_cache(cache);
+ evms_probe_all(cache, only_if_new);
+#ifdef VG_DIR
+ lvm_probe_all(cache, only_if_new);
+#endif
+ ubi_probe_all(cache, only_if_new);
+
+ proc = fopen(PROC_PARTITIONS, "r" UL_CLOEXECSTR);
+ if (!proc)
+ return -BLKID_ERR_PROC;
+
+ while (fgets(line, sizeof(line), proc)) {
+ last = which;
+ which ^= 1;
+ ptname = ptnames[which];
+
+ if (sscanf(line, " %d %d %llu %128[^\n ]",
+ &ma, &mi, &sz, ptname) != 4)
+ continue;
+ devs[which] = makedev(ma, mi);
+
+ DBG(DEVNAME, ul_debug("read partition name %s", ptname));
+
+ /* Skip whole disk devs unless they have no partitions.
+ * If base name of device has changed, also
+ * check previous dev to see if it didn't have a partn.
+ * heuristic: partition name ends in a digit, & partition
+ * names contain whole device name as substring.
+ *
+ * Skip extended partitions.
+ * heuristic: size is 1
+ *
+ * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs
+ */
+
+ lens[which] = strlen(ptname);
+
+ /* ends in a digit, clearly a partition, so check */
+ if (isdigit(ptname[lens[which] - 1])) {
+ DBG(DEVNAME, ul_debug("partition dev %s, devno 0x%04X",
+ ptname, (unsigned int) devs[which]));
+
+ if (sz > 1)
+ probe_one(cache, ptname, devs[which], 0,
+ only_if_new, 0);
+ lens[which] = 0; /* mark as checked */
+ }
+
+ /*
+ * If last was a whole disk and we just found a partition
+ * on it, remove the whole-disk dev from the cache if
+ * it exists.
+ */
+ if (lens[last] && !strncmp(ptnames[last], ptname, lens[last])) {
+ list_for_each_safe(p, pnext, &cache->bic_devs) {
+ blkid_dev tmp;
+
+ /* find blkid dev for the whole-disk devno */
+ tmp = list_entry(p, struct blkid_struct_dev,
+ bid_devs);
+ if (tmp->bid_devno == devs[last]) {
+ DBG(DEVNAME, ul_debug("freeing %s",
+ tmp->bid_name));
+ blkid_free_dev(tmp);
+ cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+ break;
+ }
+ }
+ lens[last] = 0;
+ }
+ /*
+ * If last was not checked because it looked like a whole-disk
+ * dev, and the device's base name has changed,
+ * check last as well.
+ */
+ if (lens[last] && strncmp(ptnames[last], ptname, lens[last])) {
+ DBG(DEVNAME, ul_debug("whole dev %s, devno 0x%04X",
+ ptnames[last], (unsigned int) devs[last]));
+ probe_one(cache, ptnames[last], devs[last], 0,
+ only_if_new, 0);
+ lens[last] = 0;
+ }
+ }
+
+ /* Handle the last device if it wasn't partitioned */
+ if (lens[which])
+ probe_one(cache, ptname, devs[which], 0, only_if_new, 0);
+
+ fclose(proc);
+ blkid_flush_cache(cache);
+ return 0;
+}
+
+/* Don't use it by default -- it's pretty slow (because cdroms, floppy, ...)
+ */
+static int probe_all_removable(blkid_cache cache)
+{
+ DIR *dir;
+ struct dirent *d;
+
+ if (!cache)
+ return -BLKID_ERR_PARAM;
+
+ dir = opendir(_PATH_SYS_BLOCK);
+ if (!dir)
+ return -BLKID_ERR_PROC;
+
+ while((d = readdir(dir))) {
+ struct sysfs_cxt sysfs = UL_SYSFSCXT_EMPTY;
+ int removable = 0;
+ dev_t devno;
+
+#ifdef _DIRENT_HAVE_D_TYPE
+ if (d->d_type != DT_UNKNOWN && d->d_type != DT_LNK)
+ continue;
+#endif
+ if (d->d_name[0] == '.' &&
+ ((d->d_name[1] == 0) ||
+ ((d->d_name[1] == '.') && (d->d_name[2] == 0))))
+ continue;
+
+ devno = sysfs_devname_to_devno(d->d_name, NULL);
+ if (!devno)
+ continue;
+
+ if (sysfs_init(&sysfs, devno, NULL) == 0) {
+ if (sysfs_read_int(&sysfs, "removable", &removable) != 0)
+ removable = 0;
+ sysfs_deinit(&sysfs);
+ }
+
+ if (removable)
+ probe_one(cache, d->d_name, devno, 0, 0, 1);
+ }
+
+ closedir(dir);
+ 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);
+ if (ret == 0) {
+ cache->bic_time = time(0);
+ cache->bic_flags |= BLKID_BIC_FL_PROBED;
+ }
+ 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);
+ 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 = probe_all_removable(cache);
+ 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 000000000..f4a36e4f5
--- /dev/null
+++ b/libblkid/src/devno.c
@@ -0,0 +1,375 @@
+/*
+ * 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 "at.h"
+#include "sysfs.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) {
+ memcpy(p, a, al);
+ p += al;
+ }
+ if (bl) {
+ memcpy(p, b, bl);
+ p += bl;
+ }
+ if (cl) {
+ memcpy(p, c, cl);
+ p += 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 (fstat_at(dirfd(dir), dirname, 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 (fstat_at(dirfd(dir), dirname, dp->d_name, &st, 1) ||
+ !S_ISDIR(st.st_mode))
+ continue; /* symlink or lstat() 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);
+ return;
+}
+
+/* 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 = NULL;
+ 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 major)
+{
+ 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 == major && strcmp(name, drvname) == 0) {
+ match = 1;
+ break;
+ }
+ }
+
+ fclose(f);
+
+ DBG(DEVNO, ul_debug("major %d %s associated with '%s' driver",
+ major, match ? "is" : "is NOT", drvname));
+ return match;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+ char *devname, *tmp;
+ char diskname[PATH_MAX];
+ int major, minor;
+ 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 {
+ major = strtoul(argv[1], &tmp, 0);
+ if (*tmp) {
+ fprintf(stderr, errmsg, "major number", argv[1]);
+ exit(1);
+ }
+ minor = strtoul(argv[2], &tmp, 0);
+ if (*tmp) {
+ fprintf(stderr, errmsg, "minor number", argv[2]);
+ exit(1);
+ }
+ devno = makedev(major, minor);
+ }
+ 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 000000000..ff57be4cb
--- /dev/null
+++ b/libblkid/src/encode.c
@@ -0,0 +1,340 @@
+
+/*
+ * 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"
+
+#define UDEV_ALLOWED_CHARS_INPUT "/ $%?,"
+
+/**
+ * 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 replace_whitespace(const char *str, char *to, size_t len)
+{
+ size_t i, j;
+
+ /* strip trailing whitespace */
+ len = strnlen(str, len);
+ while (len && isspace(str[len-1]))
+ len--;
+
+ /* strip leading whitespace */
+ i = 0;
+ while (isspace(str[i]) && (i < len))
+ i++;
+
+ j = 0;
+ while (i < len) {
+ /* substitute multiple whitespace with a single '_' */
+ if (isspace(str[i])) {
+ while (isspace(str[i]))
+ i++;
+ to[j++] = '_';
+ }
+ to[j++] = str[i++];
+ }
+ to[j] = '\0';
+ return 0;
+}
+
+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;
+}
+
+/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
+static int replace_chars(char *str, const char *white)
+{
+ size_t i = 0;
+ int replaced = 0;
+
+ while (str[i] != '\0') {
+ int len;
+
+ if (is_whitelisted(str[i], white)) {
+ i++;
+ continue;
+ }
+
+ /* accept hex encoding */
+ if (str[i] == '\\' && str[i+1] == 'x') {
+ i += 2;
+ continue;
+ }
+
+ /* accept valid utf8 */
+ len = utf8_encoded_valid_unichar(&str[i]);
+ if (len > 1) {
+ i += len;
+ continue;
+ }
+
+ /* if space is allowed, replace whitespace with ordinary space */
+ if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) {
+ str[i] = ' ';
+ i++;
+ replaced++;
+ continue;
+ }
+
+ /* everything else is replaced with '_' */
+ str[i] = '_';
+ i++;
+ replaced++;
+ }
+ return replaced;
+}
+
+size_t blkid_encode_to_utf8(int enc, unsigned char *dest, size_t len,
+ const unsigned char *src, size_t count)
+{
+ size_t i, j;
+ uint16_t c;
+
+ for (j = i = 0; i + 2 <= count; i += 2) {
+ if (enc == BLKID_ENC_UTF16LE)
+ c = (src[i+1] << 8) | src[i];
+ else /* BLKID_ENC_UTF16BE */
+ c = (src[i] << 8) | src[i+1];
+ if (c == 0) {
+ dest[j] = '\0';
+ break;
+ } else if (c < 0x80) {
+ if (j+1 >= len)
+ break;
+ dest[j++] = (uint8_t) c;
+ } else if (c < 0x800) {
+ if (j+2 >= len)
+ break;
+ dest[j++] = (uint8_t) (0xc0 | (c >> 6));
+ dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
+ } else {
+ if (j+3 >= len)
+ break;
+ dest[j++] = (uint8_t) (0xe0 | (c >> 12));
+ dest[j++] = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
+ dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
+ }
+ }
+ dest[j] = '\0';
+ return j;
+}
+
+/**
+ * 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
+ *
+ * Allows plain ascii, hex-escaping and valid utf8. Replaces all whitespaces
+ * with '_'.
+ *
+ * Returns: 0 on success or -1 in case of error.
+ */
+int blkid_safe_string(const char *str, char *str_safe, size_t len)
+{
+ if (!str || !str_safe || !len)
+ return -1;
+ replace_whitespace(str, str_safe, len);
+ replace_chars(str_safe, UDEV_ALLOWED_CHARS_INPUT);
+ return 0;
+}
diff --git a/libblkid/src/env.h b/libblkid/src/env.h
new file mode 100644
index 000000000..a53d31027
--- /dev/null
+++ b/libblkid/src/env.h
@@ -0,0 +1,16 @@
+#ifndef UTIL_LINUX_ENV_H
+#define UTIL_LINUX_ENV_H
+
+#include "c.h"
+
+extern void sanitize_env(void);
+extern char *safe_getenv(const char *arg);
+
+static inline void xsetenv(char const *name, char const *val, int overwrite)
+{
+ if (setenv(name, val, overwrite) != 0)
+ err(EXIT_FAILURE, "failed to set the %s environment variable", name);
+}
+
+#endif /* UTIL_LINUX_ENV_H */
+
diff --git a/libblkid/src/evaluate.c b/libblkid/src/evaluate.c
new file mode 100644
index 000000000..3d9a76b0a
--- /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/sysmacros.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;
+
+ 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);
+ 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 ? "SUCCES" : "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 {
+ 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);
+ blkid_get_cache(&c, cachefile);
+ free(cachefile);
+ }
+ 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;
+
+ if (!cache || !*cache)
+ blkid_init_debug(0);
+
+ DBG(EVALUATE, ul_debug("evaluating %s%s%s", token, value ? "=" : "",
+ value ? value : ""));
+
+ if (!value) {
+ if (!strchr(token, '=')) {
+ ret = strdup(token);
+ goto out;
+ }
+ blkid_parse_tag_string(token, &t, &v);
+ if (!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;
+ }
+
+ blkid_init_debug(0);
+
+ 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/exec_shell.h b/libblkid/src/exec_shell.h
new file mode 100644
index 000000000..a2aa757de
--- /dev/null
+++ b/libblkid/src/exec_shell.h
@@ -0,0 +1 @@
+extern void __attribute__((__noreturn__)) exec_shell(void);
diff --git a/libblkid/src/fileutils.h b/libblkid/src/fileutils.h
new file mode 100644
index 000000000..3353f69a0
--- /dev/null
+++ b/libblkid/src/fileutils.h
@@ -0,0 +1,33 @@
+#ifndef UTIL_LINUX_FILEUTILS
+#define UTIL_LINUX_FILEUTILS
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "c.h"
+
+extern int xmkstemp(char **tmpname, char *dir);
+
+static inline FILE *xfmkstemp(char **tmpname, char *dir)
+{
+ int fd;
+ FILE *ret;
+
+ fd = xmkstemp(tmpname, dir);
+ if (fd == -1)
+ return NULL;
+
+ if (!(ret = fdopen(fd, "w+" UL_CLOEXECSTR))) {
+ close(fd);
+ return NULL;
+ }
+ return ret;
+}
+
+extern int get_fd_tabsize(void);
+
+extern int mkdir_p(const char *path, mode_t mode);
+extern char *stripoff_last_component(char *path);
+
+#endif /* UTIL_LINUX_FILEUTILS */
diff --git a/libblkid/src/getsize.c b/libblkid/src/getsize.c
new file mode 100644
index 000000000..abe6ebc9c
--- /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 000000000..eead6c7df
--- /dev/null
+++ b/libblkid/src/init.c
@@ -0,0 +1,66 @@
+/*
+ * 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 debuging
+ */
+
+#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, "convertions to device name" },
+ { "evaluate", BLKID_DEBUG_EVALUATE, "tags resolving" },
+ { "help", BLKID_DEBUG_HELP, "this help" },
+ { "lowprobe", BLKID_DEBUG_LOWPROBE, "superblock/raids/partitions probing" },
+ { "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 debuging)
+ *
+ * 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(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)));
+}
diff --git a/libblkid/src/ismounted.h b/libblkid/src/ismounted.h
new file mode 100644
index 000000000..57918cb3a
--- /dev/null
+++ b/libblkid/src/ismounted.h
@@ -0,0 +1,14 @@
+#ifndef IS_MOUNTED_H
+#define IS_MOUNTED_H
+
+#define MF_MOUNTED 1
+#define MF_ISROOT 2
+#define MF_READONLY 4
+#define MF_SWAP 8
+#define MF_BUSY 16
+
+extern int is_mounted(const char *file);
+extern int check_mount_point(const char *device, int *mount_flags,
+ char *mtpt, int mtlen);
+
+#endif /* IS_MOUNTED_H */
diff --git a/libblkid/src/libblkid.sym b/libblkid/src/libblkid.sym
new file mode 100644
index 000000000..6b3cf0805
--- /dev/null
+++ b/libblkid/src/libblkid.sym
@@ -0,0 +1,166 @@
+/*
+ * 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;
diff --git a/libblkid/src/linux_version.h b/libblkid/src/linux_version.h
new file mode 100644
index 000000000..a6a1e99c7
--- /dev/null
+++ b/libblkid/src/linux_version.h
@@ -0,0 +1,14 @@
+#ifndef LINUX_VERSION_H
+#define LINUX_VERSION_H
+
+#ifdef HAVE_LINUX_VERSION_H
+# include <linux/version.h>
+#endif
+
+#ifndef KERNEL_VERSION
+# define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+int get_linux_version(void);
+
+#endif /* LINUX_VERSION_H */
diff --git a/libblkid/src/list.h b/libblkid/src/list.h
new file mode 100644
index 000000000..7b6067239
--- /dev/null
+++ b/libblkid/src/list.h
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 1999-2008 by Theodore Ts'o
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * (based on list.h from e2fsprogs)
+ * Merge sort based on kernel's implementation.
+ */
+
+#ifndef UTIL_LINUX_LIST_H
+#define UTIL_LINUX_LIST_H
+
+/* TODO: use AC_C_INLINE */
+#ifdef __GNUC__
+#define _INLINE_ static __inline__
+#else /* For Watcom C */
+#define _INLINE_ static inline
+#endif
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+_INLINE_ void __list_add(struct list_head * add,
+ struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = add;
+ add->next = next;
+ add->prev = prev;
+ prev->next = add;
+}
+
+/**
+ * list_add - add a new entry
+ * @add: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+_INLINE_ void list_add(struct list_head *add, struct list_head *head)
+{
+ __list_add(add, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @add: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+_INLINE_ void list_add_tail(struct list_head *add, struct list_head *head)
+{
+ __list_add(add, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+_INLINE_ void __list_del(struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ *
+ * list_empty() on @entry does not return true after this, @entry is
+ * in an undefined state.
+ */
+_INLINE_ void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+_INLINE_ void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+_INLINE_ int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_entry_is_last - tests whether is entry last in the list
+ * @entry: the entry to test.
+ * @head: the list to test.
+ */
+_INLINE_ int list_entry_is_last(struct list_head *entry, struct list_head *head)
+{
+ return head->prev == entry;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+_INLINE_ void list_splice(struct list_head *list, struct list_head *head)
+{
+ struct list_head *first = list->next;
+
+ if (first != list) {
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+
+#define list_first_entry(head, type, member) \
+ ((head) && (head)->next != (head) ? list_entry((head)->next, type, member) : NULL)
+
+#define list_last_entry(head, type, member) \
+ ((head) && (head)->prev != (head) ? list_entry((head)->prev, type, member) : NULL)
+
+/**
+ * list_for_each - iterate over elements in a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_backwardly - iterate over elements in a list in reverse
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each_backwardly(pos, head) \
+ for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over elements in a list, but don't dereference
+ * pos after the body is done (in case it is freed)
+ * @pos: the &struct list_head to use as a loop counter.
+ * @pnext: the &struct list_head to use as a pointer to the next item.
+ * @head: the head for your list (not included in iteration).
+ */
+#define list_for_each_safe(pos, pnext, head) \
+ for (pos = (head)->next, pnext = pos->next; pos != (head); \
+ pos = pnext, pnext = pos->next)
+
+#define MAX_LIST_LENGTH_BITS 20
+
+/*
+ * Returns a list organized in an intermediate format suited
+ * to chaining of merge() calls: null-terminated, no reserved or
+ * sentinel head node, "prev" links not maintained.
+ */
+_INLINE_ struct list_head *merge(int (*cmp)(struct list_head *a,
+ struct list_head *b),
+ struct list_head *a, struct list_head *b)
+{
+ struct list_head head, *tail = &head;
+
+ while (a && b) {
+ /* if equal, take 'a' -- important for sort stability */
+ if ((*cmp)(a, b) <= 0) {
+ tail->next = a;
+ a = a->next;
+ } else {
+ tail->next = b;
+ b = b->next;
+ }
+ tail = tail->next;
+ }
+ tail->next = a ? a : b;
+ return head.next;
+}
+
+/*
+ * Combine final list merge with restoration of standard doubly-linked
+ * list structure. This approach duplicates code from merge(), but
+ * runs faster than the tidier alternatives of either a separate final
+ * prev-link restoration pass, or maintaining the prev links
+ * throughout.
+ */
+_INLINE_ void merge_and_restore_back_links(int (*cmp)(struct list_head *a,
+ struct list_head *b),
+ struct list_head *head,
+ struct list_head *a, struct list_head *b)
+{
+ struct list_head *tail = head;
+
+ while (a && b) {
+ /* if equal, take 'a' -- important for sort stability */
+ if ((*cmp)(a, b) <= 0) {
+ tail->next = a;
+ a->prev = tail;
+ a = a->next;
+ } else {
+ tail->next = b;
+ b->prev = tail;
+ b = b->next;
+ }
+ tail = tail->next;
+ }
+ tail->next = a ? a : b;
+
+ do {
+ /*
+ * In worst cases this loop may run many iterations.
+ * Continue callbacks to the client even though no
+ * element comparison is needed, so the client's cmp()
+ * routine can invoke cond_resched() periodically.
+ */
+ (*cmp)(tail->next, tail->next);
+
+ tail->next->prev = tail;
+ tail = tail->next;
+ } while (tail->next);
+
+ tail->next = head;
+ head->prev = tail;
+}
+
+
+/**
+ * list_sort - sort a list
+ * @head: the list to sort
+ * @cmp: the elements comparison function
+ *
+ * This function implements "merge sort", which has O(nlog(n))
+ * complexity.
+ *
+ * The comparison function @cmp must return a negative value if @a
+ * should sort before @b, and a positive value if @a should sort after
+ * @b. If @a and @b are equivalent, and their original relative
+ * ordering is to be preserved, @cmp must return 0.
+ */
+_INLINE_ void list_sort(struct list_head *head,
+ int (*cmp)(struct list_head *a,
+ struct list_head *b))
+{
+ struct list_head *part[MAX_LIST_LENGTH_BITS+1]; /* sorted partial lists
+ -- last slot is a sentinel */
+ size_t lev; /* index into part[] */
+ size_t max_lev = 0;
+ struct list_head *list;
+
+ if (list_empty(head))
+ return;
+
+ memset(part, 0, sizeof(part));
+
+ head->prev->next = NULL;
+ list = head->next;
+
+ while (list) {
+ struct list_head *cur = list;
+ list = list->next;
+ cur->next = NULL;
+
+ for (lev = 0; part[lev]; lev++) {
+ cur = merge(cmp, part[lev], cur);
+ part[lev] = NULL;
+ }
+ if (lev > max_lev) {
+ /* list passed to list_sort() too long for efficiency */
+ if (lev >= ARRAY_SIZE(part) - 1)
+ lev--;
+ max_lev = lev;
+ }
+ part[lev] = cur;
+ }
+
+ for (lev = 0; lev < max_lev; lev++)
+ if (part[lev])
+ list = merge(cmp, part[lev], list);
+
+ merge_and_restore_back_links(cmp, head, part[max_lev], list);
+}
+
+#undef _INLINE_
+
+#endif /* UTIL_LINUX_LIST_H */
diff --git a/libblkid/src/llseek.c b/libblkid/src/llseek.c
new file mode 100644
index 000000000..67334785a
--- /dev/null
+++ b/libblkid/src/llseek.c
@@ -0,0 +1,142 @@
+/*
+ * llseek.c -- stub calling the llseek system call
+ *
+ * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef __MSDOS__
+#include <io.h>
+#endif
+
+#include "blkidP.h"
+
+#ifdef __linux__
+
+#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
+
+#define my_llseek lseek64
+
+#elif defined(HAVE_LLSEEK)
+#include <syscall.h>
+
+#ifndef HAVE_LLSEEK_PROTOTYPE
+extern long long llseek(int fd, long long offset, int origin);
+#endif
+
+#define my_llseek llseek
+
+#else /* ! HAVE_LLSEEK */
+
+#if SIZEOF_LONG == SIZEOF_LONG_LONG
+
+#define llseek lseek
+
+#else /* SIZEOF_LONG != SIZEOF_LONG_LONG */
+
+#include <linux/unistd.h>
+
+#ifndef __NR__llseek
+#define __NR__llseek 140
+#endif
+
+#ifndef __i386__
+static int _llseek(unsigned int, unsigned long, unsigned long,
+ blkid_loff_t *, unsigned int);
+
+static _syscall5(int, _llseek, unsigned int, fd, unsigned long, offset_high,
+ unsigned long, offset_low, blkid_loff_t *, result,
+ unsigned int, origin)
+#endif
+/*
+static blkid_loff_t my_llseek(int fd, blkid_loff_t offset, int origin)
+{
+ blkid_loff_t result;
+ int retval;
+
+#ifndef __i386__
+ retval = _llseek(fd, ((unsigned long long) offset) >> 32,
+ ((unsigned long long)offset) & 0xffffffff,
+ &result, origin);
+#else
+ retval = syscall(__NR__llseek, fd, ((unsigned long long) offset) >> 32,
+ ((unsigned long long)offset) & 0xffffffff,
+ &result, origin);
+#endif
+ return (retval == -1 ? (blkid_loff_t) retval : result);
+}
+*/
+#endif /* __alpha__ || __ia64__ */
+
+#endif /* HAVE_LLSEEK */
+
+blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence)
+{
+ blkid_loff_t result;
+ static int do_compat = 0;
+
+ if ((sizeof(off_t) >= sizeof(blkid_loff_t)) ||
+ (offset < ((blkid_loff_t) 1 << ((sizeof(off_t)*8) -1))))
+ return lseek(fd, (off_t) offset, whence);
+
+ if (do_compat) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ result = lseek64(fd, offset, whence);
+ if (result == -1 && errno == ENOSYS) {
+ /*
+ * Just in case this code runs on top of an old kernel
+ * which does not support the llseek system call
+ */
+ do_compat++;
+ errno = EOVERFLOW;
+ }
+ return result;
+}
+
+#else /* !linux */
+
+#ifndef EOVERFLOW
+#ifdef EXT2_ET_INVALID_ARGUMENT
+#define EOVERFLOW EXT2_ET_INVALID_ARGUMENT
+#else
+#define EOVERFLOW 112
+#endif
+#endif
+
+blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int origin)
+{
+#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
+ return lseek64 (fd, offset, origin);
+#else
+ if ((sizeof(off_t) < sizeof(blkid_loff_t)) &&
+ (offset >= ((blkid_loff_t) 1 << ((sizeof(off_t)*8) - 1)))) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ return lseek(fd, (off_t) offset, origin);
+#endif
+}
+
+#endif /* linux */
+
+
diff --git a/libblkid/src/loopdev.h b/libblkid/src/loopdev.h
new file mode 100644
index 000000000..573a5699d
--- /dev/null
+++ b/libblkid/src/loopdev.h
@@ -0,0 +1,193 @@
+#ifndef UTIL_LINUX_LOOPDEV_H
+#define UTIL_LINUX_LOOPDEV_H
+
+#include "sysfs.h"
+
+/*
+ * loop_info.lo_encrypt_type
+ */
+#define LO_CRYPT_NONE 0
+#define LO_CRYPT_XOR 1
+#define LO_CRYPT_DES 2
+#define LO_CRYPT_CRYPTOAPI 18
+
+#define LOOP_SET_FD 0x4C00
+#define LOOP_CLR_FD 0x4C01
+/*
+ * Obsolete (kernel < 2.6)
+ *
+ * #define LOOP_SET_STATUS 0x4C02
+ * #define LOOP_GET_STATUS 0x4C03
+ */
+#define LOOP_SET_STATUS64 0x4C04
+#define LOOP_GET_STATUS64 0x4C05
+/* #define LOOP_CHANGE_FD 0x4C06 */
+#define LOOP_SET_CAPACITY 0x4C07
+
+/* /dev/loop-control interface */
+#ifndef LOOP_CTL_ADD
+# define LOOP_CTL_ADD 0x4C80
+# define LOOP_CTL_REMOVE 0x4C81
+# define LOOP_CTL_GET_FREE 0x4C82
+#endif
+
+/*
+ * loop_info.lo_flags
+ */
+enum {
+ LO_FLAGS_READ_ONLY = 1,
+ LO_FLAGS_USE_AOPS = 2,
+ LO_FLAGS_AUTOCLEAR = 4, /* kernel >= 2.6.25 */
+ LO_FLAGS_PARTSCAN = 8, /* kernel >= 3.2 */
+};
+
+#define LO_NAME_SIZE 64
+#define LO_KEY_SIZE 32
+
+/*
+ * Linux LOOP_{SET,GET}_STATUS64 ioctl struct
+ */
+struct loop_info64 {
+ uint64_t lo_device;
+ uint64_t lo_inode;
+ uint64_t lo_rdevice;
+ uint64_t lo_offset;
+ uint64_t lo_sizelimit; /* bytes, 0 == max available */
+ uint32_t lo_number;
+ uint32_t lo_encrypt_type;
+ uint32_t lo_encrypt_key_size;
+ uint32_t lo_flags;
+ uint8_t lo_file_name[LO_NAME_SIZE];
+ uint8_t lo_crypt_name[LO_NAME_SIZE];
+ uint8_t lo_encrypt_key[LO_KEY_SIZE];
+ uint64_t lo_init[2];
+};
+
+#define LOOPDEV_MAJOR 7 /* loop major number */
+#define LOOPDEV_DEFAULT_NNODES 8 /* default number of loop devices */
+
+struct loopdev_iter {
+ FILE *proc; /* /proc/partitions */
+ DIR *sysblock; /* /sys/block */
+ int ncur; /* current position */
+ int *minors; /* ary of minor numbers (when scan whole /dev) */
+ int nminors; /* number of items in *minors */
+ int ct_perm; /* count permission problems */
+ int ct_succ; /* count number of detected devices */
+
+ unsigned int done:1; /* scanning done */
+ unsigned int default_check:1;/* check first LOOPDEV_NLOOPS */
+ int flags; /* LOOPITER_FL_* flags */
+};
+
+enum {
+ LOOPITER_FL_FREE = (1 << 0),
+ LOOPITER_FL_USED = (1 << 1)
+};
+
+/*
+ * handler for work with loop devices
+ */
+struct loopdev_cxt {
+ char device[128]; /* device path (e.g. /dev/loop<N>) */
+ char *filename; /* backing file for loopcxt_set_... */
+ int fd; /* open(/dev/looo<N>) */
+ int mode; /* fd mode O_{RDONLY,RDWR} */
+
+ int flags; /* LOOPDEV_FL_* flags */
+ unsigned int has_info:1; /* .info contains data */
+ unsigned int extra_check:1; /* unusual stuff for iterator */
+ unsigned int info_failed:1; /* LOOP_GET_STATUS ioctl failed */
+ unsigned int control_ok:1; /* /dev/loop-control success */
+
+ struct sysfs_cxt sysfs; /* pointer to /sys/dev/block/<maj:min>/ */
+ struct loop_info64 info; /* for GET/SET ioctl */
+ struct loopdev_iter iter; /* scans /sys or /dev for used/free devices */
+};
+
+#define UL_LOOPDEVCXT_EMPTY { .fd = -1, .sysfs = UL_SYSFSCXT_EMPTY }
+
+/*
+ * loopdev_cxt.flags
+ */
+enum {
+ LOOPDEV_FL_RDONLY = (1 << 0), /* open(/dev/loop) mode; default */
+ LOOPDEV_FL_RDWR = (1 << 1), /* necessary for loop setup only */
+ LOOPDEV_FL_OFFSET = (1 << 4),
+ LOOPDEV_FL_NOSYSFS = (1 << 5),
+ LOOPDEV_FL_NOIOCTL = (1 << 6),
+ LOOPDEV_FL_DEVSUBDIR = (1 << 7),
+ LOOPDEV_FL_CONTROL = (1 << 8), /* system with /dev/loop-control */
+ LOOPDEV_FL_SIZELIMIT = (1 << 9)
+};
+
+/*
+ * High-level
+ */
+extern int loopmod_supports_partscan(void);
+
+extern int is_loopdev(const char *device);
+extern int loopdev_is_autoclear(const char *device);
+
+extern char *loopdev_get_backing_file(const char *device);
+extern int loopdev_is_used(const char *device, const char *filename,
+ uint64_t offset, int flags);
+extern char *loopdev_find_by_backing_file(const char *filename,
+ uint64_t offset, int flags);
+extern int loopcxt_find_unused(struct loopdev_cxt *lc);
+extern int loopdev_delete(const char *device);
+extern int loopdev_count_by_backing_file(const char *filename, char **loopdev);
+
+/*
+ * Low-level
+ */
+extern int loopcxt_init(struct loopdev_cxt *lc, int flags)
+ __attribute__ ((warn_unused_result));
+extern void loopcxt_deinit(struct loopdev_cxt *lc);
+
+extern int loopcxt_set_device(struct loopdev_cxt *lc, const char *device)
+ __attribute__ ((warn_unused_result));
+extern int loopcxt_has_device(struct loopdev_cxt *lc);
+extern int loopcxt_add_device(struct loopdev_cxt *lc);
+extern char *loopcxt_strdup_device(struct loopdev_cxt *lc);
+extern const char *loopcxt_get_device(struct loopdev_cxt *lc);
+extern struct sysfs_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc);
+extern struct loop_info64 *loopcxt_get_info(struct loopdev_cxt *lc);
+
+extern int loopcxt_get_fd(struct loopdev_cxt *lc);
+extern int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, int mode);
+
+extern int loopcxt_init_iterator(struct loopdev_cxt *lc, int flags);
+extern int loopcxt_deinit_iterator(struct loopdev_cxt *lc);
+extern int loopcxt_next(struct loopdev_cxt *lc);
+
+extern int loopcxt_setup_device(struct loopdev_cxt *lc);
+extern int loopcxt_delete_device(struct loopdev_cxt *lc);
+extern int loopcxt_set_capacity(struct loopdev_cxt *lc);
+
+int loopcxt_set_offset(struct loopdev_cxt *lc, uint64_t offset);
+int loopcxt_set_sizelimit(struct loopdev_cxt *lc, uint64_t sizelimit);
+int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags);
+int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename);
+
+extern char *loopcxt_get_backing_file(struct loopdev_cxt *lc);
+extern int loopcxt_get_backing_devno(struct loopdev_cxt *lc, dev_t *devno);
+extern int loopcxt_get_backing_inode(struct loopdev_cxt *lc, ino_t *ino);
+extern int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset);
+extern int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size);
+extern int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type);
+extern const char *loopcxt_get_crypt_name(struct loopdev_cxt *lc);
+extern int loopcxt_is_autoclear(struct loopdev_cxt *lc);
+extern int loopcxt_is_readonly(struct loopdev_cxt *lc);
+extern int loopcxt_is_partscan(struct loopdev_cxt *lc);
+extern int loopcxt_find_by_backing_file(struct loopdev_cxt *lc,
+ const char *filename,
+ uint64_t offset, int flags);
+
+extern int loopcxt_is_used(struct loopdev_cxt *lc,
+ struct stat *st,
+ const char *backing_file,
+ uint64_t offset,
+ int flags);
+
+#endif /* UTIL_LINUX_LOOPDEV_H */
diff --git a/libblkid/src/mangle.h b/libblkid/src/mangle.h
new file mode 100644
index 000000000..ec492b556
--- /dev/null
+++ b/libblkid/src/mangle.h
@@ -0,0 +1,26 @@
+#ifndef UTIL_LINUX_MANGLE_H
+#define UTIL_LINUX_MANGLE_H
+
+/*
+ * Functions for \oct encoding used in mtab/fstab/swaps/etc.
+ */
+
+extern char *mangle(const char *s);
+
+extern void unmangle_to_buffer(const char *s, char *buf, size_t len);
+void unhexmangle_to_buffer(const char *s, char *buf, size_t len);
+
+extern char *unmangle(const char *s, char **end);
+
+static inline void unmangle_string(char *s)
+{
+ unmangle_to_buffer(s, s, strlen(s) + 1);
+}
+
+static inline void unhexmangle_string(char *s)
+{
+ unhexmangle_to_buffer(s, s, strlen(s) + 1);
+}
+
+#endif /* UTIL_LINUX_MANGLE_H */
+
diff --git a/libblkid/src/match.h b/libblkid/src/match.h
new file mode 100644
index 000000000..94440c22e
--- /dev/null
+++ b/libblkid/src/match.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef UTIL_LINUX_MATCH_H
+#define UTIL_LINUX_MATCH_H
+
+extern int match_fstype(const char *type, const char *pattern);
+
+#endif /* UTIL_LINUX_MATCH_H */
diff --git a/libblkid/src/mbsalign.h b/libblkid/src/mbsalign.h
new file mode 100644
index 000000000..5eaf606e5
--- /dev/null
+++ b/libblkid/src/mbsalign.h
@@ -0,0 +1,56 @@
+/* Align/Truncate a string in a given screen width
+ Copyright (C) 2009-2010 Free Software Foundation, Inc.
+ Copyright (C) 2010-2013 Karel Zak <kzak@redhat.com>
+
+ This program 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 program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+#ifndef UTIL_LINUX_MBSALIGN_H
+# define UTIL_LINUX_MBSALIGN_H
+# include <stddef.h>
+
+typedef enum { MBS_ALIGN_LEFT, MBS_ALIGN_RIGHT, MBS_ALIGN_CENTER } mbs_align_t;
+
+enum {
+ /* Use unibyte mode for invalid multibyte strings or
+ or when heap memory is exhausted. */
+ MBA_UNIBYTE_FALLBACK = 0x0001,
+
+#if 0 /* Other possible options. */
+ /* Skip invalid multibyte chars rather than failing */
+ MBA_IGNORE_INVALID = 0x0002,
+
+ /* Align multibyte strings using "figure space" (\u2007) */
+ MBA_USE_FIGURE_SPACE = 0x0004,
+
+ /* Don't add any padding */
+ MBA_TRUNCATE_ONLY = 0x0008,
+
+ /* Don't truncate */
+ MBA_PAD_ONLY = 0x0010,
+#endif
+};
+
+extern size_t mbs_truncate(char *str, size_t *width);
+
+extern size_t mbsalign (const char *src, char *dest,
+ size_t dest_size, size_t *width,
+ mbs_align_t align, int flags);
+
+extern size_t mbs_safe_nwidth(const char *buf, size_t bufsz, size_t *sz);
+extern size_t mbs_safe_width(const char *s);
+
+extern char *mbs_safe_encode(const char *s, size_t *width);
+extern char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf);
+extern size_t mbs_safe_encode_size(size_t bytes);
+
+#endif /* UTIL_LINUX_MBSALIGN_H */
diff --git a/libblkid/src/md5.h b/libblkid/src/md5.h
new file mode 100644
index 000000000..d997e379d
--- /dev/null
+++ b/libblkid/src/md5.h
@@ -0,0 +1,29 @@
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#else
+typedef unsigned int uint32_t;
+#endif
+
+#define MD5LENGTH 16
+
+struct MD5Context {
+ uint32_t buf[4];
+ uint32_t bits[2];
+ unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+ unsigned len);
+void MD5Final(unsigned char digest[MD5LENGTH], struct MD5Context *context);
+void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+typedef struct MD5Context MD5_CTX;
+
+#endif /* !MD5_H */
diff --git a/libblkid/src/nls.h b/libblkid/src/nls.h
new file mode 100644
index 000000000..3eabfe63b
--- /dev/null
+++ b/libblkid/src/nls.h
@@ -0,0 +1,115 @@
+#ifndef UTIL_LINUX_NLS_H
+#define UTIL_LINUX_NLS_H
+
+int main(int argc, char *argv[]);
+
+#ifndef LOCALEDIR
+#define LOCALEDIR "/usr/share/locale"
+#endif
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#else
+# undef setlocale
+# define setlocale(Category, Locale) /* empty */
+struct lconv
+{
+ char *decimal_point;
+};
+# undef localeconv
+# define localeconv() NULL
+#endif
+
+#ifdef ENABLE_NLS
+# include <libintl.h>
+# define _(Text) gettext (Text)
+# ifdef gettext_noop
+# define N_(String) gettext_noop (String)
+# else
+# define N_(String) (String)
+# endif
+# define P_(Singular, Plural, n) ngettext (Singular, Plural, n)
+#else
+# undef bindtextdomain
+# define bindtextdomain(Domain, Directory) /* empty */
+# undef textdomain
+# define textdomain(Domain) /* empty */
+# define _(Text) (Text)
+# define N_(Text) (Text)
+# define P_(Singular, Plural, n) ((n) == 1 ? (Singular) : (Plural))
+#endif
+
+#ifdef HAVE_LANGINFO_H
+# include <langinfo.h>
+#else
+
+typedef int nl_item;
+extern char *langinfo_fallback(nl_item item);
+
+# define nl_langinfo langinfo_fallback
+
+enum {
+ CODESET = 1,
+ RADIXCHAR,
+ THOUSEP,
+ D_T_FMT,
+ D_FMT,
+ T_FMT,
+ T_FMT_AMPM,
+ AM_STR,
+ PM_STR,
+
+ DAY_1,
+ DAY_2,
+ DAY_3,
+ DAY_4,
+ DAY_5,
+ DAY_6,
+ DAY_7,
+
+ ABDAY_1,
+ ABDAY_2,
+ ABDAY_3,
+ ABDAY_4,
+ ABDAY_5,
+ ABDAY_6,
+ ABDAY_7,
+
+ MON_1,
+ MON_2,
+ MON_3,
+ MON_4,
+ MON_5,
+ MON_6,
+ MON_7,
+ MON_8,
+ MON_9,
+ MON_10,
+ MON_11,
+ MON_12,
+
+ ABMON_1,
+ ABMON_2,
+ ABMON_3,
+ ABMON_4,
+ ABMON_5,
+ ABMON_6,
+ ABMON_7,
+ ABMON_8,
+ ABMON_9,
+ ABMON_10,
+ ABMON_11,
+ ABMON_12,
+
+ ERA_D_FMT,
+ ERA_D_T_FMT,
+ ERA_T_FMT,
+ ALT_DIGITS,
+ CRNCYSTR,
+ YESEXPR,
+ NOEXPR
+};
+
+#endif /* !HAVE_LANGINFO_H */
+
+#endif /* UTIL_LINUX_NLS_H */
diff --git a/libblkid/src/partitions/aix.c b/libblkid/src/partitions/aix.c
new file mode 100644
index 000000000..4efdfa33d
--- /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 tring 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 000000000..f767c5a37
--- /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/bsd.c b/libblkid/src/partitions/bsd.c
new file mode 100644
index 000000000..d83f2cf02
--- /dev/null
+++ b/libblkid/src/partitions/bsd.c
@@ -0,0 +1,179 @@
+/*
+ * 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 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;
+
+ 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);
+
+ 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";
+ 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;
+
+ /* TODO: in fdisk-mode returns all non-zero (p_size) partitions */
+ if (p->p_fstype == BSD_FS_UNUSED)
+ continue;
+
+ start = le32_to_cpu(p->p_offset);
+ size = le32_to_cpu(p->p_size);
+
+ 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 000000000..253990800
--- /dev/null
+++ b/libblkid/src/partitions/dos.c
@@ -0,0 +1,307 @@
+/*
+ * 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 "aix.h"
+
+/* see superblocks/vfat.c */
+extern int blkid_probe_is_vfat(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;
+
+ while (1) {
+ struct dos_partition *p, *p0;
+ uint32_t start, 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;
+ }
+
+ 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))
+ break;
+ }
+ if (i == 4)
+ goto leave;
+
+ cur_start = ex_start + start;
+ cur_size = size;
+ }
+leave:
+ return BLKID_PROBE_OK;
+}
+
+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[37];
+
+
+ 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;
+
+ /*
+ * 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) {
+ DBG(LOWPROBE, ul_debug("probably FAT -- ignore"));
+ 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;
+ }
+ }
+
+ 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 partiton table
+ */
+ if (blkid_partitions_need_typeonly(pr)) {
+ /* Non-binary interface -- caller does not ask for details
+ * about partitions, just set generic varibles 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)) {
+ for (p = p0, i = 0; i < 4; i++, p++) {
+ size_t n;
+ int rc;
+
+ if (!dos_partition_get_size(p) || is_extended(p))
+ continue;
+
+ for (n = 0; n < ARRAY_SIZE(dos_nested); n++) {
+ if (dos_nested[n].type != p->sys_ind)
+ continue;
+
+ rc = blkid_partitions_do_subprobe(pr,
+ blkid_partlist_get_partition(ls, i),
+ 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 000000000..665577fa4
--- /dev/null
+++ b/libblkid/src/partitions/gpt.c
@@ -0,0 +1,470 @@
+/*
+ * 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 "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; /* lirst 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)
+{
+ return (crc32(~0L, buf, 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)
+{
+ blkid_loff_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_paertitions_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;
+ if (flags & BLKID_PARTS_FORCE_GPT)
+ goto ok; /* 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)
+ 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, orgcrc;
+ uint64_t lu, fu;
+ size_t esz;
+ uint32_t hsz, ssz;
+
+ ssz = blkid_probe_get_sectorsize(pr);
+
+ /* 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 */
+ orgcrc = h->header_crc32;
+ h->header_crc32 = 0;
+ crc = count_crc32((unsigned char *) h, hsz);
+ h->header_crc32 = orgcrc;
+
+ if (crc != le32_to_cpu(orgcrc)) {
+ 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;
+ }
+
+ if (le32_to_cpu(h->num_partition_entries) == 0 ||
+ le32_to_cpu(h->sizeof_partition_entry) == 0 ||
+ ULONG_MAX / le32_to_cpu(h->num_partition_entries) < le32_to_cpu(h->sizeof_partition_entry)) {
+ DBG(LOWPROBE, ul_debug("GPT entries undefined"));
+ return NULL;
+ }
+
+ /* Size of blocks with GPT entries */
+ esz = le32_to_cpu(h->num_partition_entries) *
+ le32_to_cpu(h->sizeof_partition_entry);
+
+ /* 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);
+ 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;
+ else 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 varibles 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), BLKID_ENC_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,
+ .minsz = 1024 * 1440 + 1, /* ignore floppies */
+
+ /*
+ * It would be possible to check for DOS signature (0xAA55), but
+ * unfortunately almost all EFI GPT implemenations allow to optionaly
+ * 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 000000000..428260534
--- /dev/null
+++ b/libblkid/src/partitions/mac.c
@@ -0,0 +1,192 @@
+/*
+ * 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,
+ (blkid_loff_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, 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);
+
+ /* 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);
+
+ for (i = 1; i <= nblks; ++i) {
+ blkid_partition par;
+ uint32_t start;
+ uint32_t size;
+
+ p = (struct mac_partition *) get_mac_block(pr, block_size, i);
+ 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: inconsisten map_count in partition map, "
+ "entry[0]: %d, entry[%d]: %d",
+ nblks, i - 1,
+ 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 000000000..43c9d9af1
--- /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 000000000..25f1828a6
--- /dev/null
+++ b/libblkid/src/partitions/partitions.c
@@ -0,0 +1,1513 @@
+/*
+ * 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 <sys/sysmacros.h>
+#include <errno.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdarg.h>
+
+#include "partitions.h"
+#include "sysfs.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: faild 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: faild 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
+};
+
+/*
+ * 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 */
+ blkid_loff_t offset; /* begin of the partition table (in bytes) */
+ int nparts; /* number of partitions */
+ blkid_partition parent; /* parent of nested partition table */
+ char id[37]; /* PT identifier (e.g. UUID for GPT) */
+
+ struct list_head t_tabs; /* all tables */
+};
+
+/* exported as opaque type "blkid_partition" */
+struct blkid_struct_partition {
+ blkid_loff_t start; /* begin of the partition (512-bytes sectors) */
+ blkid_loff_t size; /* size of the partitions (512-bytes sectors) */
+
+ int type; /* partition type */
+ char typestr[37]; /* partition type string (GPT and Mac) */
+
+ unsigned long long flags; /* partition flags / attributes */
+
+ int partno; /* partition number */
+ char uuid[37]; /* UUID (when supported by PT), e.g GPT */
+ unsigned char name[128]; /* Partition in UTF8 name (when supporte 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)
+{
+ if (!pr)
+ return -1;
+ 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)
+{
+ if (!pr)
+ return -1;
+ pr->chains[BLKID_CHAIN_PARTS].flags = flags;
+ return 0;
+}
+
+/**
+ * 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)
+{
+ tab->nparts++;
+}
+
+static void unref_parttable(blkid_parttable tab)
+{
+ 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 (%p, size=%d)",
+ ls, 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, blkid_loff_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 "
+ "(%p, type=%s, offset=%"PRId64")", tab, 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 partititions.
+ */
+ 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,
+ blkid_loff_t start, blkid_loff_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 (%p start=%"
+ PRId64 ", size=%" PRId64 ", table=%p)",
+ par, par->start, par->size, tab));
+ return par;
+}
+
+/* allows 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;
+}
+
+/* allows to set "parent" for the next nested partition */
+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,
+ blkid_loff_t start, blkid_loff_t size)
+{
+ blkid_loff_t pstart;
+ blkid_loff_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;
+ blkid_loff_t off;
+ int rc = BLKID_PROBE_NONE; /* default is nothing */
+
+ if (pr->size <= 0 || (id->minsz && 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));
+ 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_vals(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,
+ (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_vals(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",
+ (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;
+ blkid_loff_t sz, off;
+
+ DBG(LOWPROBE, ul_debug(
+ "parts: ----> %s subprobe requested (parent=%p)",
+ id->name, parent));
+
+ if (!pr || !parent || !parent->size)
+ return -EINVAL;
+ if (pr->flags & BLKID_FL_NOSCAN_DEV)
+ return BLKID_PROBE_NONE;
+
+ /* range defined by parent */
+ sz = ((blkid_loff_t) parent->size) << 9;
+ off = ((blkid_loff_t) 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 (parent=%p, rc=%d)",
+ id->name, parent, 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",
+ (unsigned char *) v, strlen(v) + 1);
+ }
+
+ v = blkid_partition_get_name(par);
+ if (v)
+ blkid_probe_set_value(pr, "PART_ENTRY_NAME",
+ (unsigned char *) v, strlen(v) + 1);
+
+ v = blkid_partition_get_uuid(par);
+ if (v)
+ blkid_probe_set_value(pr, "PART_ENTRY_UUID",
+ (unsigned char *) v, strlen(v) + 1);
+
+ /* type */
+ v = blkid_partition_get_type_string(par);
+ if (v)
+ blkid_probe_set_value(pr, "PART_ENTRY_TYPE",
+ (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",
+ blkid_partition_get_start(par));
+ blkid_probe_sprintf_value(pr, "PART_ENTRY_SIZE", "%jd",
+ 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,
+ blkid_loff_t offset, blkid_loff_t size)
+{
+ blkid_probe prc = NULL;
+ blkid_partlist ls = NULL;
+ blkid_loff_t start, end;
+ int nparts, i, rc = 0;
+
+ DBG(LOWPROBE, ul_debug(
+ "=> checking if off=%jd size=%jd 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: partiton 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_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 ? ls->nparts : -1;
+}
+
+/**
+ * blkid_partlist_get_table:
+ * @ls: partitions list
+ *
+ * Returns: top-level partition table or NULL of there is not a partition table
+ * on the device.
+ */
+blkid_parttable blkid_partlist_get_table(blkid_partlist ls)
+{
+ if (!ls || 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 (!ls || n < 0 || n >= ls->nparts)
+ return NULL;
+
+ return &ls->parts[n];
+}
+
+/**
+ * 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;
+
+ if (!ls)
+ return NULL;
+
+ 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 sysfs_cxt sysfs;
+ uint64_t start, size;
+ int i, rc, partno = 0;
+
+ if (!ls)
+ return NULL;
+
+ DBG(LOWPROBE, ul_debug("triyng to convert devno 0x%llx to partition",
+ (long long) devno));
+
+ if (sysfs_init(&sysfs, devno, NULL)) {
+ DBG(LOWPROBE, ul_debug("failed t init sysfs context"));
+ return NULL;
+ }
+ rc = sysfs_read_u64(&sysfs, "size", &size);
+ if (!rc) {
+ rc = sysfs_read_u64(&sysfs, "start", &start);
+ if (rc) {
+ /* try to get partition number from DM uuid.
+ */
+ char *uuid = sysfs_strdup(&sysfs, "dm/uuid");
+ char *tmp = uuid;
+ char *prefix = uuid ? strsep(&tmp, "-") : NULL;
+
+ if (prefix && strncasecmp(prefix, "part", 4) == 0) {
+ char *end = NULL;
+
+ partno = strtol(prefix + 4, &end, 10);
+ if (prefix == end || (end && *end))
+ partno = 0;
+ else
+ rc = 0; /* success */
+ }
+ free(uuid);
+ }
+ }
+
+ sysfs_deinit(&sysfs);
+
+ 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 releation bettween 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 ((blkid_loff_t) size == blkid_partition_get_size(par) ||
+ (blkid_partition_is_extended(par) && size <= 1024))
+ 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 (blkid_partition_get_start(par) == (blkid_loff_t) start &&
+ blkid_partition_get_size(par) == (blkid_loff_t) size)
+ return par;
+
+ /* exception for extended dos partitions */
+ if (blkid_partition_get_start(par) == (blkid_loff_t) start &&
+ blkid_partition_is_extended(par) && size <= 1024)
+ 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;
+
+ strncpy(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");
+
+ blkid_unparse_uuid(uuid, (char *) v->data, sizeof(v->data));
+ v->len = 37;
+
+ return 0;
+}
+
+/* set PTUUID variable for non-binary API for tables where
+ * the ID is just a string */
+int blkid_partitions_strcpy_ptuuid(blkid_probe pr, char *str)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ struct blkid_prval *v;
+ size_t len;
+
+ if (chn->binary || !str || !*str)
+ return 0;
+
+ len = strlen((char *) str);
+ if (len > BLKID_PROBVAL_BUFSIZ)
+ len = BLKID_PROBVAL_BUFSIZ;
+
+ v = blkid_probe_assign_value(pr, "PTUUID");
+ if (v) {
+ if (len == BLKID_PROBVAL_BUFSIZ)
+ len--; /* make a space for \0 */
+
+ memcpy((char *) v->data, str, len);
+ v->data[len] = '\0';
+ v->len = len + 1;
+ return 0;
+ }
+ return -1;
+}
+
+/**
+ * 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 && *tab->id ? tab->id : NULL;
+}
+
+
+int blkid_partition_set_type(blkid_partition par, int type)
+{
+ if (!par)
+ return -1;
+ 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 ? tab->type : NULL;
+}
+
+/**
+ * blkid_parttable_get_parent:
+ * @tab: partition table
+ *
+ * Returns: parent for nexted partitition tables or NULL.
+ */
+blkid_partition blkid_parttable_get_parent(blkid_parttable tab)
+{
+ return tab ? tab->parent : NULL;
+}
+
+/**
+ * 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 patition 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 tab ? tab->offset : -1;
+}
+
+/**
+ * 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 ? par->tab : NULL;
+}
+
+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;
+
+ blkid_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), "%s-%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 && *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 && *par->uuid ? par->uuid : NULL;
+}
+
+/**
+ * blkid_partition_get_partno:
+ * @par: partition
+ *
+ * Returns: proposed partitin number (e.g. 'N' from sda'N') or -1 in case of
+ * error. Note that the number is generate by library independenly on your OS.
+ */
+int blkid_partition_get_partno(blkid_partition par)
+{
+ return par ? par->partno : -1;
+}
+
+/**
+ * blkid_partition_get_start:
+ * @par: partition
+ *
+ * Be careful if you _not_ probe whole disk:
+ *
+ * 1) the offset is usully 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 releative 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 proble 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 par ? par->start : -1;
+}
+
+/**
+ * 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 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 unaccessible 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 par ? par->size : -1;
+}
+
+/**
+ * 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)
+{
+ if (!par)
+ return -1;
+
+ set_string((unsigned char *) par->typestr,
+ sizeof(par->typestr), type, len);
+ return 0;
+}
+
+/* Sets partition 'type' for PT where the type is defined by UUIDrather
+ * than by number
+ */
+int blkid_partition_set_type_uuid(blkid_partition par, const unsigned char *uuid)
+{
+ if (!par)
+ return -1;
+
+ 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 && *par->typestr ? par->typestr : NULL;
+}
+
+
+int blkid_partition_set_flags(blkid_partition par, unsigned long long flags)
+{
+ if (!par)
+ return -1;
+ 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 000000000..3651bbb4d
--- /dev/null
+++ b/libblkid/src/partitions/partitions.h
@@ -0,0 +1,71 @@
+#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, blkid_loff_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,
+ blkid_loff_t start, blkid_loff_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 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, char *str);
+
+
+extern int blkid_is_nested_dimension(blkid_partition par,
+ blkid_loff_t start, blkid_loff_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;
+
+#endif /* BLKID_PARTITIONS_H */
diff --git a/libblkid/src/partitions/sgi.c b/libblkid/src/partitions/sgi.c
new file mode 100644
index 000000000..99c0bf1c7
--- /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 (sgi_pt_checksum(l)) {
+ 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 000000000..4ac9be5fe
--- /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 allows 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 partitionning
+ * 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; /* partitsion */
+ 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 000000000..22ee45042
--- /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;
+ blkid_loff_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 (sun_pt_checksum(l)) {
+ 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 = 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,
+ * alow 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++) {
+
+ blkid_loff_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 000000000..9c060be1d
--- /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 000000000..6742bcf36
--- /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 intersting 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/pathnames.h b/libblkid/src/pathnames.h
new file mode 100644
index 000000000..0d21b980b
--- /dev/null
+++ b/libblkid/src/pathnames.h
@@ -0,0 +1,196 @@
+/*
+ * Vaguely based on
+ * @(#)pathnames.h 5.3 (Berkeley) 5/9/89
+ * This code is in the public domain.
+ */
+#ifndef PATHNAMES_H
+#define PATHNAMES_H
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifndef __STDC__
+# error "we need an ANSI compiler"
+#endif
+
+/* used by kernel in /proc (e.g. /proc/swaps) for deleted files */
+#define PATH_DELETED_SUFFIX "\\040(deleted)"
+#define PATH_DELETED_SUFFIX_SZ (sizeof(PATH_DELETED_SUFFIX) - 1)
+
+/* DEFPATHs from <paths.h> don't include /usr/local */
+#undef _PATH_DEFPATH
+#define _PATH_DEFPATH "/usr/local/bin:/bin:/usr/bin"
+
+#undef _PATH_DEFPATH_ROOT
+#define _PATH_DEFPATH_ROOT "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
+
+#define _PATH_SECURETTY "/etc/securetty"
+#define _PATH_WTMPLOCK "/etc/wtmplock"
+
+#define _PATH_HUSHLOGIN ".hushlogin"
+#define _PATH_HUSHLOGINS "/etc/hushlogins"
+
+#define _PATH_NOLOGIN_TXT "/etc/nologin.txt"
+
+#ifndef _PATH_MAILDIR
+#define _PATH_MAILDIR "/var/spool/mail"
+#endif
+#define _PATH_MOTDFILE "/etc/motd"
+#define _PATH_NOLOGIN "/etc/nologin"
+#define _PATH_VAR_NOLOGIN "/var/run/nologin"
+
+#define _PATH_LOGIN "/bin/login"
+#define _PATH_INITTAB "/etc/inittab"
+#define _PATH_RC "/etc/rc"
+#define _PATH_REBOOT "/sbin/reboot"
+#define _PATH_SHUTDOWN "/sbin/shutdown"
+#define _PATH_SINGLE "/etc/singleboot"
+#define _PATH_SHUTDOWN_CONF "/etc/shutdown.conf"
+
+#define _PATH_SECURE "/etc/securesingle"
+#define _PATH_USERTTY "/etc/usertty"
+
+#define _PATH_TERMCOLORS_DIRNAME "terminal-colors.d"
+#define _PATH_TERMCOLORS_DIR "/etc/" _PATH_TERMCOLORS_DIRNAME
+
+/* used in login-utils/shutdown.c */
+
+/* used in login-utils/setpwnam.h and login-utils/islocal.c */
+#define _PATH_PASSWD "/etc/passwd"
+
+/* used in login-utils/newgrp and login-utils/setpwnam.h*/
+#define _PATH_GSHADOW "/etc/gshadow"
+
+/* used in login-utils/setpwnam.h */
+#define _PATH_GROUP "/etc/group"
+#define _PATH_SHADOW_PASSWD "/etc/shadow"
+#define _PATH_SHELLS "/etc/shells"
+
+/* used in term-utils/agetty.c */
+#define _PATH_ISSUE "/etc/issue"
+#define _PATH_OS_RELEASE "/etc/os-release"
+#define _PATH_NUMLOCK_ON _PATH_LOCALSTATEDIR "/numlock-on"
+
+#define _PATH_LOGINDEFS "/etc/login.defs"
+
+/* used in misc-utils/look.c */
+#define _PATH_WORDS "/usr/share/dict/words"
+#define _PATH_WORDS_ALT "/usr/share/dict/web2"
+
+/* mount paths */
+#define _PATH_UMOUNT "/bin/umount"
+
+#define _PATH_FILESYSTEMS "/etc/filesystems"
+#define _PATH_PROC_SWAPS "/proc/swaps"
+#define _PATH_PROC_FILESYSTEMS "/proc/filesystems"
+#define _PATH_PROC_MOUNTS "/proc/mounts"
+#define _PATH_PROC_PARTITIONS "/proc/partitions"
+#define _PATH_PROC_DEVICES "/proc/devices"
+#define _PATH_PROC_MOUNTINFO "/proc/self/mountinfo"
+#define _PATH_PROC_LOCKS "/proc/locks"
+#define _PATH_PROC_CDROMINFO "/proc/sys/dev/cdrom/info"
+
+#define _PATH_PROC_UIDMAP "/proc/self/uid_map"
+#define _PATH_PROC_GIDMAP "/proc/self/gid_map"
+
+#define _PATH_PROC_ATTR_CURRENT "/proc/self/attr/current"
+#define _PATH_PROC_ATTR_EXEC "/proc/self/attr/exec"
+#define _PATH_PROC_CAPLASTCAP "/proc/sys/kernel/cap_last_cap"
+
+
+#define _PATH_SYS_BLOCK "/sys/block"
+#define _PATH_SYS_DEVBLOCK "/sys/dev/block"
+#define _PATH_SYS_CLASS "/sys/class"
+#define _PATH_SYS_SCSI "/sys/bus/scsi"
+
+#define _PATH_SYS_SELINUX "/sys/fs/selinux"
+#define _PATH_SYS_APPARMOR "/sys/kernel/security/apparmor"
+
+#ifndef _PATH_MOUNTED
+# ifdef MOUNTED /* deprecated */
+# define _PATH_MOUNTED MOUNTED
+# else
+# define _PATH_MOUNTED "/etc/mtab"
+# endif
+#endif
+
+#ifndef _PATH_MNTTAB
+# ifdef MNTTAB /* deprecated */
+# define _PATH_MNTTAB MNTTAB
+# else
+# define _PATH_MNTTAB "/etc/fstab"
+# endif
+#endif
+
+#define _PATH_MNTTAB_DIR _PATH_MNTTAB ".d"
+
+#define _PATH_MOUNTED_LOCK _PATH_MOUNTED "~"
+#define _PATH_MOUNTED_TMP _PATH_MOUNTED ".tmp"
+
+#ifndef _PATH_DEV
+ /*
+ * The tailing '/' in _PATH_DEV is there for compatibility with libc.
+ */
+# define _PATH_DEV "/dev/"
+#endif
+
+#define _PATH_DEV_MEM "/dev/mem"
+
+#define _PATH_DEV_LOOP "/dev/loop"
+#define _PATH_DEV_LOOPCTL "/dev/loop-control"
+#define _PATH_DEV_TTY "/dev/tty"
+
+
+/* udev paths */
+#define _PATH_DEV_BYLABEL "/dev/disk/by-label"
+#define _PATH_DEV_BYUUID "/dev/disk/by-uuid"
+#define _PATH_DEV_BYID "/dev/disk/by-id"
+#define _PATH_DEV_BYPATH "/dev/disk/by-path"
+#define _PATH_DEV_BYPARTLABEL "/dev/disk/by-partlabel"
+#define _PATH_DEV_BYPARTUUID "/dev/disk/by-partuuid"
+
+/* hwclock paths */
+#ifdef CONFIG_ADJTIME_PATH
+# define _PATH_ADJTIME CONFIG_ADJTIME_PATH
+#else
+# define _PATH_ADJTIME "/etc/adjtime"
+#endif
+
+#define _PATH_LASTDATE "/var/lib/lastdate"
+#ifdef __ia64__
+# define _PATH_RTC_DEV "/dev/efirtc"
+#else
+# define _PATH_RTC_DEV "/dev/rtc"
+#endif
+
+#ifndef _PATH_BTMP
+#define _PATH_BTMP "/var/log/btmp"
+#endif
+
+/* raw paths*/
+#define _PATH_RAWDEVDIR "/dev/raw/"
+#define _PATH_RAWDEVCTL _PATH_RAWDEVDIR "rawctl"
+/* deprecated */
+#define _PATH_RAWDEVCTL_OLD "/dev/rawctl"
+
+/* wdctl path */
+#define _PATH_WATCHDOG_DEV "/dev/watchdog"
+
+/* ipc paths */
+#define _PATH_PROC_SYSV_MSG "/proc/sysvipc/msg"
+#define _PATH_PROC_SYSV_SEM "/proc/sysvipc/sem"
+#define _PATH_PROC_SYSV_SHM "/proc/sysvipc/shm"
+#define _PATH_PROC_IPC_MSGMAX "/proc/sys/kernel/msgmax"
+#define _PATH_PROC_IPC_MSGMNB "/proc/sys/kernel/msgmnb"
+#define _PATH_PROC_IPC_MSGMNI "/proc/sys/kernel/msgmni"
+#define _PATH_PROC_IPC_SEM "/proc/sys/kernel/sem"
+#define _PATH_PROC_IPC_SHMALL "/proc/sys/kernel/shmall"
+#define _PATH_PROC_IPC_SHMMAX "/proc/sys/kernel/shmmax"
+#define _PATH_PROC_IPC_SHMMNI "/proc/sys/kernel/shmmni"
+
+/* kernel command line */
+#define _PATH_PROC_CMDLINE "/proc/cmdline"
+
+#endif /* PATHNAMES_H */
+
diff --git a/libblkid/src/probe.c b/libblkid/src/probe.c
new file mode 100644
index 000000000..d476b7a23
--- /dev/null
+++ b/libblkid/src/probe.c
@@ -0,0 +1,1815 @@
+/*
+ * 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) == 0)
+ * ... 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_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <limits.h>
+
+#ifdef HAVE_LIBUUID
+# include <uuid.h>
+#endif
+
+#include "blkidP.h"
+#include <blkid.h>
+#include "all-io.h"
+#include "sysfs.h"
+#include "strutils.h"
+
+/* chains */
+extern const struct blkid_chaindrv superblocks_drv;
+extern const struct blkid_chaindrv topology_drv;
+extern const struct blkid_chaindrv partitions_drv;
+
+/*
+ * 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_vals(blkid_probe pr);
+static void blkid_probe_reset_buffer(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;
+
+ blkid_init_debug(0);
+ pr = calloc(1, sizeof(struct blkid_struct_probe));
+ if (!pr)
+ return NULL;
+
+ DBG(LOWPROBE, ul_debug("allocate a new probe %p", pr));
+
+ /* 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);
+ return pr;
+}
+
+/*
+ * Clone @parent, the new clone shares all, but except:
+ *
+ * - probing result
+ * - bufferes 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->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 = -1;
+ blkid_probe pr = NULL;
+
+ if (!filename)
+ return NULL;
+
+ fd = open(filename, O_RDONLY|O_CLOEXEC);
+ 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:
+ if (fd >= 0)
+ 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);
+ }
+
+ if ((pr->flags & BLKID_FL_PRIVATE_FD) && pr->fd >= 0)
+ close(pr->fd);
+ blkid_probe_reset_buffer(pr);
+ blkid_free_probe(pr->disk_probe);
+
+ DBG(LOWPROBE, ul_debug("free probe %p", pr));
+ free(pr);
+}
+
+
+/*
+ * Removes chain values from probing result.
+ */
+void blkid_probe_chain_reset_vals(blkid_probe pr, struct blkid_chain *chn)
+{
+ int nvals = pr->nvals;
+ int i, x;
+
+ for (x = 0, i = 0; i < pr->nvals; i++) {
+ struct blkid_prval *v = &pr->vals[i];
+
+ if (v->chain != chn && x == i) {
+ x++;
+ continue;
+ }
+ if (v->chain == chn) {
+ --nvals;
+ continue;
+ }
+ memcpy(&pr->vals[x++], v, sizeof(struct blkid_prval));
+ }
+ pr->nvals = nvals;
+}
+
+static void blkid_probe_chain_reset_position(struct blkid_chain *chn)
+{
+ if (chn)
+ chn->idx = -1;
+}
+
+/*
+ * Copies chain values from probing result to @vals, the max size of @vals is
+ * @nvals and returns real number of values.
+ */
+int blkid_probe_chain_copy_vals(blkid_probe pr, struct blkid_chain *chn,
+ struct blkid_prval *vals, int nvals)
+{
+ int i, x;
+
+ for (x = 0, i = 0; i < pr->nvals && x < nvals; i++) {
+ struct blkid_prval *v = &pr->vals[i];
+
+ if (v->chain != chn)
+ continue;
+ memcpy(&vals[x++], v, sizeof(struct blkid_prval));
+ }
+ return x;
+}
+
+/*
+ * Appends values from @vals to the probing result
+ */
+void blkid_probe_append_vals(blkid_probe pr, struct blkid_prval *vals, int nvals)
+{
+ int i = 0;
+
+ while (i < nvals && pr->nvals < BLKID_NVALS) {
+ memcpy(&pr->vals[pr->nvals++], &vals[i++],
+ sizeof(struct blkid_prval));
+ }
+}
+
+static void blkid_probe_reset_vals(blkid_probe pr)
+{
+ memset(pr->vals, 0, sizeof(pr->vals));
+ pr->nvals = 0;
+}
+
+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 && 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;
+
+ if (!pr)
+ return;
+
+ blkid_probe_reset_vals(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 (flag & BLKID_FLTR_ONLYIN) {
+ if (!has)
+ blkid_bmp_set_item(fltr, i);
+ } else if (flag & BLKID_FLTR_NOTIN) {
+ if (has)
+ 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;
+}
+
+unsigned char *blkid_probe_get_buffer(blkid_probe pr,
+ blkid_loff_t off, blkid_loff_t len)
+{
+ struct list_head *p;
+ struct blkid_bufinfo *bf = NULL;
+
+ if (pr->size <= 0) {
+ errno = EINVAL;
+ 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
+ * beginig of the device.
+ */
+ return blkid_probe_get_buffer(pr->parent,
+ pr->off + off - pr->parent->off, len);
+ }
+
+ list_for_each(p, &pr->buffers) {
+ struct blkid_bufinfo *x =
+ list_entry(p, struct blkid_bufinfo, bufs);
+
+ if (x->off <= off && off + len <= x->off + x->len) {
+ DBG(LOWPROBE, ul_debug("\treuse buffer: off=%jd len=%jd pr=%p",
+ x->off, x->len, pr));
+ bf = x;
+ break;
+ }
+ }
+ if (!bf) {
+ ssize_t ret;
+
+ if (blkid_llseek(pr->fd, pr->off + off, SEEK_SET) < 0) {
+ 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 why 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 = off;
+ INIT_LIST_HEAD(&bf->bufs);
+
+ DBG(LOWPROBE, ul_debug("\tbuffer read: off=%jd len=%jd pr=%p",
+ off, len, pr));
+
+ ret = read(pr->fd, bf->data, len);
+ if (ret != (ssize_t) len) {
+ DBG(LOWPROBE, ul_debug("\tbuffer read: return %zd error %m", ret));
+ free(bf);
+ if (ret >= 0)
+ errno = 0;
+ return NULL;
+ }
+ list_add_tail(&bf->bufs, &pr->buffers);
+ }
+
+ errno = 0;
+ return off ? bf->data + (off - bf->off) : bf->data;
+}
+
+
+static void blkid_probe_reset_buffer(blkid_probe pr)
+{
+ uint64_t read_ct = 0, len_ct = 0;
+
+ if (!pr || list_empty(&pr->buffers))
+ return;
+
+ DBG(LOWPROBE, ul_debug("reseting probing buffers pr=%p", pr));
+
+ while (!list_empty(&pr->buffers)) {
+ struct blkid_bufinfo *bf = list_entry(pr->buffers.next,
+ struct blkid_bufinfo, bufs);
+ read_ct++;
+ len_ct += bf->len;
+ list_del(&bf->bufs);
+ free(bf);
+ }
+
+ DBG(LOWPROBE, ul_debug("buffers summary: %"PRIu64" bytes "
+ "by %"PRIu64" read() call(s)",
+ len_ct, read_ct));
+
+ INIT_LIST_HEAD(&pr->buffers);
+}
+
+/*
+ * 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;
+}
+
+/**
+ * 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 and
+ * resets the current probing.
+ *
+ * Returns: -1 in case of failure, or 0 on success.
+ */
+int blkid_probe_set_device(blkid_probe pr, int fd,
+ blkid_loff_t off, blkid_loff_t size)
+{
+ struct stat sb;
+
+ if (!pr)
+ return -1;
+
+ blkid_reset_probe(pr);
+ blkid_probe_reset_buffer(pr);
+
+ if ((pr->flags & BLKID_FL_PRIVATE_FD) && pr->fd >= 0)
+ close(pr->fd);
+
+ 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 = 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;
+
+#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))
+ goto err;
+
+
+ pr->mode = sb.st_mode;
+ if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode))
+ pr->devno = sb.st_rdev;
+
+ if (size)
+ pr->size = size;
+ else {
+ if (S_ISBLK(sb.st_mode)) {
+ if (blkdev_get_size(fd, (unsigned long long *) &pr->size)) {
+ DBG(LOWPROBE, ul_debug("failed to get device size"));
+ goto err;
+ }
+ } else if (S_ISCHR(sb.st_mode))
+ pr->size = 1; /* UBI devices are char... */
+ else if (S_ISREG(sb.st_mode))
+ pr->size = sb.st_size; /* regular file */
+
+ if (pr->off > pr->size)
+ goto err;
+
+ /* The probing area cannot be larger than whole device, pr->off
+ * is offset within the device */
+ pr->size -= pr->off;
+ }
+
+ if (pr->size <= 1440 * 1024 && !S_ISCHR(sb.st_mode))
+ pr->flags |= BLKID_FL_TINY_DEV;
+
+ if (S_ISBLK(sb.st_mode) && sysfs_devno_is_lvm_private(sb.st_rdev)) {
+ DBG(LOWPROBE, ul_debug("ignore private LVM device"));
+ pr->flags |= BLKID_FL_NOSCAN_DEV;
+ }
+
+#ifdef CDROM_GET_CAPABILITY
+ else if (S_ISBLK(sb.st_mode) &&
+ !blkid_probe_is_tiny(pr) &&
+ blkid_probe_is_wholedisk(pr) &&
+ ioctl(fd, CDROM_GET_CAPABILITY, NULL) >= 0)
+ pr->flags |= BLKID_FL_CDROM_DEV;
+#endif
+
+ DBG(LOWPROBE, ul_debug("ready for low-probing, offset=%jd, size=%jd",
+ pr->off, pr->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,
+ blkid_loff_t *off, blkid_loff_t *size)
+{
+ *off = pr->off;
+ *size = pr->size;
+ return 0;
+}
+
+int blkid_probe_set_dimension(blkid_probe pr,
+ blkid_loff_t off, blkid_loff_t size)
+{
+ DBG(LOWPROBE, ul_debug(
+ "changing probing area pr=%p: size=%llu, off=%llu "
+ "-to-> size=%llu, off=%llu",
+ pr,
+ (unsigned long long) pr->size,
+ (unsigned long long) pr->off,
+ (unsigned long long) size,
+ (unsigned long long) off));
+
+ pr->off = off;
+ pr->size = size;
+ pr->flags &= ~BLKID_FL_TINY_DEV;
+
+ if (pr->size <= 1440 * 1024 && !S_ISCHR(pr->mode))
+ pr->flags |= BLKID_FL_TINY_DEV;
+
+ blkid_probe_reset_buffer(pr);
+
+ return 0;
+}
+
+/*
+ * 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,
+ blkid_loff_t *offset, const struct blkid_idmag **res)
+{
+ const struct blkid_idmag *mag = NULL;
+ blkid_loff_t off = 0;
+
+ if (id)
+ mag = &id->magics[0];
+ if (res)
+ *res = NULL;
+
+ /* try to detect by magic string */
+ while(mag && mag->magic) {
+ unsigned char *buf;
+
+ off = (mag->kboff + (mag->sboff >> 10)) << 10;
+ buf = blkid_probe_get_buffer(pr, off, 1024);
+
+ if (!buf && errno)
+ return -errno;
+ if (buf && !memcmp(mag->magic,
+ buf + (mag->sboff & 0x3ff), mag->len)) {
+ DBG(LOWPROBE, ul_debug("\tmagic sboff=%u, kboff=%ld",
+ mag->sboff, mag->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)
+{
+ if (pr) {
+ DBG(LOWPROBE, ul_debug("%p: start probe", pr));
+ pr->cur_chain = NULL;
+ pr->prob_flags = 0;
+ blkid_probe_set_wiper(pr, 0, 0);
+ }
+}
+
+static inline void blkid_probe_end(blkid_probe pr)
+{
+ if (pr) {
+ DBG(LOWPROBE, ul_debug("%p: end probe", pr));
+ 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) == 0) {
+ * 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) == 0) {
+ * 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)
+ return -1;
+
+ if (pr->flags & BLKID_FL_NOSCAN_DEV)
+ return 1;
+
+ 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 1; /* 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 == 1);
+
+ return rc;
+}
+
+/**
+ * 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 errase 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.
+ *
+ * <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 build-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;
+ loff_t offset, l;
+ char buf[BUFSIZ];
+ int fd, rc = 0;
+ struct blkid_chain *chn;
+
+ if (!pr)
+ return -1;
+
+ chn = pr->cur_chain;
+ if (!chn)
+ return -1;
+
+ 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 0;
+ }
+
+ if (rc || len == 0 || off == NULL)
+ return 0;
+
+ offset = strtoll(off, NULL, 10);
+ fd = blkid_probe_get_fd(pr);
+ if (fd < 0)
+ return -1;
+
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+
+ DBG(LOWPROBE, ul_debug(
+ "do_wipe [offset=0x%jx, len=%zd, chain=%s, idx=%d, dryrun=%s]\n",
+ offset, len, chn->driver->name, chn->idx, dryrun ? "yes" : "not"));
+
+ l = lseek(fd, offset, SEEK_SET);
+ if (l == (off_t) -1)
+ return -1;
+
+ memset(buf, 0, len);
+
+ if (!dryrun && len) {
+ if (write_all(fd, buf, len))
+ return -1;
+ fsync(fd);
+ return blkid_probe_step_back(pr);
+ }
+
+ return 0;
+}
+
+/**
+ * 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.
+ *
+ * <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) == 0) {
+ * 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 errase @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;
+
+ if (!pr)
+ return -1;
+
+ chn = pr->cur_chain;
+ if (!chn)
+ return -1;
+
+ blkid_probe_reset_buffer(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 suberblocks chain -- the function does not check for filesystems
+ * when a RAID signature is detected. The function also does not check for
+ * collision between RAIDs. The first detected RAID is returned. The function
+ * checks for collision between partition table and RAID signature -- it's
+ * recommended to enable partitions chain together with superblocks chain.
+ *
+ * Returns: 0 on success, 1 if nothing is detected, -2 if ambivalen result is
+ * detected and -1 on case of error.
+ */
+int blkid_do_safeprobe(blkid_probe pr)
+{
+ int i, count = 0, rc = 0;
+
+ if (!pr)
+ return -1;
+ if (pr->flags & BLKID_FL_NOSCAN_DEV)
+ return 1;
+
+ 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 rc;
+ return count ? 0 : 1;
+}
+
+/**
+ * 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)
+ return -1;
+ if (pr->flags & BLKID_FL_NOSCAN_DEV)
+ return 1;
+
+ 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 rc;
+ return count ? 0 : 1;
+}
+
+/* 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, ((blkid_loff_t) sector) << 9, 0x200);
+}
+
+struct blkid_prval *blkid_probe_assign_value(
+ blkid_probe pr, const char *name)
+{
+ struct blkid_prval *v;
+
+ if (pr->nvals >= BLKID_NVALS)
+ return NULL;
+
+ v = &pr->vals[pr->nvals];
+ v->name = name;
+ v->chain = pr->cur_chain;
+ pr->nvals++;
+
+ DBG(LOWPROBE, ul_debug("assigning %s [%s]", name, v->chain->driver->name));
+ return v;
+}
+
+int blkid_probe_reset_last_value(blkid_probe pr)
+{
+ struct blkid_prval *v;
+
+ if (pr->nvals == 0)
+ return -1;
+
+ v = &pr->vals[pr->nvals - 1];
+
+ DBG(LOWPROBE, ul_debug("un-assigning %s [%s]", v->name, v->chain->driver->name));
+
+ memset(v, 0, sizeof(struct blkid_prval));
+ pr->nvals--;
+
+ return 0;
+
+}
+
+int blkid_probe_set_value(blkid_probe pr, const char *name,
+ unsigned char *data, size_t len)
+{
+ struct blkid_prval *v;
+
+ if (len > BLKID_PROBVAL_BUFSIZ)
+ len = BLKID_PROBVAL_BUFSIZ;
+
+ v = blkid_probe_assign_value(pr, name);
+ if (!v)
+ return -1;
+
+ memcpy(v->data, data, len);
+ v->len = len;
+ return 0;
+}
+
+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 -1;
+
+ len = vsnprintf((char *) v->data, sizeof(v->data), fmt, ap);
+
+ if (len <= 0 || (size_t) len >= sizeof(v->data)) {
+ blkid_probe_reset_last_value(pr);
+ return -1;
+ }
+ 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, blkid_loff_t offset,
+ size_t len, 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;
+}
+
+int blkid_probe_verify_csum(blkid_probe pr, uint64_t csum, uint64_t expected)
+{
+ if (csum != expected) {
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+ DBG(LOWPROBE, ul_debug(
+ "incorrect checksum for type %s,"
+ " got %jX, expected %jX",
+ blkid_probe_get_probername(pr),
+ csum, expected));
+ /*
+ * Accept bad checksum if BLKID_SUBLKS_BADCSUM flags is set
+ */
+ if (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;
+}
+
+/**
+ * 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);
+
+ 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? */
+ }
+
+ 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 pr ? pr->size : -1;
+}
+
+/**
+ * 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 pr ? pr->off : -1;
+}
+
+/**
+ * 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 ? pr->fd : -1;
+}
+
+/**
+ * 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)
+ return DEFAULT_SECTOR_SIZE; /*... and good luck! */
+
+ 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_get_sectors:
+ * @pr: probe
+ *
+ * Returns: 512-byte sector count or -1 in case of error.
+ */
+blkid_loff_t blkid_probe_get_sectors(blkid_probe pr)
+{
+ return pr ? pr->size >> 9 : -1;
+}
+
+/**
+ * blkid_probe_numof_values:
+ * @pr: probe
+ *
+ * Returns: number of values in probing result or -1 in case of error.
+ */
+int blkid_probe_numof_values(blkid_probe pr)
+{
+ if (!pr)
+ return -1;
+ return pr->nvals;
+}
+
+/**
+ * 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)
+{
+ if (num < 0 || num >= pr->nvals)
+ return NULL;
+
+ return &pr->vals[num];
+}
+
+struct blkid_prval *__blkid_probe_lookup_value(blkid_probe pr, const char *name)
+{
+ int i;
+
+ if (!pr->nvals)
+ return NULL;
+
+ for (i = 0; i < pr->nvals; i++) {
+ struct blkid_prval *v = &pr->vals[i];
+
+ 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 */
+#ifdef HAVE_LIBUUID
+void blkid_unparse_uuid(const unsigned char *uuid, char *str,
+ size_t len __attribute__((__unused__)))
+{
+ uuid_unparse(uuid, str);
+}
+#else
+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]);
+}
+#endif
+
+/* 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" allows to keep the begin of the device unmodified. It's probably better
+ * to use this heuristic for conflicts between superblocks and partition tables
+ * than for conflicts between filesystem superblocks -- existence of unwanted
+ * partition table is very unusual, because PT is pretty visible (parsed and
+ * interpreted by kernel).
+ *
+ * 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, blkid_loff_t off, blkid_loff_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=%jd size=%jd",
+ chn->driver->name,
+ chn->driver->idinfos[chn->idx]->name,
+ pr->wipe_off, pr->wipe_size));
+ return;
+}
+
+/*
+ * Returns 1 if the <@off,@size> area was wiped
+ */
+int blkid_probe_is_wiped(blkid_probe pr, struct blkid_chain **chn,
+ blkid_loff_t off, blkid_loff_t size)
+{
+ if (!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 (reseted).
+ */
+void blkid_probe_use_wiper(blkid_probe pr, blkid_loff_t off, blkid_loff_t size)
+{
+ struct blkid_chain *chn = NULL;
+
+ if (blkid_probe_is_wiped(pr, &chn, off, size) && chn) {
+ DBG(LOWPROBE, ul_debug("previously wiped area modified "
+ " -- ignore previous results"));
+ blkid_probe_set_wiper(pr, 0, 0);
+ blkid_probe_chain_reset_vals(pr, chn);
+ }
+}
diff --git a/libblkid/src/pt-bsd.h b/libblkid/src/pt-bsd.h
new file mode 100644
index 000000000..9bf47a576
--- /dev/null
+++ b/libblkid/src/pt-bsd.h
@@ -0,0 +1,156 @@
+#ifndef UTIL_LINUX_PT_BSD_H
+#define UTIL_LINUX_PT_BSD_H
+
+#define BSD_MAXPARTITIONS 16
+#define BSD_FS_UNUSED 0
+
+#ifndef BSD_DISKMAGIC
+# define BSD_DISKMAGIC ((uint32_t) 0x82564557)
+#endif
+
+#define BSD_LINUX_BOOTDIR "/usr/ucb/mdec"
+
+#if defined (__alpha__) || defined (__powerpc__) || \
+ defined (__ia64__) || defined (__hppa__)
+# define BSD_LABELSECTOR 0
+# define BSD_LABELOFFSET 64
+#else
+# define BSD_LABELSECTOR 1
+# define BSD_LABELOFFSET 0
+#endif
+
+#define BSD_BBSIZE 8192 /* size of boot area, with label */
+#define BSD_SBSIZE 8192 /* max size of fs superblock */
+
+struct bsd_disklabel {
+ uint32_t d_magic; /* the magic number */
+ int16_t d_type; /* drive type */
+ int16_t d_subtype; /* controller/d_type specific */
+ char d_typename[16]; /* type name, e.g. "eagle" */
+ char d_packname[16]; /* pack identifier */
+
+ /* disk geometry: */
+ uint32_t d_secsize; /* # of bytes per sector */
+ uint32_t d_nsectors; /* # of data sectors per track */
+ uint32_t d_ntracks; /* # of tracks per cylinder */
+ uint32_t d_ncylinders; /* # of data cylinders per unit */
+ uint32_t d_secpercyl; /* # of data sectors per cylinder */
+ uint32_t d_secperunit; /* # of data sectors per unit */
+
+ /*
+ * Spares (bad sector replacements) below
+ * are not counted in d_nsectors or d_secpercyl.
+ * Spare sectors are assumed to be physical sectors
+ * which occupy space at the end of each track and/or cylinder.
+ */
+ uint16_t d_sparespertrack; /* # of spare sectors per track */
+ uint16_t d_sparespercyl; /* # of spare sectors per cylinder */
+
+ /*
+ * Alternate cylinders include maintenance, replacement,
+ * configuration description areas, etc.
+ */
+ uint32_t d_acylinders; /* # of alt. cylinders per unit */
+
+ /* hardware characteristics: */
+ /*
+ * d_interleave, d_trackskew and d_cylskew describe perturbations
+ * in the media format used to compensate for a slow controller.
+ * Interleave is physical sector interleave, set up by the formatter
+ * or controller when formatting. When interleaving is in use,
+ * logically adjacent sectors are not physically contiguous,
+ * but instead are separated by some number of sectors.
+ * It is specified as the ratio of physical sectors traversed
+ * per logical sector. Thus an interleave of 1:1 implies contiguous
+ * layout, while 2:1 implies that logical sector 0 is separated
+ * by one sector from logical sector 1.
+ * d_trackskew is the offset of sector 0 on track N
+ * relative to sector 0 on track N-1 on the same cylinder.
+ * Finally, d_cylskew is the offset of sector 0 on cylinder N
+ * relative to sector 0 on cylinder N-1.
+ */
+ uint16_t d_rpm; /* rotational speed */
+ uint16_t d_interleave; /* hardware sector interleave */
+ uint16_t d_trackskew; /* sector 0 skew, per track */
+ uint16_t d_cylskew; /* sector 0 skew, per cylinder */
+ uint32_t d_headswitch; /* head switch time, usec */
+ uint32_t d_trkseek; /* track-to-track seek, usec */
+ uint32_t d_flags; /* generic flags */
+ uint32_t d_drivedata[5]; /* drive-type specific information */
+ uint32_t d_spare[5]; /* reserved for future use */
+ uint32_t d_magic2; /* the magic number (again) */
+ uint16_t d_checksum; /* xor of data incl. partitions */
+
+ /* filesystem and partition information: */
+ uint16_t d_npartitions; /* number of partitions in following */
+ uint32_t d_bbsize; /* size of boot area at sn0, bytes */
+ uint32_t d_sbsize; /* max size of fs superblock, bytes */
+
+ struct bsd_partition { /* the partition table */
+ uint32_t p_size; /* number of sectors in partition */
+ uint32_t p_offset; /* starting sector */
+ uint32_t p_fsize; /* filesystem basic fragment size */
+ uint8_t p_fstype; /* filesystem type, see below */
+ uint8_t p_frag; /* filesystem fragments per block */
+ uint16_t p_cpg; /* filesystem cylinders per group */
+ } __attribute__((packed)) d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */
+} __attribute__((packed));
+
+
+/* d_type values: */
+#define BSD_DTYPE_SMD 1 /* SMD, XSMD; VAX hp/up */
+#define BSD_DTYPE_MSCP 2 /* MSCP */
+#define BSD_DTYPE_DEC 3 /* other DEC (rk, rl) */
+#define BSD_DTYPE_SCSI 4 /* SCSI */
+#define BSD_DTYPE_ESDI 5 /* ESDI interface */
+#define BSD_DTYPE_ST506 6 /* ST506 etc. */
+#define BSD_DTYPE_HPIB 7 /* CS/80 on HP-IB */
+#define BSD_DTYPE_HPFL 8 /* HP Fiber-link */
+#define BSD_DTYPE_FLOPPY 10 /* floppy */
+
+/* d_subtype values: */
+#define BSD_DSTYPE_INDOSPART 0x8 /* is inside dos partition */
+#define BSD_DSTYPE_DOSPART(s) ((s) & 3) /* dos partition number */
+#define BSD_DSTYPE_GEOMETRY 0x10 /* drive params in label */
+
+/*
+ * Filesystem type and version.
+ * Used to interpret other filesystem-specific
+ * per-partition information.
+ */
+#define BSD_FS_UNUSED 0 /* unused */
+#define BSD_FS_SWAP 1 /* swap */
+#define BSD_FS_V6 2 /* Sixth Edition */
+#define BSD_FS_V7 3 /* Seventh Edition */
+#define BSD_FS_SYSV 4 /* System V */
+#define BSD_FS_V71K 5 /* V7 with 1K blocks (4.1, 2.9) */
+#define BSD_FS_V8 6 /* Eighth Edition, 4K blocks */
+#define BSD_FS_BSDFFS 7 /* 4.2BSD fast file system */
+#define BSD_FS_BSDLFS 9 /* 4.4BSD log-structured file system */
+#define BSD_FS_OTHER 10 /* in use, but unknown/unsupported */
+#define BSD_FS_HPFS 11 /* OS/2 high-performance file system */
+#define BSD_FS_ISO9660 12 /* ISO-9660 filesystem (cdrom) */
+#define BSD_FS_ISOFS BSD_FS_ISO9660
+#define BSD_FS_BOOT 13 /* partition contains bootstrap */
+#define BSD_FS_ADOS 14 /* AmigaDOS fast file system */
+#define BSD_FS_HFS 15 /* Macintosh HFS */
+#define BSD_FS_ADVFS 16 /* Digital Unix AdvFS */
+
+/* this is annoying, but it's also the way it is :-( */
+#ifdef __alpha__
+#define BSD_FS_EXT2 8 /* ext2 file system */
+#else
+#define BSD_FS_MSDOS 8 /* MS-DOS file system */
+#endif
+
+/*
+ * flags shared by various drives:
+ */
+#define BSD_D_REMOVABLE 0x01 /* removable media */
+#define BSD_D_ECC 0x02 /* supports ECC */
+#define BSD_D_BADSECT 0x04 /* supports bad sector forw. */
+#define BSD_D_RAMDISK 0x08 /* disk emulator */
+#define BSD_D_CHAIN 0x10 /* can do back-back transfers */
+#define BSD_D_DOSPART 0x20 /* within MSDOS partition */
+
+#endif /* UTIL_LINUX_PT_BSD_H */
diff --git a/libblkid/src/pt-mbr.h b/libblkid/src/pt-mbr.h
new file mode 100644
index 000000000..1279e3cf2
--- /dev/null
+++ b/libblkid/src/pt-mbr.h
@@ -0,0 +1,178 @@
+#ifndef UTIL_LINUX_PT_MBR_H
+#define UTIL_LINUX_PT_MBR_H
+
+struct dos_partition {
+ unsigned char boot_ind; /* 0x80 - active */
+ unsigned char bh, bs, bc; /* begin CHS */
+ unsigned char sys_ind;
+ unsigned char eh, es, ec; /* end CHS */
+ unsigned char start_sect[4];
+ unsigned char nr_sects[4];
+} __attribute__((packed));
+
+#define MBR_PT_OFFSET 0x1be
+
+static inline struct dos_partition *mbr_get_partition(unsigned char *mbr, int i)
+{
+ return (struct dos_partition *)
+ (mbr + MBR_PT_OFFSET + (i * sizeof(struct dos_partition)));
+}
+
+/* assemble badly aligned little endian integer */
+static inline unsigned int __dos_assemble_4le(const unsigned char *p)
+{
+ return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+}
+
+static inline void __dos_store_4le(unsigned char *p, unsigned int val)
+{
+ p[0] = (val & 0xff);
+ p[1] = ((val >> 8) & 0xff);
+ p[2] = ((val >> 16) & 0xff);
+ p[3] = ((val >> 24) & 0xff);
+}
+
+static inline unsigned int dos_partition_get_start(struct dos_partition *p)
+{
+ return __dos_assemble_4le(&(p->start_sect[0]));
+}
+
+static inline void dos_partition_set_start(struct dos_partition *p, unsigned int n)
+{
+ __dos_store_4le(p->start_sect, n);
+}
+
+static inline unsigned int dos_partition_get_size(struct dos_partition *p)
+{
+ return __dos_assemble_4le(&(p->nr_sects[0]));
+}
+
+static inline void dos_partition_set_size(struct dos_partition *p, unsigned int n)
+{
+ __dos_store_4le(p->nr_sects, n);
+}
+
+static inline int mbr_is_valid_magic(const unsigned char *mbr)
+{
+ return mbr[510] == 0x55 && mbr[511] == 0xaa ? 1 : 0;
+}
+
+static inline void mbr_set_magic(unsigned char *b)
+{
+ b[510] = 0x55;
+ b[511] = 0xaa;
+}
+
+static inline unsigned int mbr_get_id(const unsigned char *mbr)
+{
+ return __dos_assemble_4le(&mbr[440]);
+}
+
+static inline void mbr_set_id(unsigned char *b, unsigned int id)
+{
+ __dos_store_4le(&b[440], id);
+}
+
+enum {
+ MBR_EMPTY_PARTITION = 0x00,
+ MBR_FAT12_PARTITION = 0x01,
+ MBR_XENIX_ROOT_PARTITION = 0x02,
+ MBR_XENIX_USR_PARTITION = 0x03,
+ MBR_FAT16_LESS32M_PARTITION = 0x04,
+ MBR_DOS_EXTENDED_PARTITION = 0x05,
+ MBR_FAT16_PARTITION = 0x06, /* DOS 16-bit >=32M */
+ MBR_HPFS_NTFS_PARTITION = 0x07, /* OS/2 IFS, eg, HPFS or NTFS or QNX */
+ MBR_AIX_PARTITION = 0x08, /* AIX boot (AIX -- PS/2 port) or SplitDrive */
+ MBR_AIX_BOOTABLE_PARTITION = 0x09, /* AIX data or Coherent */
+ MBR_OS2_BOOTMNGR_PARTITION = 0x0a, /* OS/2 Boot Manager */
+ MBR_W95_FAT32_PARTITION = 0x0b,
+ MBR_W95_FAT32_LBA_PARTITION = 0x0c, /* LBA really is `Extended Int 13h' */
+ MBR_W95_FAT16_LBA_PARTITION = 0x0e,
+ MBR_W95_EXTENDED_PARTITION = 0x0f,
+ MBR_OPUS_PARTITION = 0x10,
+ MBR_HIDDEN_FAT12_PARTITION = 0x11,
+ MBR_COMPAQ_DIAGNOSTICS_PARTITION = 0x12,
+ MBR_HIDDEN_FAT16_L32M_PARTITION = 0x14,
+ MBR_HIDDEN_FAT16_PARTITION = 0x16,
+ MBR_HIDDEN_HPFS_NTFS_PARTITION = 0x17,
+ MBR_AST_SMARTSLEEP_PARTITION = 0x18,
+ MBR_HIDDEN_W95_FAT32_PARTITION = 0x1b,
+ MBR_HIDDEN_W95_FAT32LBA_PARTITION = 0x1c,
+ MBR_HIDDEN_W95_FAT16LBA_PARTITION = 0x1e,
+ MBR_NEC_DOS_PARTITION = 0x24,
+ MBR_PLAN9_PARTITION = 0x39,
+ MBR_PARTITIONMAGIC_PARTITION = 0x3c,
+ MBR_VENIX80286_PARTITION = 0x40,
+ MBR_PPC_PREP_BOOT_PARTITION = 0x41,
+ MBR_SFS_PARTITION = 0x42,
+ MBR_QNX_4X_PARTITION = 0x4d,
+ MBR_QNX_4X_2ND_PARTITION = 0x4e,
+ MBR_QNX_4X_3RD_PARTITION = 0x4f,
+ MBR_DM_PARTITION = 0x50,
+ MBR_DM6_AUX1_PARTITION = 0x51, /* (or Novell) */
+ MBR_CPM_PARTITION = 0x52, /* CP/M or Microport SysV/AT */
+ MBR_DM6_AUX3_PARTITION = 0x53,
+ MBR_DM6_PARTITION = 0x54,
+ MBR_EZ_DRIVE_PARTITION = 0x55,
+ MBR_GOLDEN_BOW_PARTITION = 0x56,
+ MBR_PRIAM_EDISK_PARTITION = 0x5c,
+ MBR_SPEEDSTOR_PARTITION = 0x61,
+ MBR_GNU_HURD_PARTITION = 0x63, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+ MBR_UNIXWARE_PARTITION = MBR_GNU_HURD_PARTITION,
+ MBR_NETWARE_286_PARTITION = 0x64,
+ MBR_NETWARE_386_PARTITION = 0x65,
+ MBR_DISKSECURE_MULTIBOOT_PARTITION = 0x70,
+ MBR_PC_IX_PARTITION = 0x75,
+ MBR_OLD_MINIX_PARTITION = 0x80, /* Minix 1.4a and earlier */
+ MBR_MINIX_PARTITION = 0x81, /* Minix 1.4b and later */
+ MBR_LINUX_SWAP_PARTITION = 0x82,
+ MBR_SOLARIS_X86_PARTITION = MBR_LINUX_SWAP_PARTITION,
+ MBR_LINUX_DATA_PARTITION = 0x83,
+ MBR_OS2_HIDDEN_DRIVE_PARTITION = 0x84,
+ MBR_LINUX_EXTENDED_PARTITION = 0x85,
+ MBR_NTFS_VOL_SET1_PARTITION = 0x86,
+ MBR_NTFS_VOL_SET2_PARTITION = 0x87,
+ MBR_LINUX_PLAINTEXT_PARTITION = 0x88,
+ MBR_LINUX_LVM_PARTITION = 0x8e,
+ MBR_AMOEBA_PARTITION = 0x93,
+ MBR_AMOEBA_BBT_PARTITION = 0x94, /* (bad block table) */
+ MBR_BSD_OS_PARTITION = 0x9f, /* BSDI */
+ MBR_THINKPAD_HIBERNATION_PARTITION = 0xa0,
+ MBR_FREEBSD_PARTITION = 0xa5, /* various BSD flavours */
+ MBR_OPENBSD_PARTITION = 0xa6,
+ MBR_NEXTSTEP_PARTITION = 0xa7,
+ MBR_DARWIN_UFS_PARTITION = 0xa8,
+ MBR_NETBSD_PARTITION = 0xa9,
+ MBR_DARWIN_BOOT_PARTITION = 0xab,
+ MBR_HFS_HFS_PARTITION = 0xaf,
+ MBR_BSDI_FS_PARTITION = 0xb7,
+ MBR_BSDI_SWAP_PARTITION = 0xb8,
+ MBR_BOOTWIZARD_HIDDEN_PARTITION = 0xbb,
+ MBR_SOLARIS_BOOT_PARTITION = 0xbe,
+ MBR_SOLARIS_PARTITION = 0xbf,
+ MBR_DRDOS_FAT12_PARTITION = 0xc1,
+ MBR_DRDOS_FAT16_L32M_PARTITION = 0xc4,
+ MBR_DRDOS_FAT16_PARTITION = 0xc6,
+ MBR_SYRINX_PARTITION = 0xc7,
+ MBR_NONFS_DATA_PARTITION = 0xda,
+ MBR_CPM_CTOS_PARTITION = 0xdb, /* CP/M or Concurrent CP/M or Concurrent DOS or CTOS */
+ MBR_DELL_UTILITY_PARTITION = 0xde, /* Dell PowerEdge Server utilities */
+ MBR_BOOTIT_PARTITION = 0xdf, /* BootIt EMBRM */
+ MBR_DOS_ACCESS_PARTITION = 0xe1, /* DOS access or SpeedStor 12-bit FAT extended partition */
+ MBR_DOS_RO_PARTITION = 0xe3, /* DOS R/O or SpeedStor */
+ MBR_SPEEDSTOR_EXTENDED_PARTITION = 0xe4, /* SpeedStor 16-bit FAT extended partition < 1024 cyl. */
+ MBR_BEOS_FS_PARTITION = 0xeb,
+ MBR_GPT_PARTITION = 0xee, /* Intel EFI GUID Partition Table */
+ MBR_EFI_SYSTEM_PARTITION = 0xef, /* Intel EFI System Partition */
+ MBR_LINUX_PARISC_BOOT_PARTITION = 0xf0, /* Linux/PA-RISC boot loader */
+ MBR_SPEEDSTOR1_PARTITION = 0xf1,
+ MBR_SPEEDSTOR2_PARTITION = 0xf4, /* SpeedStor large partition */
+ MBR_DOS_SECONDARY_PARTITION = 0xf2, /* DOS 3.3+ secondary */
+ MBR_VMWARE_VMFS_PARTITION = 0xfb,
+ MBR_VMWARE_VMKCORE_PARTITION = 0xfc, /* VMware kernel dump partition */
+ MBR_LINUX_RAID_PARTITION = 0xfd, /* New (2.2.x) raid partition with autodetect using persistent superblock */
+ MBR_LANSTEP_PARTITION = 0xfe, /* SpeedStor >1024 cyl. or LANstep */
+ MBR_XENIX_BBT_PARTITION = 0xff, /* Xenix Bad Block Table */
+};
+
+#endif /* UTIL_LINUX_PT_MBR_H */
diff --git a/libblkid/src/read.c b/libblkid/src/read.c
new file mode 100644
index 000000000..99a9684c8
--- /dev/null
+++ b/libblkid/src/read.c
@@ -0,0 +1,507 @@
+/*
+ * 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
+
+#ifdef HAVE_STRTOULL
+#define STRTOULL strtoull /* defined in stdlib.h if you try hard enough */
+#else
+/* FIXME: need to support real strtoull here */
+#define STRTOULL strtoul
+#endif
+
+#ifdef TEST_PROGRAM
+#define blkid_debug_dump_dev(dev) (debug_dump_dev(dev))
+static void debug_dump_dev(blkid_dev dev);
+#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 of the form <NAME>value</NAME> from the line.
+ */
+/*
+static int parse_xml(char **name, char **value, char **cp)
+{
+ char *end;
+
+ if (!name || !value || !cp)
+ return -BLKID_ERR_PARAM;
+
+ *name = strip_line(*cp);
+
+ if ((*name)[0] != '<' || (*name)[1] == '/')
+ return 0;
+
+ FIXME: finish this.
+}
+*/
+
+/*
+ * 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 /* &&
+ (ret = parse_xml(&name, &value, cp)) <= 0 */)
+ return ret;
+
+ /* Some tags are stored directly in the device struct */
+ if (!strcmp(name, "DEVNO"))
+ dev->bid_devno = STRTOULL(value, 0, 0);
+ else if (!strcmp(name, "PRI"))
+ dev->bid_pri = strtol(value, 0, 0);
+ else if (!strcmp(name, "TIME")) {
+ char *end = NULL;
+ dev->bid_time = STRTOULL(value, &end, 0);
+ if (end && *end == '.')
+ dev->bid_utime = STRTOULL(end + 1, 0, 0);
+ } else
+ ret = blkid_set_tag(dev, name, value, strlen(value));
+
+ DBG(READ, ul_debug(" tag: %s=\"%s\"", name, 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;
+ }
+
+ DBG(READ, blkid_debug_dump_dev(dev));
+
+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);
+ return;
+}
+
+#ifdef TEST_PROGRAM
+static void debug_dump_dev(blkid_dev dev)
+{
+ struct list_head *p;
+
+ if (!dev) {
+ printf(" dev: NULL\n");
+ return;
+ }
+
+ printf(" dev: name = %s\n", dev->bid_name);
+ printf(" dev: DEVNO=\"0x%0llx\"\n", (long long)dev->bid_devno);
+ printf(" dev: TIME=\"%ld.%ld\"\n", (long)dev->bid_time, (long)dev->bid_utime);
+ printf(" dev: PRI=\"%d\"\n", dev->bid_pri);
+ printf(" 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)
+ printf(" tag: %s=\"%s\"\n", tag->bit_name,
+ tag->bit_val);
+ else
+ printf(" tag: NULL\n");
+ }
+ printf("\n");
+}
+
+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 000000000..59f0fea2b
--- /dev/null
+++ b/libblkid/src/resolve.c
@@ -0,0 +1,130 @@
+/*
+ * 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 %s on %s", 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 = 0, *v = 0;
+ 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;
+ }
+ blkid_parse_tag_string(token, &t, &v);
+ if (!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 000000000..bdc9c5980
--- /dev/null
+++ b/libblkid/src/save.c
@@ -0,0 +1,241 @@
+/*
+ * 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 "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=\"%ld.%ld\"",
+ (unsigned long) dev->bid_devno,
+ (long) dev->bid_time,
+ (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)) {
+ tmp = malloc(strlen(filename) + 8);
+ if (tmp) {
+ sprintf(tmp, "%s-XXXXXX", filename);
+ fd = mkstemp(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;
+
+ backup = malloc(strlen(filename) + 5);
+ if (backup) {
+ sprintf(backup, "%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/strutils.h b/libblkid/src/strutils.h
new file mode 100644
index 000000000..4d8463a6d
--- /dev/null
+++ b/libblkid/src/strutils.h
@@ -0,0 +1,204 @@
+#ifndef UTIL_LINUX_STRUTILS
+#define UTIL_LINUX_STRUTILS
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+/* default strtoxx_or_err() exit code */
+#ifndef STRTOXX_EXIT_CODE
+# define STRTOXX_EXIT_CODE EXIT_FAILURE
+#endif
+
+
+extern int parse_size(const char *str, uintmax_t *res, int *power);
+extern int strtosize(const char *str, uintmax_t *res);
+extern uintmax_t strtosize_or_err(const char *str, const char *errmesg);
+
+extern int16_t strtos16_or_err(const char *str, const char *errmesg);
+extern uint16_t strtou16_or_err(const char *str, const char *errmesg);
+
+extern int32_t strtos32_or_err(const char *str, const char *errmesg);
+extern uint32_t strtou32_or_err(const char *str, const char *errmesg);
+
+extern int64_t strtos64_or_err(const char *str, const char *errmesg);
+extern uint64_t strtou64_or_err(const char *str, const char *errmesg);
+
+extern double strtod_or_err(const char *str, const char *errmesg);
+
+extern long strtol_or_err(const char *str, const char *errmesg);
+extern unsigned long strtoul_or_err(const char *str, const char *errmesg);
+
+extern void strtotimeval_or_err(const char *str, struct timeval *tv,
+ const char *errmesg);
+
+extern int isdigit_string(const char *str);
+
+#ifndef HAVE_MEMPCPY
+extern void *mempcpy(void *restrict dest, const void *restrict src, size_t n);
+#endif
+#ifndef HAVE_STRNLEN
+extern size_t strnlen(const char *s, size_t maxlen);
+#endif
+#ifndef HAVE_STRNDUP
+extern char *strndup(const char *s, size_t n);
+#endif
+#ifndef HAVE_STRNCHR
+extern char *strnchr(const char *s, size_t maxlen, int c);
+#endif
+
+/* caller guarantees n > 0 */
+static inline void xstrncpy(char *dest, const char *src, size_t n)
+{
+ strncpy(dest, src, n-1);
+ dest[n-1] = 0;
+}
+
+static inline char *strdup_to_offset(void *stru, size_t offset, const char *str)
+{
+ char *n = NULL;
+ char **o = (char **) ((char *) stru + offset);
+
+ if (str) {
+ n = strdup(str);
+ if (!n)
+ return NULL;
+ }
+
+ free(*o);
+ *o = n;
+ return n;
+}
+
+#define strdup_to_struct_member(_s, _m, _str) \
+ strdup_to_offset((void *) _s, offsetof(__typeof__(*(_s)), _m), _str)
+
+extern void strmode(mode_t mode, char *str);
+
+/* Options for size_to_human_string() */
+enum
+{
+ SIZE_SUFFIX_1LETTER = 0,
+ SIZE_SUFFIX_3LETTER = 1,
+ SIZE_SUFFIX_SPACE = 2
+};
+
+extern char *size_to_human_string(int options, uint64_t bytes);
+
+extern int string_to_idarray(const char *list, int ary[], size_t arysz,
+ int (name2id)(const char *, size_t));
+extern int string_add_to_idarray(const char *list, int ary[],
+ size_t arysz, int *ary_pos,
+ int (name2id)(const char *, size_t));
+
+extern int string_to_bitarray(const char *list, char *ary,
+ int (*name2bit)(const char *, size_t));
+
+extern int string_to_bitmask(const char *list,
+ unsigned long *mask,
+ long (*name2flag)(const char *, size_t));
+extern int parse_range(const char *str, int *lower, int *upper, int def);
+
+extern int streq_except_trailing_slash(const char *s1, const char *s2);
+
+/*
+ * Match string beginning.
+ */
+static inline const char *startswith(const char *s, const char *prefix)
+{
+ size_t sz = prefix ? strlen(prefix) : 0;
+
+ if (s && sz && strncmp(s, prefix, sz) == 0)
+ return s + sz;
+ return NULL;
+}
+
+/*
+ * Case insensitive match string beginning.
+ */
+static inline const char *startswith_no_case(const char *s, const char *prefix)
+{
+ size_t sz = prefix ? strlen(prefix) : 0;
+
+ if (s && sz && strncasecmp(s, prefix, sz) == 0)
+ return s + sz;
+ return NULL;
+}
+
+/*
+ * Match string ending.
+ */
+static inline const char *endswith(const char *s, const char *postfix)
+{
+ size_t sl = s ? strlen(s) : 0;
+ size_t pl = postfix ? strlen(postfix) : 0;
+
+ if (pl == 0)
+ return (char *)s + sl;
+ if (sl < pl)
+ return NULL;
+ if (memcmp(s + sl - pl, postfix, pl) != 0)
+ return NULL;
+ return (char *)s + sl - pl;
+}
+
+/*
+ * Skip leading white space.
+ */
+static inline const char *skip_space(const char *p)
+{
+ while (isspace(*p))
+ ++p;
+ return p;
+}
+
+static inline const char *skip_blank(const char *p)
+{
+ while (isblank(*p))
+ ++p;
+ return p;
+}
+
+
+/* Removes whitespace from the right-hand side of a string (trailing
+ * whitespace).
+ *
+ * Returns size of the new string (without \0).
+ */
+static inline size_t rtrim_whitespace(unsigned char *str)
+{
+ size_t i = strlen((char *) str);
+
+ while (i) {
+ i--;
+ if (!isspace(str[i])) {
+ i++;
+ break;
+ }
+ }
+ str[i] = '\0';
+ return i;
+}
+
+/* Removes whitespace from the left-hand side of a string.
+ *
+ * Returns size of the new string (without \0).
+ */
+static inline size_t ltrim_whitespace(unsigned char *str)
+{
+ size_t len;
+ unsigned char *p;
+
+ for (p = str; p && isspace(*p); p++);
+
+ len = strlen((char *) p);
+
+ if (len && p > str)
+ memmove(str, p, len + 1);
+
+ return len;
+}
+
+#endif
diff --git a/libblkid/src/superblocks/adaptec_raid.c b/libblkid/src/superblocks/adaptec_raid.c
new file mode 100644
index 000000000..65fd5b8b4
--- /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/bcache.c b/libblkid/src/superblocks/bcache.c
new file mode 100644
index 000000000..b3e397ba1
--- /dev/null
+++ b/libblkid/src/superblocks/bcache.c
@@ -0,0 +1,136 @@
+/*
+ * 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 "superblocks.h"
+#include "crc64.h"
+
+#define SB_LABEL_SIZE 32
+#define SB_JOURNAL_BUCKETS 256U
+
+#define node(i, j) ((i)->d + (j))
+#define end(i) node(i, (i)->keys)
+
+static const char bcache_magic[] = {
+ 0xc6, 0x85, 0x73, 0xf6, 0x4e, 0x1a, 0x45, 0xca,
+ 0x82, 0x65, 0xf5, 0x7f, 0x48, 0xba, 0x6d, 0x81
+};
+
+struct bcache_super_block {
+ uint64_t csum;
+ uint64_t offset; /* sector where this sb was written */
+ uint64_t version;
+
+ uint8_t magic[16];
+
+ uint8_t uuid[16];
+ union {
+ uint8_t set_uuid[16];
+ uint64_t set_magic;
+ };
+ uint8_t label[SB_LABEL_SIZE];
+
+ uint64_t flags;
+ uint64_t seq;
+ uint64_t pad[8];
+
+ 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;
+
+ /*
+ * block_size from the cache device section is still used by
+ * backing devices, so don't add anything here until we fix
+ * things to not need it for backing devices anymore
+ */
+ };
+ };
+
+ uint32_t last_mount; /* time_t */
+
+ uint16_t first_bucket;
+ union {
+ uint16_t njournal_buckets;
+ uint16_t keys;
+ };
+ uint64_t d[SB_JOURNAL_BUCKETS]; /* journal buckets */
+};
+
+/* magic string */
+#define BCACHE_SB_MAGIC bcache_magic
+/* magic string len */
+#define BCACHE_SB_MAGIC_LEN sizeof (bcache_magic)
+/* super block offset */
+#define BCACHE_SB_OFF 0x1000
+/* 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)
+
+static uint64_t bcache_crc64(struct bcache_super_block *bcs)
+{
+ unsigned char *data = (unsigned char *) bcs;
+ size_t sz;
+
+ data += 8; /* skip csum field */
+ sz = (unsigned char *) end(bcs) - data;
+
+ return crc64(0xFFFFFFFFFFFFFFFFULL, data, sz) ^ 0xFFFFFFFFFFFFFFFFULL;
+}
+
+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 (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / 512)
+ return BLKID_PROBE_NONE;
+ if (!blkid_probe_verify_csum(pr, bcache_crc64(bcs), le64_to_cpu(bcs->csum)))
+ return BLKID_PROBE_NONE;
+
+ if (blkid_probe_set_uuid(pr, bcs->uuid) < 0)
+ return BLKID_PROBE_NONE;
+
+ 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 }
+ }
+};
+
diff --git a/libblkid/src/superblocks/befs.c b/libblkid/src/superblocks/befs.c
new file mode 100644
index 000000000..e4453bc17
--- /dev/null
+++ b/libblkid/src/superblocks/befs.c
@@ -0,0 +1,482 @@
+/*
+ * 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,
+ ((blkid_loff_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))
+ + ((blkid_loff_t) FS16_TO_CPU(br->start, fs_le)
+ << FS32_TO_CPU(bs->block_shift, fs_le)),
+ (blkid_loff_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,
+ ((blkid_loff_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))
+ + ((blkid_loff_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);
+ else
+ 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);
+ else
+ start -= br_len;
+ }
+ } else if (start < (int64_t) FS64_TO_CPU(ds->max_double_indirect_range, fs_le)) {
+ struct block_run *br;
+ int64_t 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;
+
+ br = (struct block_run *) get_block_run(pr, bs,
+ &ds->double_indirect, fs_le);
+ if (!br)
+ return NULL;
+
+ 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;
+}
+
+static int32_t compare_keys(const char keys1[], uint16_t keylengths1[], int32_t index,
+ const char *key2, uint16_t keylength2, int fs_le)
+{
+ const char *key1;
+ uint16_t keylength1;
+ int32_t result;
+
+ key1 = &keys1[index == 0 ? 0 : FS16_TO_CPU(keylengths1[index - 1],
+ fs_le)];
+ keylength1 = FS16_TO_CPU(keylengths1[index], fs_le)
+ - (index == 0 ? 0 : FS16_TO_CPU(keylengths1[index - 1],
+ fs_le));
+
+ result = strncmp(key1, key2, min(keylength1, keylength2));
+
+ if (result == 0)
+ return keylength1 - keylength2;
+
+ 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;
+ int32_t first, last, mid, cmp;
+
+ 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);
+
+ do {
+ errno = 0;
+ bn = (struct bplustree_node *) get_tree_node(pr, bs, &bi->data,
+ node_pointer, FS32_TO_CPU(bh->node_size, fs_le), fs_le);
+ if (!bn)
+ return errno ? -errno : -ENOENT;
+
+ keylengths = (uint16_t *) ((uint8_t *) bn
+ + ((sizeof(struct bplustree_node)
+ + FS16_TO_CPU(bn->all_key_length, fs_le)
+ + sizeof(int64_t) - 1)
+ & ~(sizeof(int64_t) - 1)));
+ values = (int64_t *) ((uint8_t *) keylengths
+ + FS16_TO_CPU(bn->all_key_count, fs_le)
+ * sizeof(uint16_t));
+ first = 0;
+ mid = 0;
+ last = FS16_TO_CPU(bn->all_key_count, fs_le) - 1;
+
+ cmp = compare_keys(bn->name, keylengths, last, key, strlen(key),
+ fs_le);
+ if (cmp == 0) {
+ if ((int64_t) FS64_TO_CPU(bn->overflow_link, fs_le)
+ == BPLUSTREE_NULL)
+ return FS64_TO_CPU(values[last], fs_le);
+ else
+ 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), fs_le);
+ if (cmp == 0) {
+ if ((int64_t) FS64_TO_CPU(bn->overflow_link,
+ fs_le) == BPLUSTREE_NULL)
+ return FS64_TO_CPU(values[mid],
+ fs_le);
+ else
+ break;
+ } else 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 ((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;
+
+ 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;
+
+ sd = (struct small_data *) bi->small_data;
+
+ do {
+ 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;
+ } else 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;
+
+ sd = (struct small_data *) ((uint8_t *) sd
+ + sizeof(struct small_data)
+ + FS16_TO_CPU(sd->name_size, fs_le) + 3
+ + FS16_TO_CPU(sd->data_size, fs_le) + 1);
+
+ } while ((intptr_t) sd < (intptr_t) bi
+ + (int32_t) FS32_TO_CPU(bi->inode_size, fs_le)
+ - (int32_t) sizeof(struct small_data));
+ 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;
+
+ else 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;
+ 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;
+
+ ret = get_uuid(pr, bs, &volume_id, fs_le);
+
+ if (ret != 0)
+ return ret;
+
+ /*
+ * all checks pass, set LABEL, VERSION and UUID
+ */
+ if (strlen(bs->name))
+ 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));
+ 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 000000000..8a34c583a
--- /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/btrfs.c b/libblkid/src/superblocks/btrfs.c
new file mode 100644
index 000000000..7ce3dfff8
--- /dev/null
+++ b/libblkid/src/superblocks/btrfs.c
@@ -0,0 +1,93 @@
+/*
+ * 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 "superblocks.h"
+
+struct btrfs_super_block {
+ uint8_t csum[32];
+ 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];
+} __attribute__ ((__packed__));
+
+static int probe_btrfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct btrfs_super_block *bfs;
+
+ bfs = blkid_probe_get_sb(pr, mag, struct btrfs_super_block);
+ if (!bfs)
+ return errno ? -errno : 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");
+
+ 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 },
+ { NULL }
+ }
+};
+
diff --git a/libblkid/src/superblocks/cramfs.c b/libblkid/src/superblocks/cramfs.c
new file mode 100644
index 000000000..6d01b0baa
--- /dev/null
+++ b/libblkid/src/superblocks/cramfs.c
@@ -0,0 +1,62 @@
+/*
+ * 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"
+
+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));
+
+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;
+
+ blkid_probe_set_label(pr, cs->name, sizeof(cs->name));
+ return 0;
+}
+
+const struct blkid_idinfo cramfs_idinfo =
+{
+ .name = "cramfs",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_cramfs,
+ .magics =
+ {
+ { "\x45\x3d\xcd\x28", 4, 0, 0 },
+ { "\x28\xcd\x3d\x45", 4, 0, 0 },
+ { NULL }
+ }
+};
+
+
diff --git a/libblkid/src/superblocks/ddf_raid.c b/libblkid/src/superblocks/ddf_raid.c
new file mode 100644
index 000000000..fc2c39d3e
--- /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, 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 000000000..e88e9f353
--- /dev/null
+++ b/libblkid/src/superblocks/drbd.c
@@ -0,0 +1,117 @@
+/*
+ * 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 only support v08 for now
+ */
+#define DRBD_MD_MAGIC_08 (DRBD_MAGIC+4)
+#define DRBD_MD_MAGIC_84_UNCLEAN (DRBD_MAGIC+5)
+
+/*
+ * 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. */
+};
+
+/*
+ * user/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];
+
+ char reserved[8 * 512 - (8*(UI_SIZE+3)+4*11)];
+};
+
+
+static int probe_drbd(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ struct md_on_disk_08 *md;
+ off_t off;
+
+ off = pr->size - sizeof(*md);
+
+ /* 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;
+}
+
+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/drbdproxy_datalog.c b/libblkid/src/superblocks/drbdproxy_datalog.c
new file mode 100644
index 000000000..af597224f
--- /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%jd", 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/exfat.c b/libblkid/src/superblocks/exfat.c
new file mode 100644
index 000000000..b52656036
--- /dev/null
+++ b/libblkid/src/superblocks/exfat.c
@@ -0,0 +1,148 @@
+/*
+ * 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 jump[3];
+ uint8_t oem_name[8];
+ uint8_t __unused1[53];
+ uint64_t block_start;
+ uint64_t block_count;
+ uint32_t fat_block_start;
+ uint32_t fat_block_count;
+ uint32_t cluster_block_start;
+ uint32_t cluster_count;
+ uint32_t rootdir_cluster;
+ uint8_t volume_serial[4];
+ struct {
+ uint8_t minor;
+ uint8_t major;
+ } version;
+ uint16_t volume_state;
+ uint8_t block_bits;
+ uint8_t bpc_bits;
+ uint8_t fat_count;
+ uint8_t drive_no;
+ uint8_t allocated_percent;
+} __attribute__((__packed__));
+
+struct exfat_entry_label {
+ uint8_t type;
+ uint8_t length;
+ uint8_t name[30];
+} __attribute__((__packed__));
+
+#define BLOCK_SIZE(sb) (1 << (sb)->block_bits)
+#define CLUSTER_SIZE(sb) (BLOCK_SIZE(sb) << (sb)->bpc_bits)
+#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
+
+static blkid_loff_t block_to_offset(const struct exfat_super_block *sb,
+ blkid_loff_t block)
+{
+ return (blkid_loff_t) block << sb->block_bits;
+}
+
+static blkid_loff_t cluster_to_block(const struct exfat_super_block *sb,
+ uint32_t cluster)
+{
+ return le32_to_cpu(sb->cluster_block_start) +
+ ((blkid_loff_t) (cluster - EXFAT_FIRST_DATA_CLUSTER)
+ << sb->bpc_bits);
+}
+
+static blkid_loff_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 *next;
+ blkid_loff_t fat_offset;
+
+ fat_offset = block_to_offset(sb, le32_to_cpu(sb->fat_block_start))
+ + (blkid_loff_t) cluster * sizeof(cluster);
+ next = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset,
+ sizeof(uint32_t));
+ if (!next)
+ return 0;
+ 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->rootdir_cluster);
+ blkid_loff_t offset = cluster_to_offset(sb, cluster);
+ uint8_t *entry;
+
+ for (;;) {
+ 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 (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);
+ }
+ }
+}
+
+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;
+
+ label = find_label(pr, sb);
+ if (label)
+ blkid_probe_set_utf8label(pr, label->name,
+ min(label->length * 2, 30), BLKID_ENC_UTF16LE);
+ else if (errno)
+ return -errno;
+
+ blkid_probe_sprintf_uuid(pr, sb->volume_serial, 4,
+ "%02hhX%02hhX-%02hhX%02hhX",
+ sb->volume_serial[3], sb->volume_serial[2],
+ sb->volume_serial[1], sb->volume_serial[0]);
+
+ blkid_probe_sprintf_version(pr, "%u.%u",
+ sb->version.major, sb->version.minor);
+
+ 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/ext.c b/libblkid/src/superblocks/ext.c
new file mode 100644
index 000000000..5b1d179f3
--- /dev/null
+++ b/libblkid/src/superblocks/ext.c
@@ -0,0 +1,365 @@
+/*
+ * 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"
+
+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[163];
+} __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
+
+/* 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, 0x200);
+ if (!es)
+ 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);
+
+ DBG(PROBE, ul_debug("ext2_sb.compat = %08X:%08X:%08X",
+ le32_to_cpu(es->s_feature_compat),
+ le32_to_cpu(es->s_feature_incompat),
+ le32_to_cpu(es->s_feature_ro_compat)));
+
+ if (strlen(es->s_volume_name))
+ 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) &&
+ ((le32_to_cpu(es->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));
+}
+
+
+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 000000000..2bf0f5e98
--- /dev/null
+++ b/libblkid/src/superblocks/f2fs.c
@@ -0,0 +1,99 @@
+/*
+ * 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"
+
+#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 probe_f2fs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct f2fs_super_block *sb;
+ uint16_t major, minor;
+
+ sb = blkid_probe_get_sb(pr, mag, struct f2fs_super_block);
+ if (!sb)
+ return errno ? -errno : 1;
+
+ major = le16_to_cpu(sb->major_ver);
+ minor = le16_to_cpu(sb->minor_ver);
+
+ /* For version 1.0 we cannot know the correct sb structure */
+ if (major == 1 && minor == 0)
+ return 0;
+
+ if (*((unsigned char *) sb->volume_name))
+ blkid_probe_set_utf8label(pr, (unsigned char *) sb->volume_name,
+ sizeof(sb->volume_name),
+ BLKID_ENC_UTF16LE);
+
+ blkid_probe_set_uuid(pr, sb->uuid);
+ blkid_probe_sprintf_version(pr, "%u.%u", major, minor);
+ 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 000000000..1b265581d
--- /dev/null
+++ b/libblkid/src/superblocks/gfs.c
@@ -0,0 +1,131 @@
+/*
+ * 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
+/* gfs2 constants: */
+#define GFS2_FORMAT_FS 1801
+#define GFS2_FORMAT_MULTI 1900
+
+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 licence 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 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 (be32_to_cpu(sbd->sb_fs_format) == GFS2_FORMAT_FS &&
+ be32_to_cpu(sbd->sb_multihost_format) == GFS2_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);
+ blkid_probe_set_version(pr, "1");
+ 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 000000000..fe57241c0
--- /dev/null
+++ b/libblkid/src/superblocks/hfs.c
@@ -0,0 +1,321 @@
+/*
+ * 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));
+
+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[MD5LENGTH] = {
+ 0xb3, 0xe2, 0x0f, 0x39, 0xf2, 0x92, 0x11, 0xd6,
+ 0x97, 0xa4, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac
+ };
+ unsigned char uuid[MD5LENGTH];
+ struct MD5Context md5c;
+
+ if (memcmp(hfs_info, "\0\0\0\0\0\0\0\0", len) == 0)
+ return -1;
+ MD5Init(&md5c);
+ MD5Update(&md5c, hash_init, MD5LENGTH);
+ MD5Update(&md5c, hfs_info, len);
+ 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;
+
+ 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+ */
+
+ hfs_set_uuid(pr, hfs->finder_info.id, sizeof(hfs->finder_info.id));
+
+ blkid_probe_set_label(pr, hfs->label, hfs->label_len);
+ 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;
+ 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;
+
+ memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
+ cat_block = be32_to_cpu(extents[0].start_block);
+
+ buf = blkid_probe_get_buffer(pr,
+ off + ((blkid_loff_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_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 = (ext_block_start + leaf_block) * blocksize;
+
+ buf = blkid_probe_get_buffer(pr,
+ (blkid_loff_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)
+ return 0;
+
+ blkid_probe_set_utf8label(pr, key->unicode,
+ be16_to_cpu(key->unicode_len) * 2,
+ BLKID_ENC_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 000000000..ad3b538cd
--- /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 = {
+ /*
+ * Superblok 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 000000000..0565d370c
--- /dev/null
+++ b/libblkid/src/superblocks/hpfs.c
@@ -0,0 +1,122 @@
+/*
+ * 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);
+
+ 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 000000000..d099467a2
--- /dev/null
+++ b/libblkid/src/superblocks/iso9660.c
@@ -0,0 +1,273 @@
+/*
+ * 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"
+
+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 unused1[222];
+ unsigned char publisher_id[128];
+ unsigned char unused2[128];
+ unsigned char application_id[128];
+ unsigned char unused3[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_OFFSET (ISO_SUPERBLOCK_OFFSET + ISO_SECTOR_SIZE)
+#define ISO_VD_BOOT_RECORD 0x0
+#define ISO_VD_SUPPLEMENTARY 0x2
+#define ISO_VD_END 0xff
+#define ISO_VD_MAX 16
+
+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));
+
+/* returns 1 if the begin of @ascii is equal to @utf16 string.
+ */
+static int ascii_eq_utf16be(unsigned char *ascii,
+ unsigned char *utf16, size_t len)
+{
+ size_t a, u;
+
+ for (a = 0, u = 0; u < len; a++, u += 2) {
+ if (utf16[u] != 0x0 || ascii[a] != utf16[u + 1])
+ return 0;
+ }
+ return 1;
+}
+
+/* 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_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;
+}
+
+/* iso9660 [+ Microsoft Joliet Extension] */
+int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct iso_volume_descriptor *iso;
+ unsigned char label[32];
+ int i;
+ int off;
+
+ if (strcmp(mag->magic, "CDROM") == 0)
+ return probe_iso9660_hsfs(pr, mag);
+
+ iso = blkid_probe_get_sb(pr, mag, struct iso_volume_descriptor);
+ if (!iso)
+ return errno ? -errno : 1;
+
+ memcpy(label, iso->volume_id, sizeof(label));
+
+ if (!is_str_empty(iso->system_id, sizeof(iso->system_id)))
+ blkid_probe_set_id_label(pr, "SYSTEM_ID",
+ iso->system_id, sizeof(iso->system_id));
+
+ if (!is_str_empty(iso->publisher_id, sizeof(iso->publisher_id)))
+ blkid_probe_set_id_label(pr, "PUBLISHER_ID",
+ iso->publisher_id, sizeof(iso->publisher_id));
+
+ if (!is_str_empty(iso->application_id, sizeof(iso->application_id)))
+ blkid_probe_set_id_label(pr, "APPLICATION_ID",
+ iso->application_id, sizeof(iso->application_id));
+
+ /* create an UUID using the modified/created date */
+ if (! probe_iso9660_set_uuid(pr, &iso->modified))
+ probe_iso9660_set_uuid(pr, &iso->created);
+
+ /* Joliet Extension and Boot Record */
+ off = ISO_VD_OFFSET;
+ for (i = 0; i < ISO_VD_MAX; i++) {
+ struct boot_record *boot= (struct boot_record *)
+ blkid_probe_get_buffer(pr,
+ off,
+ max(sizeof(struct boot_record),
+ sizeof(struct iso_volume_descriptor)));
+
+ if (boot == NULL || boot->vd_type == ISO_VD_END)
+ break;
+
+ if (boot->vd_type == ISO_VD_BOOT_RECORD) {
+ if (!is_str_empty(boot->boot_system_id,
+ sizeof(boot->boot_system_id)))
+ blkid_probe_set_id_label(pr, "BOOT_SYSTEM_ID",
+ boot->boot_system_id,
+ sizeof(boot->boot_system_id));
+ off += ISO_SECTOR_SIZE;
+ continue;
+ }
+
+ /* Not a Boot record, lets see if its supplemntary volume descriptor */
+ iso = (struct iso_volume_descriptor *) boot;
+
+ if (iso->vd_type != ISO_VD_SUPPLEMENTARY) {
+ off += ISO_SECTOR_SIZE;
+ continue;
+ }
+
+ if (memcmp(iso->escape_sequences, "%/@", 3) == 0 ||
+ memcmp(iso->escape_sequences, "%/C", 3) == 0 ||
+ memcmp(iso->escape_sequences, "%/E", 3) == 0) {
+
+ blkid_probe_set_version(pr, "Joliet Extension");
+
+ /* Is the Joliet (UTF16BE) label equal to the label in
+ * the PVD? If yes, use PVD label. The Jolied version
+ * of the label could be trimed (because UTF16..).
+ */
+ if (ascii_eq_utf16be(label, iso->volume_id, 32))
+ break;
+
+ blkid_probe_set_utf8label(pr,
+ iso->volume_id,
+ sizeof(iso->volume_id),
+ BLKID_ENC_UTF16BE);
+ goto has_label;
+ }
+ off += ISO_SECTOR_SIZE;
+ }
+
+ /* Joliet not found, let use standard iso label */
+ blkid_probe_set_label(pr, label, sizeof(label));
+
+has_label:
+ 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 },
+ { .magic = "CDROM", .len = 5, .kboff = 32, .sboff = 9 },
+ { NULL }
+ }
+};
+
diff --git a/libblkid/src/superblocks/iso9660.h b/libblkid/src/superblocks/iso9660.h
new file mode 100644
index 000000000..a8d729df6
--- /dev/null
+++ b/libblkid/src/superblocks/iso9660.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2013 Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef _BLKID_ISO9660_H
+#define _BLKID_ISO9660_H
+
+#include "blkidP.h"
+
+extern int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag);
+
+#endif /* _BLKID_ISO9660_H */
diff --git a/libblkid/src/superblocks/isw_raid.c b/libblkid/src/superblocks/isw_raid.c
new file mode 100644
index 000000000..065c2b208
--- /dev/null
+++ b/libblkid/src/superblocks/isw_raid.c
@@ -0,0 +1,66 @@
+/*
+ * 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;
+ struct isw_metadata *isw;
+
+ if (pr->size < 0x10000)
+ return 1;
+ if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+ return 1;
+
+ off = ((pr->size / 0x200) - 2) * 0x200;
+ 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 000000000..ac684d8cd
--- /dev/null
+++ b/libblkid/src/superblocks/jfs.c
@@ -0,0 +1,71 @@
+/*
+ * 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 (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 (strlen((char *) js->js_label))
+ blkid_probe_set_label(pr, js->js_label, sizeof(js->js_label));
+ blkid_probe_set_uuid(pr, js->js_uuid);
+ 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 000000000..ca7986733
--- /dev/null
+++ b/libblkid/src/superblocks/jmicron_raid.c
@@ -0,0 +1,65 @@
+/*
+ * 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 jm_metadata {
+ int8_t signature[2];
+ uint8_t minor_version;
+ uint8_t major_version;
+ uint16_t checksum;
+};
+
+#define JM_SIGNATURE "JM"
+
+static int probe_jmraid(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ uint64_t off;
+ 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 (blkid_probe_sprintf_version(pr, "%u.%u",
+ jm->major_version, jm->minor_version) != 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 000000000..cf29141c6
--- /dev/null
+++ b/libblkid/src/superblocks/linux_raid.c
@@ -0,0 +1,267 @@
+/*
+ * 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 btes 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, blkid_loff_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 < 0 || (uint64_t) pr->size < size + MD_RESERVED_BYTES)
+ /* device is too small */
+ return 1;
+
+ if (off < 0 || (uint64_t) 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 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 (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;
+}
+
+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 000000000..697b0fe89
--- /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 000000000..00696f28c
--- /dev/null
+++ b/libblkid/src/superblocks/luks.c
@@ -0,0 +1,66 @@
+/*
+ * 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"
+
+#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
+
+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));
+
+static int probe_luks(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct luks_phdr *header;
+
+ header = blkid_probe_get_sb(pr, mag, struct luks_phdr);
+ if (header == NULL)
+ return errno ? -errno : 1;
+
+ blkid_probe_strncpy_uuid(pr, (unsigned char *) header->uuid,
+ sizeof(header->uuid));
+ blkid_probe_sprintf_version(pr, "%u", be16_to_cpu(header->version));
+ return 0;
+}
+
+const struct blkid_idinfo luks_idinfo =
+{
+ .name = "crypto_LUKS",
+ .usage = BLKID_USAGE_CRYPTO,
+ .probefunc = probe_luks,
+ .magics =
+ {
+ { .magic = "LUKS\xba\xbe", .len = 6 },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/lvm.c b/libblkid/src/superblocks/lvm.c
new file mode 100644
index 000000000..3c6df743d
--- /dev/null
+++ b/libblkid/src/superblocks/lvm.c
@@ -0,0 +1,226 @@
+/*
+ * 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 <mbroz@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"
+
+#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 bettween 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;
+}
+
+/* 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 }
+ }
+};
diff --git a/libblkid/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c
new file mode 100644
index 000000000..9ea49fee8
--- /dev/null
+++ b/libblkid/src/superblocks/minix.c
@@ -0,0 +1,161 @@
+/*
+ * 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)
+{
+ struct minix_super_block *sb = (struct minix_super_block *) data;
+ struct minix3_super_block *sb3 = (struct minix3_super_block *) data;
+ int version = 0;
+
+ *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;
+
+ DBG(LOWPROBE, ul_debug("minix version %d detected [%s]", version,
+#if defined(WORDS_BIGENDIAN)
+ *other_endian ? "LE" : "BE"
+#else
+ *other_endian ? "BE" : "LE"
+#endif
+ ));
+ return version;
+}
+
+static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ unsigned char *ext;
+ const unsigned char *data;
+ int version = 0, swabme = 0;
+
+ 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);
+ if (version < 1)
+ return 1;
+
+ if (version <= 2) {
+ struct minix_super_block *sb = (struct minix_super_block *) data;
+ int zones, ninodes, imaps, zmaps, firstz;
+
+ if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
+ 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);
+
+ /* sanity checks to be sure that the FS is really minix */
+ if (imaps * MINIX_BLOCK_SIZE * 8 < ninodes + 1)
+ return 1;
+ if (zmaps * MINIX_BLOCK_SIZE * 8 < zones - firstz + 1)
+ return 1;
+
+ } else if (version == 3) {
+ struct minix3_super_block *sb = (struct minix3_super_block *) data;
+
+ if (sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
+ 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;
+ else if (memcmp(ext, "\123\357", 2) == 0)
+ return 1;
+
+ blkid_probe_sprintf_version(pr, "%d", version);
+ 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/netware.c b/libblkid/src/superblocks/netware.c
new file mode 100644
index 000000000..af81cf5fd
--- /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 000000000..3f0390126
--- /dev/null
+++ b/libblkid/src/superblocks/nilfs.c
@@ -0,0 +1,138 @@
+/*
+ * 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];
+};
+
+#define NILFS_SB_MAGIC 0x3434
+#define NILFS_SB_OFFSET 0x400
+
+static int nilfs_valid_sb(blkid_probe pr, struct nilfs_super_block *sb)
+{
+ static unsigned char sum[4];
+ const int sumoff = offsetof(struct nilfs_super_block, s_sum);
+ size_t bytes;
+ uint32_t crc;
+
+ if (!sb || le16_to_cpu(sb->s_magic) != NILFS_SB_MAGIC)
+ return 0;
+
+ bytes = le16_to_cpu(sb->s_bytes);
+ crc = crc32(le32_to_cpu(sb->s_crc_seed), (unsigned char *)sb, sumoff);
+ crc = crc32(crc, sum, 4);
+ crc = crc32(crc, (unsigned char *)sb + sumoff + 4, bytes - sumoff - 4);
+
+ 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)
+{
+ struct nilfs_super_block *sb, *sbp, *sbb;
+ int valid[2], swp = 0;
+
+ /* 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;
+ /* backup */
+ sbb = (struct nilfs_super_block *) blkid_probe_get_buffer(
+ pr, ((pr->size / 0x200) - 8) * 0x200, sizeof(struct nilfs_super_block));
+ if (!sbb)
+ return errno ? -errno : 1;
+
+ /*
+ * Compare two super blocks and set 1 in swp if the secondary
+ * super block is valid and newer. Otherwise, set 0 in swp.
+ */
+ valid[0] = nilfs_valid_sb(pr, sbp);
+ valid[1] = nilfs_valid_sb(pr, sbb);
+ 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 (strlen(sb->s_volume_name))
+ 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));
+
+ 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 000000000..8ff9ccda1
--- /dev/null
+++ b/libblkid/src/superblocks/ntfs.c
@@ -0,0 +1,224 @@
+/*
+ * 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
+#define NTFS_MAX_CLUSTER_SIZE (64 * 1024)
+
+enum {
+ MFT_RECORD_ATTR_VOLUME_NAME = 0x60,
+ MFT_RECORD_ATTR_END = 0xffffffff
+};
+
+static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct ntfs_super_block *ns;
+ struct master_file_table_record *mft;
+
+ uint32_t sectors_per_cluster, mft_record_size, attr_off;
+ uint16_t sector_size;
+ uint64_t nr_clusters, 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);
+ sectors_per_cluster = ns->bpb.sectors_per_cluster;
+
+ if (sector_size < 256 || sector_size > 4096)
+ return 1;
+
+ switch (sectors_per_cluster) {
+ case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
+ break;
+ default:
+ return 1;
+ }
+
+ if ((uint16_t) le16_to_cpu(ns->bpb.sector_size) *
+ ns->bpb.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
+ mft_record_size = 1 << (0 - ns->clusters_per_mft_record);
+
+ 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=%d, mft_record_size=%d, "
+ "sectors_per_cluster=%d, nr_clusters=%ju "
+ "cluster_offset=%jd",
+ (int) sector_size, mft_record_size,
+ sectors_per_cluster, nr_clusters,
+ off));
+
+ buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
+ if (!buf_mft)
+ return errno ? -errno : 1;
+
+ if (memcmp(buf_mft, "FILE", 4))
+ 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))
+ return 1;
+
+ mft = (struct master_file_table_record *) buf_mft;
+ attr_off = le16_to_cpu(mft->attrs_offset);
+
+ while (attr_off < 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) == MFT_RECORD_ATTR_END)
+ break;
+ if (le32_to_cpu(attr->type) == 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;
+
+ blkid_probe_set_utf8label(pr, val, val_len, BLKID_ENC_UTF16LE);
+ break;
+ }
+
+ if (UINT_MAX - attr_len < attr_off)
+ break;
+ attr_off += attr_len;
+ }
+
+ blkid_probe_sprintf_uuid(pr,
+ (unsigned char *) &ns->volume_serial,
+ sizeof(ns->volume_serial),
+ "%016" PRIX64, le64_to_cpu(ns->volume_serial));
+ return 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 000000000..5db8ec260
--- /dev/null
+++ b/libblkid/src/superblocks/nvidia_raid.c
@@ -0,0 +1,64 @@
+/*
+ * 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"
+
+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,
+ sizeof(struct nv_metadata));
+ if (!nv)
+ return errno ? -errno : 1;
+
+ if (memcmp(nv->vendor, NVIDIA_SIGNATURE, sizeof(NVIDIA_SIGNATURE)-1) != 0)
+ 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 000000000..3fe199d3f
--- /dev/null
+++ b/libblkid/src/superblocks/ocfs.c
@@ -0,0 +1,213 @@
+/*
+ * 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"));
+
+ blkid_probe_set_label(pr, (unsigned char *) ovl.label,
+ ocfslabellen(ovl));
+ 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));
+
+ 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 000000000..678460a43
--- /dev/null
+++ b/libblkid/src/superblocks/promise_raid.c
@@ -0,0 +1,71 @@
+/*
+ * 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__)))
+{
+ unsigned int i;
+ static unsigned int sectors[] = {
+ 63, 255, 256, 16, 399, 591, 675, 735, 911, 974, 991, 951, 3087, 0
+ };
+
+ if (pr->size < 0x40000)
+ return 1;
+ if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+ return 1;
+
+ for (i = 0; sectors[i] != 0; i++) {
+ uint64_t off;
+ struct promise_metadata *pdc;
+
+ off = ((pr->size / 0x200) - sectors[i]) * 0x200;
+ 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 000000000..ea81f208c
--- /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 000000000..edbaaa946
--- /dev/null
+++ b/libblkid/src/superblocks/reiserfs.c
@@ -0,0 +1,128 @@
+/*
+ * 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];
+ uint16_t rs4_dummy[2];
+ 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");
+
+ return 0;
+}
+
+static int probe_reiser4(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct reiser4_super_block *rs4;
+
+ rs4 = blkid_probe_get_sb(pr, mag, struct reiser4_super_block);
+ if (!rs4)
+ return errno ? -errno : 1;
+
+ 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");
+
+ 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 000000000..8e63c100d
--- /dev/null
+++ b/libblkid/src/superblocks/romfs.c
@@ -0,0 +1,51 @@
+/*
+ * 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_dummy1[2];
+ unsigned char ros_volume[16];
+} __attribute__((packed));
+
+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 (strlen((char *) ros->ros_volume))
+ blkid_probe_set_label(pr, ros->ros_volume,
+ sizeof(ros->ros_volume));
+ 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 000000000..edbefbc09
--- /dev/null
+++ b/libblkid/src/superblocks/silicon_raid.c
@@ -0,0 +1,130 @@
+/*
+ * 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;
+ uint16_t *p = (uint16_t *) sil;
+
+ while (count--) {
+ uint16_t x = *p++;
+ sum += le16_to_cpu(x);
+ }
+
+ 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 000000000..8ed28385f
--- /dev/null
+++ b/libblkid/src/superblocks/squashfs.c
@@ -0,0 +1,101 @@
+/*
+ * 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 s_magic;
+ uint32_t inodes;
+ uint32_t bytes_used_2;
+ uint32_t uid_start_2;
+ uint32_t guid_start_2;
+ uint32_t inode_table_start_2;
+ uint32_t directory_table_start_2;
+ uint16_t s_major;
+ uint16_t s_minor;
+} __attribute__((packed));
+
+static int probe_squashfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct sqsh_super_block *sq;
+ uint16_t major;
+ uint16_t minor;
+
+ sq = blkid_probe_get_sb(pr, mag, struct sqsh_super_block);
+ if (!sq)
+ return errno ? -errno : 1;
+
+ major = le16_to_cpu(sq->s_major);
+ minor = le16_to_cpu(sq->s_minor);
+ if (major < 4)
+ return 1;
+
+ blkid_probe_sprintf_version(pr, "%u.%u", major, minor);
+
+ return 0;
+}
+
+static int probe_squashfs3(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct sqsh_super_block *sq;
+ uint16_t major;
+ uint16_t minor;
+
+ sq = blkid_probe_get_sb(pr, mag, struct sqsh_super_block);
+ if (!sq)
+ return errno ? -errno : 1;
+
+ if (strcmp(mag->magic, "sqsh") == 0) {
+ major = be16_to_cpu(sq->s_major);
+ minor = be16_to_cpu(sq->s_minor);
+ } else {
+ major = le16_to_cpu(sq->s_major);
+ minor = le16_to_cpu(sq->s_minor);
+ }
+
+ if (major > 3)
+ return 1;
+
+ blkid_probe_sprintf_version(pr, "%u.%u", major, minor);
+
+ 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/superblocks.c b/libblkid/src/superblocks/superblocks.c
new file mode 100644
index 000000000..80bd6e596
--- /dev/null
+++ b/libblkid/src/superblocks/superblocks.c
@@ -0,0 +1,813 @@
+/*
+ * 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 <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 filessystem [not-implemented yet]
+ *
+ * @SYSTEM_ID: ISO9660 system identifier
+ *
+ * @PUBLISHER_ID: ISO9660 publisher identifier
+ *
+ * @APPLICATION_ID: ISO9660 application identifier
+ *
+ * @BOOT_SYSTEM_ID: ISO9660 boot system identifier
+ */
+
+static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn);
+static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn);
+
+static int blkid_probe_set_usage(blkid_probe pr, int usage);
+
+
+/*
+ * Superblocks chains probing functions
+ */
+static const struct blkid_idinfo *idinfos[] =
+{
+ /* RAIDs */
+ &linuxraid_idinfo,
+ &ddfraid_idinfo,
+ &iswraid_idinfo,
+ &lsiraid_idinfo,
+ &viaraid_idinfo,
+ &silraid_idinfo,
+ &nvraid_idinfo,
+ &pdcraid_idinfo,
+ &highpoint45x_idinfo,
+ &highpoint37x_idinfo,
+ &adraid_idinfo,
+ &jmraid_idinfo,
+
+ &bcache_idinfo,
+ &drbd_idinfo,
+ &drbdproxy_datalog_idinfo,
+ &lvm2_idinfo,
+ &lvm1_idinfo,
+ &snapcow_idinfo,
+ &verity_hash_idinfo,
+ &luks_idinfo,
+ &vmfs_volume_idinfo,
+
+ /* Filesystems */
+ &vfat_idinfo,
+ &swsuspend_idinfo,
+ &swap_idinfo,
+ &xfs_idinfo,
+ &xfs_log_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
+};
+
+/*
+ * Driver definition
+ */
+const struct blkid_chaindrv superblocks_drv = {
+ .id = BLKID_CHAIN_SUBLKS,
+ .name = "superblocks",
+ .dflt_enabled = TRUE,
+ .dflt_flags = BLKID_SUBLKS_DEFAULT,
+ .idinfos = idinfos,
+ .nidinfos = ARRAY_SIZE(idinfos),
+ .has_fltr = TRUE,
+ .probe = superblocks_probe,
+ .safeprobe = superblocks_safeprobe,
+};
+
+/**
+ * blkid_probe_enable_superblocks:
+ * @pr: probe
+ * @enable: TRUE/FALSE
+ *
+ * Enables/disables the superblocks probing for non-binary interface.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_enable_superblocks(blkid_probe pr, int enable)
+{
+ if (!pr)
+ return -1;
+ pr->chains[BLKID_CHAIN_SUBLKS].enabled = enable;
+ return 0;
+}
+
+/**
+ * blkid_probe_set_superblocks_flags:
+ * @pr: prober
+ * @flags: BLKID_SUBLKS_* flags
+ *
+ * Sets probing flags to the superblocks prober. This function is optional, the
+ * default are BLKID_SUBLKS_DEFAULTS flags.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_set_superblocks_flags(blkid_probe pr, int flags)
+{
+ if (!pr)
+ return -1;
+
+ pr->chains[BLKID_CHAIN_SUBLKS].flags = flags;
+ return 0;
+}
+
+/**
+ * blkid_probe_reset_superblocks_filter:
+ * @pr: prober
+ *
+ * Resets superblocks probing filter
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_reset_superblocks_filter(blkid_probe pr)
+{
+ return __blkid_probe_reset_filter(pr, BLKID_CHAIN_SUBLKS);
+}
+
+/**
+ * blkid_probe_invert_superblocks_filter:
+ * @pr: prober
+ *
+ * Inverts superblocks probing filter
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_invert_superblocks_filter(blkid_probe pr)
+{
+ return __blkid_probe_invert_filter(pr, BLKID_CHAIN_SUBLKS);
+}
+
+/**
+ * blkid_probe_filter_superblocks_type:
+ * @pr: prober
+ * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
+ * @names: NULL terminated array of probing function names (e.g. "vfat").
+ *
+ * %BLKID_FLTR_NOTIN - probe for all items which are NOT IN @names;
+ *
+ * %BLKID_FLTR_ONLYIN - probe for items which are IN @names
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_filter_superblocks_type(blkid_probe pr, int flag, char *names[])
+{
+ return __blkid_probe_filter_types(pr, BLKID_CHAIN_SUBLKS, flag, names);
+}
+
+/**
+ * blkid_probe_filter_superblocks_usage:
+ * @pr: prober
+ * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
+ * @usage: BLKID_USAGE_* flags
+ *
+ * %BLKID_FLTR_NOTIN - probe for all items which are NOT IN @usage;
+ *
+ * %BLKID_FLTR_ONLYIN - probe for items which are IN @usage
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_filter_superblocks_usage(blkid_probe pr, int flag, int usage)
+{
+ unsigned long *fltr;
+ struct blkid_chain *chn;
+ size_t i;
+
+ fltr = blkid_probe_get_filter(pr, BLKID_CHAIN_SUBLKS, TRUE);
+ if (!fltr)
+ return -1;
+
+ chn = &pr->chains[BLKID_CHAIN_SUBLKS];
+
+ for (i = 0; i < chn->driver->nidinfos; i++) {
+ const struct blkid_idinfo *id = chn->driver->idinfos[i];
+
+ if (id->usage & usage) {
+ if (flag & BLKID_FLTR_NOTIN)
+ blkid_bmp_set_item(chn->fltr, i);
+ } else if (flag & BLKID_FLTR_ONLYIN)
+ blkid_bmp_set_item(chn->fltr, i);
+ }
+ DBG(LOWPROBE, ul_debug("a new probing usage-filter initialized"));
+ return 0;
+}
+
+/**
+ * blkid_known_fstype:
+ * @fstype: filesystem name
+ *
+ * Returns: 1 for known filesytems, or 0 for unknown filesystem.
+ */
+int blkid_known_fstype(const char *fstype)
+{
+ size_t i;
+
+ if (!fstype)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(idinfos); i++) {
+ const struct blkid_idinfo *id = idinfos[i];
+ if (strcmp(id->name, fstype) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * blkid_superblocks_get_name:
+ * @idx: number >= 0
+ * @name: returns name of supported filesystem/raid (optional)
+ * @usage: returns BLKID_USAGE_* flags, (optional)
+ *
+ * Returns: -1 if @idx is out of range, or 0 on success.
+ */
+int blkid_superblocks_get_name(size_t idx, const char **name, int *usage)
+{
+ if (idx < ARRAY_SIZE(idinfos)) {
+ if (name)
+ *name = idinfos[idx]->name;
+ if (usage)
+ *usage = idinfos[idx]->usage;
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ * The blkid_do_probe() backend.
+ */
+static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn)
+{
+ size_t i;
+ int rc = BLKID_PROBE_NONE;
+
+ if (!pr || chn->idx < -1)
+ return -EINVAL;
+
+ blkid_probe_chain_reset_vals(pr, chn);
+
+ if (pr->flags & BLKID_FL_NOSCAN_DEV)
+ 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 */
+ 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;
+ blkid_loff_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 && id->minsz > pr->size) {
+ rc = BLKID_PROBE_NONE;
+ continue; /* the device is too small */
+ }
+
+ /* don't probe for RAIDs, swap or journal on CD/DVDs */
+ if ((id->usage & (BLKID_USAGE_RAID | BLKID_USAGE_OTHER)) &&
+ blkid_probe_is_cdrom(pr)) {
+ rc = BLKID_PROBE_NONE;
+ continue;
+ }
+
+ /* don't probe for RAIDs on floppies */
+ if ((id->usage & BLKID_USAGE_RAID) && blkid_probe_is_tiny(pr)) {
+ rc = BLKID_PROBE_NONE;
+ continue;
+ }
+
+ DBG(LOWPROBE, ul_debug("[%zd] %s:", i, id->name));
+
+ rc = blkid_probe_get_idmag(pr, id, &off, &mag);
+ if (rc < 0)
+ break;
+ if (rc != BLKID_PROBE_OK)
+ continue;
+
+ /* final check by probing function */
+ if (id->probefunc) {
+ DBG(LOWPROBE, ul_debug("\tcall probefunc()"));
+ rc = id->probefunc(pr, mag);
+ if (rc != BLKID_PROBE_OK) {
+ blkid_probe_chain_reset_vals(pr, chn);
+ if (rc < 0)
+ break;
+ continue;
+ }
+ }
+
+ /* all cheks passed */
+ if (chn->flags & BLKID_SUBLKS_TYPE)
+ rc = blkid_probe_set_value(pr, "TYPE",
+ (unsigned char *) id->name,
+ strlen(id->name) + 1);
+
+ if (!rc)
+ rc = blkid_probe_set_usage(pr, id->usage);
+
+ if (!rc && mag)
+ rc = blkid_probe_set_magic(pr, off, mag->len,
+ (unsigned char *) mag->magic);
+ if (rc) {
+ blkid_probe_chain_reset_vals(pr, chn);
+ DBG(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 ambivalen results (more
+ * filesystems on the device) -- in such case returns -2.
+ *
+ * The function does not check for filesystems when a RAID or crypto signature
+ * is detected. The function also does not check for collision between RAIDs
+ * and crypto devices. The first detected RAID or crypto device is returned.
+ *
+ * The function does not probe for ambivalent results on very small devices
+ * (e.g. floppies), on small devices the first detected filesystem is returned.
+ */
+static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn)
+{
+ struct blkid_prval vals[BLKID_NVALS_SUBLKS];
+ int nvals = BLKID_NVALS_SUBLKS;
+ int idx = -1;
+ int count = 0;
+ int intol = 0;
+ int rc;
+
+ 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 */
+ nvals = blkid_probe_chain_copy_vals(pr, chn, vals, nvals);
+ idx = chn->idx;
+ }
+ }
+
+ if (rc < 0)
+ return rc; /* error */
+
+ if (count > 1 && intol) {
+ DBG(LOWPROBE, ul_debug("ERROR: superblocks chain: "
+ "ambivalent result detected (%d filesystems)!",
+ count));
+ return -2; /* error, ambivalent result (more FS) */
+ }
+ if (!count)
+ return BLKID_PROBE_NONE;
+
+ if (idx != -1) {
+ /* restore the first result */
+ blkid_probe_chain_reset_vals(pr, chn);
+ blkid_probe_append_vals(pr, vals, nvals);
+ chn->idx = idx;
+ }
+
+ /*
+ * The RAID device could be partitioned. The problem are RAID1 devices
+ * where the partition table is visible from underlaying devices. We
+ * have to ignore such partition tables.
+ */
+ if (chn->idx >= 0 && idinfos[chn->idx]->usage & BLKID_USAGE_RAID)
+ pr->prob_flags |= BLKID_PROBE_FL_IGNORE_PT;
+
+ return BLKID_PROBE_OK;
+}
+
+int blkid_probe_set_version(blkid_probe pr, const char *version)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+ if (chn->flags & BLKID_SUBLKS_VERSION)
+ return blkid_probe_set_value(pr, "VERSION",
+ (unsigned char *) version, strlen(version) + 1);
+ return 0;
+}
+
+
+int blkid_probe_sprintf_version(blkid_probe pr, const char *fmt, ...)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ int rc = 0;
+
+ if (chn->flags & BLKID_SUBLKS_VERSION) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ rc = blkid_probe_vsprintf_value(pr, "VERSION", fmt, ap);
+ va_end(ap);
+ }
+ return rc;
+}
+
+static int blkid_probe_set_usage(blkid_probe pr, int usage)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ char *u = NULL;
+
+ if (!(chn->flags & BLKID_SUBLKS_USAGE))
+ return 0;
+
+ if (usage & BLKID_USAGE_FILESYSTEM)
+ u = "filesystem";
+ else if (usage & BLKID_USAGE_RAID)
+ u = "raid";
+ else if (usage & BLKID_USAGE_CRYPTO)
+ u = "crypto";
+ else if (usage & BLKID_USAGE_OTHER)
+ u = "other";
+ else
+ u = "unknown";
+
+ return blkid_probe_set_value(pr, "USAGE", (unsigned char *) u, strlen(u) + 1);
+}
+
+int blkid_probe_set_id_label(blkid_probe pr, const char *name,
+ unsigned char *data, size_t len)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ struct blkid_prval *v;
+
+ if (!(chn->flags & BLKID_SUBLKS_LABEL))
+ return 0;
+
+ v = blkid_probe_assign_value(pr, name);
+ if (!v)
+ return -1;
+
+ if (len >= BLKID_PROBVAL_BUFSIZ)
+ len = BLKID_PROBVAL_BUFSIZ - 1; /* make a space for \0 */
+
+ memcpy(v->data, data, len);
+ v->data[len] = '\0';
+
+ /* remove white spaces */
+ v->len = blkid_rtrim_whitespace(v->data) + 1;
+ if (v->len > 1)
+ v->len = blkid_ltrim_whitespace(v->data) + 1;
+
+ if (v->len <= 1)
+ blkid_probe_reset_last_value(pr); /* ignore empty */
+ return 0;
+}
+
+int blkid_probe_set_label(blkid_probe pr, unsigned char *label, size_t len)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ struct blkid_prval *v;
+ if (len > BLKID_PROBVAL_BUFSIZ)
+ len = BLKID_PROBVAL_BUFSIZ;
+
+ if ((chn->flags & BLKID_SUBLKS_LABELRAW) &&
+ blkid_probe_set_value(pr, "LABEL_RAW", label, len) < 0)
+ return -1;
+ if (!(chn->flags & BLKID_SUBLKS_LABEL))
+ return 0;
+ v = blkid_probe_assign_value(pr, "LABEL");
+ if (!v)
+ return -1;
+
+ if (len == BLKID_PROBVAL_BUFSIZ)
+ len--; /* make a space for \0 */
+
+ memcpy(v->data, label, len);
+ v->data[len] = '\0';
+
+ v->len = blkid_rtrim_whitespace(v->data) + 1;
+ if (v->len == 1)
+ blkid_probe_reset_last_value(pr);
+ return 0;
+}
+
+int blkid_probe_set_utf8label(blkid_probe pr, unsigned char *label,
+ size_t len, int enc)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ struct blkid_prval *v;
+
+ if ((chn->flags & BLKID_SUBLKS_LABELRAW) &&
+ blkid_probe_set_value(pr, "LABEL_RAW", label, len) < 0)
+ return -1;
+ if (!(chn->flags & BLKID_SUBLKS_LABEL))
+ return 0;
+ v = blkid_probe_assign_value(pr, "LABEL");
+ if (!v)
+ return -1;
+
+ blkid_encode_to_utf8(enc, v->data, sizeof(v->data), label, len);
+ v->len = blkid_rtrim_whitespace(v->data) + 1;
+ if (v->len == 1)
+ blkid_probe_reset_last_value(pr);
+ return 0;
+}
+
+int blkid_probe_sprintf_uuid(blkid_probe pr, unsigned char *uuid,
+ size_t len, const char *fmt, ...)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ int rc = -1;
+ va_list ap;
+
+ if (len > BLKID_PROBVAL_BUFSIZ)
+ len = BLKID_PROBVAL_BUFSIZ;
+
+ if (blkid_uuid_is_empty(uuid, len))
+ return 0;
+
+ if ((chn->flags & BLKID_SUBLKS_UUIDRAW) &&
+ blkid_probe_set_value(pr, "UUID_RAW", uuid, len) < 0)
+ return -1;
+ if (!(chn->flags & BLKID_SUBLKS_UUID))
+ return 0;
+
+ va_start(ap, fmt);
+ rc = blkid_probe_vsprintf_value(pr, "UUID", fmt, ap);
+ va_end(ap);
+
+ /* convert to lower case (..be paranoid) */
+ if (!rc) {
+ size_t i;
+ struct blkid_prval *v = __blkid_probe_get_value(pr,
+ blkid_probe_numof_values(pr));
+ if (v) {
+ for (i = 0; i < v->len; i++)
+ if (v->data[i] >= 'A' && v->data[i] <= 'F')
+ v->data[i] = (v->data[i] - 'A') + 'a';
+ }
+ }
+ return rc;
+}
+
+/* function to set UUIDs that are in suberblocks stored as strings */
+int blkid_probe_strncpy_uuid(blkid_probe pr, unsigned char *str, size_t len)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ struct blkid_prval *v;
+
+ if (str == NULL || *str == '\0')
+ return -1;
+ if (!len)
+ len = strlen((char *) str);
+ if (len > BLKID_PROBVAL_BUFSIZ)
+ len = BLKID_PROBVAL_BUFSIZ;
+
+ if ((chn->flags & BLKID_SUBLKS_UUIDRAW) &&
+ blkid_probe_set_value(pr, "UUID_RAW", str, len) < 0)
+ return -1;
+ if (!(chn->flags & BLKID_SUBLKS_UUID))
+ return 0;
+
+ v = blkid_probe_assign_value(pr, "UUID");
+ if (v) {
+ if (len == BLKID_PROBVAL_BUFSIZ)
+ len--; /* make a space for \0 */
+
+ memcpy((char *) v->data, str, len);
+ v->data[len] = '\0';
+ v->len = len + 1;
+ return 0;
+ }
+ return -1;
+}
+
+/* default _set_uuid function to set DCE UUIDs */
+int blkid_probe_set_uuid_as(blkid_probe pr, unsigned char *uuid, const char *name)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ struct blkid_prval *v;
+
+ if (blkid_uuid_is_empty(uuid, 16))
+ return 0;
+
+ if (!name) {
+ if ((chn->flags & BLKID_SUBLKS_UUIDRAW) &&
+ blkid_probe_set_value(pr, "UUID_RAW", uuid, 16) < 0)
+ return -1;
+ if (!(chn->flags & BLKID_SUBLKS_UUID))
+ return 0;
+
+ v = blkid_probe_assign_value(pr, "UUID");
+ } else
+ v = blkid_probe_assign_value(pr, name);
+
+ blkid_unparse_uuid(uuid, (char *) v->data, sizeof(v->data));
+ v->len = 37;
+
+ return 0;
+}
+
+int blkid_probe_set_uuid(blkid_probe pr, unsigned char *uuid)
+{
+ return blkid_probe_set_uuid_as(pr, uuid, NULL);
+}
+
+/**
+ * blkid_probe_set_request:
+ * @pr: probe
+ * @flags: BLKID_PROBREQ_* (deprecated) or BLKID_SUBLKS_* flags
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_set_superblocks_flags().
+ */
+int blkid_probe_set_request(blkid_probe pr, int flags)
+{
+ return blkid_probe_set_superblocks_flags(pr, flags);
+}
+
+/**
+ * blkid_probe_reset_filter:
+ * @pr: prober
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_reset_superblocks_filter().
+ */
+int blkid_probe_reset_filter(blkid_probe pr)
+{
+ return __blkid_probe_reset_filter(pr, BLKID_CHAIN_SUBLKS);
+}
+
+/**
+ * blkid_probe_invert_filter:
+ * @pr: prober
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_invert_superblocks_filter().
+ */
+int blkid_probe_invert_filter(blkid_probe pr)
+{
+ return __blkid_probe_invert_filter(pr, BLKID_CHAIN_SUBLKS);
+}
+
+/**
+ * blkid_probe_filter_types
+ * @pr: prober
+ * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
+ * @names: NULL terminated array of probing function names (e.g. "vfat").
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_filter_superblocks_types().
+ */
+int blkid_probe_filter_types(blkid_probe pr, int flag, char *names[])
+{
+ return __blkid_probe_filter_types(pr, BLKID_CHAIN_SUBLKS, flag, names);
+}
+
+/**
+ * blkid_probe_filter_usage
+ * @pr: prober
+ * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
+ * @usage: BLKID_USAGE_* flags
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_filter_superblocks_usage().
+ */
+int blkid_probe_filter_usage(blkid_probe pr, int flag, int usage)
+{
+ return blkid_probe_filter_superblocks_usage(pr, flag, usage);
+}
+
+
diff --git a/libblkid/src/superblocks/superblocks.h b/libblkid/src/superblocks/superblocks.h
new file mode 100644
index 000000000..3bbfb9c19
--- /dev/null
+++ b/libblkid/src/superblocks/superblocks.h
@@ -0,0 +1,99 @@
+/*
+ * 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"
+
+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 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 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 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 drbd_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;
+
+/*
+ * 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, unsigned char *label, size_t len);
+extern int blkid_probe_set_utf8label(blkid_probe pr, unsigned char *label,
+ size_t len, int enc);
+extern int blkid_probe_sprintf_uuid(blkid_probe pr, unsigned char *uuid,
+ size_t len, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 4, 5)));
+extern int blkid_probe_strncpy_uuid(blkid_probe pr, unsigned char *str, size_t len);
+
+extern int blkid_probe_set_uuid(blkid_probe pr, unsigned char *uuid);
+extern int blkid_probe_set_uuid_as(blkid_probe pr, unsigned char *uuid, const char *name);
+
+extern int blkid_probe_set_id_label(blkid_probe pr, const char *name,
+ unsigned char *data, size_t len);
+
+#endif /* _BLKID_SUPERBLOCKS_H */
diff --git a/libblkid/src/superblocks/swap.c b/libblkid/src/superblocks/swap.c
new file mode 100644
index 000000000..3f21391c8
--- /dev/null
+++ b/libblkid/src/superblocks/swap.c
@@ -0,0 +1,178 @@
+/*
+ * 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 int swap_set_info(blkid_probe pr, 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;
+ }
+ }
+
+ /* 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;
+
+ } else if (!memcmp(mag->magic, "SWAPSPACE2", mag->len))
+ return swap_set_info(pr, "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, "s1suspend");
+ if (!memcmp(mag->magic, "S2SUSPEND", mag->len))
+ return swap_set_info(pr, "s2suspend");
+ if (!memcmp(mag->magic, "ULSUSPEND", mag->len))
+ return swap_set_info(pr, "ulsuspend");
+ if (!memcmp(mag->magic, TOI_MAGIC_STRING, mag->len))
+ return swap_set_info(pr, "tuxonice");
+ if (!memcmp(mag->magic, "LINHIB0001", mag->len))
+ return swap_set_info(pr, "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 =
+ {
+ { "SWAP-SPACE", 10, 0, 0xff6 },
+ { "SWAPSPACE2", 10, 0, 0xff6 },
+ { "SWAP-SPACE", 10, 0, 0x1ff6 },
+ { "SWAPSPACE2", 10, 0, 0x1ff6 },
+ { "SWAP-SPACE", 10, 0, 0x3ff6 },
+ { "SWAPSPACE2", 10, 0, 0x3ff6 },
+ { "SWAP-SPACE", 10, 0, 0x7ff6 },
+ { "SWAPSPACE2", 10, 0, 0x7ff6 },
+ { "SWAP-SPACE", 10, 0, 0xfff6 },
+ { "SWAPSPACE2", 10, 0, 0xfff6 },
+ { NULL }
+ }
+};
+
+
+const struct blkid_idinfo swsuspend_idinfo =
+{
+ .name = "swsuspend",
+ .usage = BLKID_USAGE_OTHER,
+ .probefunc = probe_swsuspend,
+ .minsz = 10 * 4096, /* 10 pages */
+ .magics =
+ {
+ { TOI_MAGIC_STRING, TOI_MAGIC_STRLEN, 0, 0 },
+ { "S1SUSPEND", 9, 0, 0xff6 },
+ { "S2SUSPEND", 9, 0, 0xff6 },
+ { "ULSUSPEND", 9, 0, 0xff6 },
+ { "LINHIB0001",10,0, 0xff6 },
+
+ { "S1SUSPEND", 9, 0, 0x1ff6 },
+ { "S2SUSPEND", 9, 0, 0x1ff6 },
+ { "ULSUSPEND", 9, 0, 0x1ff6 },
+ { "LINHIB0001",10,0, 0x1ff6 },
+
+ { "S1SUSPEND", 9, 0, 0x3ff6 },
+ { "S2SUSPEND", 9, 0, 0x3ff6 },
+ { "ULSUSPEND", 9, 0, 0x3ff6 },
+ { "LINHIB0001",10,0, 0x3ff6 },
+
+ { "S1SUSPEND", 9, 0, 0x7ff6 },
+ { "S2SUSPEND", 9, 0, 0x7ff6 },
+ { "ULSUSPEND", 9, 0, 0x7ff6 },
+ { "LINHIB0001",10,0, 0x7ff6 },
+
+ { "S1SUSPEND", 9, 0, 0xfff6 },
+ { "S2SUSPEND", 9, 0, 0xfff6 },
+ { "ULSUSPEND", 9, 0, 0xfff6 },
+ { "LINHIB0001",10,0, 0xfff6 },
+
+ { NULL }
+ }
+};
+
diff --git a/libblkid/src/superblocks/sysv.c b/libblkid/src/superblocks/sysv.c
new file mode 100644
index 000000000..4b345910e
--- /dev/null
+++ b/libblkid/src/superblocks/sysv.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This is written from sratch 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/ubifs.c b/libblkid/src/superblocks/ubifs.c
new file mode 100644
index 000000000..dc8426063
--- /dev/null
+++ b/libblkid/src/superblocks/ubifs.c
@@ -0,0 +1,121 @@
+/*
+ * 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"
+
+/*
+ * 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 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;
+
+ 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));
+ 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 000000000..5cde3cc2a
--- /dev/null
+++ b/libblkid/src/superblocks/udf.c
@@ -0,0 +1,201 @@
+/*
+ * 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"
+#include "iso9660.h"
+
+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 dstring {
+ uint8_t clen;
+ uint8_t c[31];
+ } __attribute__((packed)) ident;
+ } __attribute__((packed)) primary;
+
+ } __attribute__((packed)) type;
+
+} __attribute__((packed));
+
+struct volume_structure_descriptor {
+ uint8_t type;
+ uint8_t id[5];
+ uint8_t version;
+} __attribute__((packed));
+
+#define UDF_VSD_OFFSET 0x8000LL
+
+static int probe_udf(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ struct volume_descriptor *vd;
+ struct volume_structure_descriptor *vsd;
+ unsigned int bs;
+ unsigned int pbs[2];
+ unsigned int b;
+ unsigned int type;
+ unsigned int count;
+ unsigned int loc;
+ unsigned int i;
+
+ /* 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 the 2048-byte block size of optical media. */
+ pbs[0] = blkid_probe_get_sectorsize(pr);
+ pbs[1] = 0x800;
+
+ /* check for a Volume Structure Descriptor (VSD); each is
+ * 2048 bytes long */
+ for (b = 0; b < 0x8000; b += 0x800) {
+ vsd = (struct volume_structure_descriptor *)
+ blkid_probe_get_buffer(pr,
+ UDF_VSD_OFFSET + b,
+ sizeof(*vsd));
+ if (!vsd)
+ return errno ? -errno : 1;
+ if (vsd->id[0] != '\0')
+ goto nsr;
+ }
+ return 1;
+
+nsr:
+ /* search the list of VSDs for a NSR descriptor */
+ for (b = 0; b < 64; b++) {
+ vsd = (struct volume_structure_descriptor *)
+ blkid_probe_get_buffer(pr,
+ UDF_VSD_OFFSET + ((blkid_loff_t) b * 0x800),
+ sizeof(*vsd));
+ if (!vsd)
+ return errno ? -errno : 1;
+ if (vsd->id[0] == '\0')
+ return 1;
+ if (memcmp(vsd->id, "NSR02", 5) == 0)
+ goto anchor;
+ if (memcmp(vsd->id, "NSR03", 5) == 0)
+ goto anchor;
+ }
+ return 1;
+
+anchor:
+ /* read Anchor Volume Descriptor (AVDP), checking block size */
+ for (i = 0; i < 2; i++) {
+ vd = (struct volume_descriptor *)
+ blkid_probe_get_buffer(pr, 256 * pbs[i], sizeof(*vd));
+ if (!vd)
+ return errno ? -errno : 1;
+
+ type = le16_to_cpu(vd->tag.id);
+ if (type == 2) /* TAG_ID_AVDP */
+ goto real_blksz;
+ }
+ return 0;
+
+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);
+
+ /* check if the list is usable */
+ for (b = 0; b < count; b++) {
+ vd = (struct volume_descriptor *)
+ blkid_probe_get_buffer(pr,
+ (blkid_loff_t) (loc + b) * bs,
+ sizeof(*vd));
+ if (!vd)
+ return errno ? -errno : 1;
+ }
+
+ /* Try extract all possible ISO9660 information -- if there is
+ * usable LABEL in ISO header then use it, otherwise read UDF
+ * specific LABEL */
+ if (probe_iso9660(pr, mag) == 0 &&
+ __blkid_probe_lookup_value(pr, "LABEL") != NULL)
+ return 0;
+
+ /* Read UDF label */
+ for (b = 0; b < count; b++) {
+ vd = (struct volume_descriptor *)
+ blkid_probe_get_buffer(pr,
+ (blkid_loff_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 == 1) { /* TAG_ID_PVD */
+ uint8_t clen = vd->type.primary.ident.clen;
+
+ if (clen == 8)
+ blkid_probe_set_label(pr,
+ vd->type.primary.ident.c, 31);
+ else if (clen == 16)
+ blkid_probe_set_utf8label(pr,
+ vd->type.primary.ident.c,
+ 31, BLKID_ENC_UTF16BE);
+
+ if (clen == 8 || clen == 16)
+ break;
+ }
+ }
+
+ 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 },
+ { .magic = "BOOT2", .len = 5, .kboff = 32, .sboff = 1 },
+ { .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1 },
+ { .magic = "CDW02", .len = 5, .kboff = 32, .sboff = 1 },
+ { .magic = "NSR02", .len = 5, .kboff = 32, .sboff = 1 },
+ { .magic = "NSR03", .len = 5, .kboff = 32, .sboff = 1 },
+ { .magic = "TEA01", .len = 5, .kboff = 32, .sboff = 1 },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/ufs.c b/libblkid/src/superblocks/ufs.c
new file mode 100644
index 000000000..6ef2acddc
--- /dev/null
+++ b/libblkid/src/superblocks/ufs.c
@@ -0,0 +1,258 @@
+/*
+ * 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;
+
+ 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/vfat.c b/libblkid/src/superblocks/vfat.c
new file mode 100644
index 000000000..f38deac8b
--- /dev/null
+++ b/libblkid/src/superblocks/vfat.c
@@ -0,0 +1,431 @@
+/*
+ * 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"
+
+/* 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_unknown[3];
+/* 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 {
+/* 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 */
+/* 18*/ uint16_t ms_secs_track;
+/* 1a*/ uint16_t ms_heads;
+/* 1c*/ uint32_t ms_hidden;
+/* V3 BPB */
+/* 20*/ uint32_t ms_total_sect; /* iff ms_sectors == 0 */
+/* V4 BPB */
+/* 24*/ unsigned char ms_unknown[3]; /* Phys drive no., resvd, V4 sig (0x29) */
+/* 27*/ unsigned char ms_serno[4];
+/* 2b*/ unsigned char ms_label[11];
+/* 36*/ unsigned char ms_magic[8];
+/* 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 unsigned char *search_fat_label(blkid_probe pr,
+ uint64_t offset, uint32_t entries)
+{
+ struct vfat_dir_entry *ent, *dir = NULL;
+ uint32_t i;
+
+ DBG(LOWPROBE, ul_debug("\tlook for label in root-dir "
+ "(entries: %d, offset: %jd)", 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,
+ (blkid_loff_t) entries *
+ sizeof(struct vfat_dir_entry));
+ if (!dir)
+ return NULL;
+ }
+
+ 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,
+ (blkid_loff_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));
+ return ent->name;
+ }
+ }
+ return NULL;
+}
+
+static int fat_valid_superblock(const struct blkid_idmag *mag,
+ struct msdos_super_block *ms,
+ struct vfat_super_block *vs,
+ uint32_t *cluster_count, uint32_t *fat_size)
+{
+ 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))
+ 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;
+
+ return 1; /* valid */
+}
+
+/*
+ * 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(mag, ms, vs, 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 = 0;
+ unsigned char *vol_serno = NULL, vol_label_buf[11];
+ uint16_t sector_size = 0, reserved;
+ uint32_t cluster_count, fat_size;
+ 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(mag, ms, vs, &cluster_count, &fat_size))
+ 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);
+
+ vol_label = search_fat_label(pr, root_start, root_dir_entries);
+ if (vol_label) {
+ memcpy(vol_label_buf, vol_label, 11);
+ vol_label = vol_label_buf;
+ }
+
+ if (!vol_label || !memcmp(vol_label, no_name, 11))
+ vol_label = ms->ms_label;
+ 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 = 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);
+
+ vol_label = search_fat_label(pr, next_off, count);
+ if (vol_label) {
+ memcpy(vol_label_buf, vol_label, 11);
+ 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 (!vol_label || !memcmp(vol_label, no_name, 11))
+ vol_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,
+ (blkid_loff_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 (vol_label && memcmp(vol_label, no_name, 11))
+ blkid_probe_set_label(pr, (unsigned char *) 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);
+
+ 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 000000000..ee3ab658e
--- /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 000000000..fac87abe5
--- /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 000000000..19d284cbf
--- /dev/null
+++ b/libblkid/src/superblocks/vxfs.c
@@ -0,0 +1,41 @@
+/*
+ * 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;
+};
+
+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;
+
+ blkid_probe_sprintf_version(pr, "%u", (unsigned int) vxs->vs_version);
+ 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 },
+ { NULL }
+ }
+};
+
diff --git a/libblkid/src/superblocks/xfs.c b/libblkid/src/superblocks/xfs.c
new file mode 100644
index 000000000..a6c04a2f0
--- /dev/null
+++ b/libblkid/src/superblocks/xfs.c
@@ -0,0 +1,277 @@
+/*
+ * 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"
+
+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 */
+
+ /* this is not all... but enough for libblkid */
+
+} __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)
+
+
+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);
+}
+
+static int xfs_verify_sb(struct xfs_super_block *ondisk)
+{
+ 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 != (1 << 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;
+
+ /* TODO: version 5 has also checksum CRC32, maybe we can check it too */
+
+ return 1;
+}
+
+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))
+ return 1;
+
+ if (strlen(xs->sb_fname))
+ blkid_probe_set_label(pr, (unsigned char *) xs->sb_fname,
+ sizeof(xs->sb_fname));
+ blkid_probe_set_uuid(pr, xs->sb_uuid);
+ 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)
+{
+ 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;
+
+ if (memcmp(buf, "XFSB", 4) == 0)
+ return 1; /* this is regular XFS, ignore */
+
+ /* check the first 512 512-byte sectors */
+ for (i = 0; i < 512; i++) {
+ rhead = (struct xlog_rec_header *)&buf[i*512];
+
+ if (xlog_valid_rec_header(rhead)) {
+ blkid_probe_set_uuid_as(pr, rhead->h_uuid, "LOGUUID");
+ 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 000000000..86da59d4a
--- /dev/null
+++ b/libblkid/src/superblocks/zfs.c
@@ -0,0 +1,233 @@
+/*
+ * 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)
+
+/* #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_TRIES 64
+#define ZFS_WANT 4
+
+#define DATA_TYPE_UINT64 8
+#define DATA_TYPE_STRING 9
+
+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;
+};
+
+struct nvlist {
+ uint32_t nvl_unknown[3];
+ struct nvpair nvl_nvpair;
+};
+
+#define nvdebug(fmt, ...) do { } while(0)
+/*#define nvdebug(fmt, a...) fprintf(stderr, fmt, ##a)*/
+
+static void zfs_extract_guid_name(blkid_probe pr, loff_t offset)
+{
+ struct nvlist *nvl;
+ struct nvpair *nvp;
+ size_t left = 4096;
+ int found = 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. */
+ nvl = (struct nvlist *)blkid_probe_get_buffer(pr, offset, left);
+ if (nvl == NULL)
+ return;
+
+ nvdebug("zfs_extract: nvlist offset %llu\n", offset);
+
+ nvp = &nvl->nvl_nvpair;
+ while (left > sizeof(*nvp) && nvp->nvp_size != 0 && found < 3) {
+ int avail; /* tracks that name/value data fits in nvp_size */
+ int namesize;
+
+ nvp->nvp_size = be32_to_cpu(nvp->nvp_size);
+ nvp->nvp_namelen = be32_to_cpu(nvp->nvp_namelen);
+ avail = nvp->nvp_size - nvp->nvp_namelen - sizeof(*nvp);
+
+ nvdebug("left %zd nvp_size %u\n", left, nvp->nvp_size);
+ if (left < nvp->nvp_size || avail < 0)
+ break;
+
+ namesize = (nvp->nvp_namelen + 3) & ~3;
+
+ nvdebug("nvlist: size %u, namelen %u, name %*s\n",
+ nvp->nvp_size, nvp->nvp_namelen, nvp->nvp_namelen,
+ nvp->nvp_name);
+ if (strncmp(nvp->nvp_name, "name", nvp->nvp_namelen) == 0) {
+ struct nvstring *nvs = (void *)(nvp->nvp_name+namesize);
+
+ nvs->nvs_type = be32_to_cpu(nvs->nvs_type);
+ nvs->nvs_strlen = be32_to_cpu(nvs->nvs_strlen);
+ if (nvs->nvs_strlen > UINT_MAX - sizeof(*nvs))
+ break;
+ avail -= nvs->nvs_strlen + sizeof(*nvs);
+ nvdebug("nvstring: type %u string %*s\n", nvs->nvs_type,
+ nvs->nvs_strlen, nvs->nvs_string);
+ if (nvs->nvs_type == DATA_TYPE_STRING && avail >= 0)
+ blkid_probe_set_label(pr, nvs->nvs_string,
+ nvs->nvs_strlen);
+ found++;
+ } else if (strncmp(nvp->nvp_name, "guid",
+ nvp->nvp_namelen) == 0) {
+ struct nvuint64 *nvu = (void *)(nvp->nvp_name+namesize);
+ uint64_t nvu_value;
+
+ memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
+ nvu->nvu_type = be32_to_cpu(nvu->nvu_type);
+ nvu_value = be64_to_cpu(nvu_value);
+ avail -= sizeof(*nvu);
+ nvdebug("nvuint64: type %u value %"PRIu64"\n",
+ nvu->nvu_type, nvu_value);
+ if (nvu->nvu_type == DATA_TYPE_UINT64 && avail >= 0)
+ blkid_probe_sprintf_value(pr, "UUID_SUB",
+ "%"PRIu64, nvu_value);
+ found++;
+ } else if (strncmp(nvp->nvp_name, "pool_guid",
+ nvp->nvp_namelen) == 0) {
+ struct nvuint64 *nvu = (void *)(nvp->nvp_name+namesize);
+ uint64_t nvu_value;
+
+ memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
+ nvu->nvu_type = be32_to_cpu(nvu->nvu_type);
+ nvu_value = be64_to_cpu(nvu_value);
+ avail -= sizeof(*nvu);
+ nvdebug("nvuint64: type %u value %"PRIu64"\n",
+ nvu->nvu_type, nvu_value);
+ if (nvu->nvu_type == DATA_TYPE_UINT64 && avail >= 0)
+ blkid_probe_sprintf_uuid(pr, (unsigned char *)
+ &nvu_value,
+ sizeof(nvu_value),
+ "%"PRIu64, nvu_value);
+ found++;
+ }
+ if (left > nvp->nvp_size)
+ left -= nvp->nvp_size;
+ else
+ left = 0;
+ nvp = (struct nvpair *)((char *)nvp + nvp->nvp_size);
+ }
+}
+
+#define zdebug(fmt, ...) do {} while(0)
+/*#define zdebug(fmt, a...) fprintf(stderr, fmt, ##a)*/
+
+/* 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__)))
+{
+ uint64_t swab_magic = swab64(UBERBLOCK_MAGIC);
+ struct zfs_uberblock *ub;
+ int swab_endian;
+ loff_t offset, ub_offset = 0;
+ int tried;
+ int found;
+
+ zdebug("probe_zfs\n");
+ /* Look for at least 4 uberblocks to ensure a positive match */
+ for (tried = found = 0, offset = VDEV_LABEL_UBERBLOCK;
+ tried < ZFS_TRIES && found < ZFS_WANT;
+ tried++, offset += 4096) {
+ /* also try the second uberblock copy */
+ if (tried == (ZFS_TRIES / 2))
+ offset = VDEV_LABEL_SIZE + VDEV_LABEL_UBERBLOCK;
+
+ ub = (struct zfs_uberblock *)
+ blkid_probe_get_buffer(pr, offset,
+ sizeof(struct zfs_uberblock));
+ if (ub == NULL)
+ return errno ? -errno : 1;
+
+ if (ub->ub_magic == UBERBLOCK_MAGIC) {
+ ub_offset = offset;
+ found++;
+ }
+
+ if ((swab_endian = (ub->ub_magic == swab_magic))) {
+ ub_offset = offset;
+ found++;
+ }
+
+ zdebug("probe_zfs: found %s-endian uberblock at %llu\n",
+ swab_endian ? "big" : "little", offset >> 10);
+ }
+
+ if (found < 4)
+ 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;
+
+ return 0;
+}
+
+const struct blkid_idinfo zfs_idinfo =
+{
+ .name = "zfs_member",
+ .usage = BLKID_USAGE_RAID,
+ .probefunc = probe_zfs,
+ .minsz = 64 * 1024 * 1024,
+ .magics = BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/src/sysfs.h b/libblkid/src/sysfs.h
new file mode 100644
index 000000000..1de624aad
--- /dev/null
+++ b/libblkid/src/sysfs.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ */
+#ifndef UTIL_LINUX_SYSFS_H
+#define UTIL_LINUX_SYSFS_H
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <inttypes.h>
+#include <dirent.h>
+
+struct sysfs_cxt {
+ dev_t devno;
+ int dir_fd; /* /sys/block/<name> */
+ char *dir_path;
+ struct sysfs_cxt *parent;
+
+ unsigned int scsi_host,
+ scsi_channel,
+ scsi_target,
+ scsi_lun;
+
+ unsigned int has_hctl : 1;
+};
+
+#define UL_SYSFSCXT_EMPTY { 0, -1, NULL, NULL, 0, 0, 0, 0, 0 }
+
+extern char *sysfs_devno_attribute_path(dev_t devno, char *buf,
+ size_t bufsiz, const char *attr);
+extern int sysfs_devno_has_attribute(dev_t devno, const char *attr);
+extern char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz);
+extern char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz);
+extern dev_t sysfs_devname_to_devno(const char *name, const char *parent);
+
+extern int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent)
+ __attribute__ ((warn_unused_result));
+extern void sysfs_deinit(struct sysfs_cxt *cxt);
+
+extern DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr);
+
+extern int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st);
+extern ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr,
+ char *buf, size_t bufsiz);
+extern int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr);
+
+extern int sysfs_scanf(struct sysfs_cxt *cxt, const char *attr,
+ const char *fmt, ...)
+ __attribute__ ((format (scanf, 3, 4)));
+
+extern int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res);
+extern int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res);
+extern int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res);
+
+extern int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str);
+extern int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num);
+
+extern char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz);
+
+extern char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr);
+
+extern int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr);
+extern int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname);
+extern dev_t sysfs_partno_to_devno(struct sysfs_cxt *cxt, int partno);
+extern char *sysfs_get_slave(struct sysfs_cxt *cxt);
+
+extern char *sysfs_get_devchain(struct sysfs_cxt *cxt, char *buf, size_t bufsz);
+extern int sysfs_next_subsystem(struct sysfs_cxt *cxt, char *devchain, char **subsys);
+extern int sysfs_is_hotpluggable(struct sysfs_cxt *cxt);
+
+extern int sysfs_is_partition_dirent(DIR *dir, struct dirent *d,
+ const char *parent_name);
+
+extern int sysfs_devno_to_wholedisk(dev_t dev, char *diskname,
+ size_t len, dev_t *diskdevno);
+
+extern int sysfs_devno_is_lvm_private(dev_t devno);
+extern int sysfs_devno_is_wholedisk(dev_t devno);
+
+extern int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h,
+ int *c, int *t, int *l);
+extern char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt *cxt,
+ const char *type, const char *attr);
+extern int sysfs_scsi_host_is(struct sysfs_cxt *cxt, const char *type);
+extern int sysfs_scsi_has_attribute(struct sysfs_cxt *cxt, const char *attr);
+extern int sysfs_scsi_path_contains(struct sysfs_cxt *cxt, const char *pattern);
+
+#endif /* UTIL_LINUX_SYSFS_H */
diff --git a/libblkid/src/tag.c b/libblkid/src/tag.c
new file mode 100644
index 000000000..6fcaa7e5e
--- /dev/null
+++ b/libblkid/src/tag.c
@@ -0,0 +1,475 @@
+/*
+ * 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 = (blkid_tag) calloc(1, sizeof(struct blkid_struct_tag))))
+ return NULL;
+
+ INIT_LIST_HEAD(&tag->bit_tags);
+ INIT_LIST_HEAD(&tag->bit_names);
+
+ return tag;
+}
+
+void blkid_debug_dump_tag(blkid_tag tag)
+{
+ if (!tag) {
+ fprintf(stderr, " tag: NULL\n");
+ return;
+ }
+
+ fprintf(stderr, " tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val);
+}
+
+void blkid_free_tag(blkid_tag tag)
+{
+ if (!tag)
+ return;
+
+ DBG(TAG, ul_debug(" freeing tag %s=%s", tag->bit_name,
+ tag->bit_val ? tag->bit_val : "(NULL)"));
+ DBG(TAG, blkid_debug_dump_tag(tag));
+
+ 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))
+ 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 tagsfrom the device.
+ */
+int blkid_set_tag(blkid_dev dev, const char *name,
+ const char *value, const int vlength)
+{
+ blkid_tag t = 0, head = 0;
+ char *val = 0;
+ char **dev_var = 0;
+
+ 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;
+ }
+ 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;
+
+ 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_debug(" 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 && *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 implemenation. 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 tradeoff 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 = 0;
+ *value = 0;
+ 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;
+
+ if (!cache || !type || !value)
+ return NULL;
+
+ blkid_read_cache(cache);
+
+ DBG(TAG, ul_debug("looking for %s=%s in cache", type, value));
+
+try_again:
+ pri = -1;
+ dev = 0;
+ 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 && (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 && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) {
+ if (blkid_probe_all(cache) < 0)
+ return NULL;
+ goto try_again;
+ }
+ return dev;
+}
+
+#ifdef TEST_PROGRAM
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+
+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: Can not 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 000000000..6add2f7ae
--- /dev/null
+++ b/libblkid/src/topology/dm.c
@@ -0,0 +1,136 @@
+/*
+ * 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/sysmacros.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 *paths[] = {
+ "/usr/local/sbin/dmsetup",
+ "/usr/sbin/dmsetup",
+ "/sbin/dmsetup"
+ };
+ int dmpipe[] = { -1, -1 }, stripes, stripesize;
+ char *cmd = NULL;
+ FILE *stream = NULL;
+ long long offset, size;
+ 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 = (char *) 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:
+ {
+ char *dmargv[7], maj[16], min[16];
+
+ /* Plumbing */
+ close(dmpipe[0]);
+
+ if (dmpipe[1] != STDOUT_FILENO)
+ dup2(dmpipe[1], STDOUT_FILENO);
+
+ /* The libblkid library could linked with setuid programs */
+ if (setgid(getgid()) < 0)
+ exit(1);
+ if (setuid(getuid()) < 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], 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 (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);
+ close(dmpipe[1]);
+ return 0;
+
+nothing:
+ if (stream)
+ fclose(stream);
+ else if (dmpipe[0] != -1)
+ close(dmpipe[0]);
+ if (dmpipe[1] != -1)
+ close(dmpipe[1]);
+ 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 000000000..1fce25abe
--- /dev/null
+++ b/libblkid/src/topology/evms.c
@@ -0,0 +1,78 @@
+/*
+ * Evms topology
+ * -- this is fallback for old systems where the toplogy 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/sysmacros.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 */
+} evms_stripe_info;
+
+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 000000000..3aba09e4f
--- /dev/null
+++ b/libblkid/src/topology/ioctl.c
@@ -0,0 +1,74 @@
+/*
+ * 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 struct topology_val {
+
+ long ioc;
+
+ /* functions to set probing result */
+ int (*set_ulong)(blkid_probe, unsigned long);
+ int (*set_int)(blkid_probe, int);
+
+} 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 }
+ /* 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++) {
+ struct topology_val *val = &topology_vals[i];
+ int rc = 1;
+ unsigned int data;
+
+ if (ioctl(pr->fd, val->ioc, &data) == -1)
+ goto nothing;
+
+ if (val->set_int)
+ rc = val->set_int(pr, (int) data);
+ else
+ rc = val->set_ulong(pr, (unsigned long) data);
+ 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 000000000..20a66b4ea
--- /dev/null
+++ b/libblkid/src/topology/lvm.c
@@ -0,0 +1,148 @@
+/*
+ * 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/sysmacros.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 *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);
+
+ /* The libblkid library could linked with setuid programs */
+ if (setgid(getgid()) < 0)
+ exit(1);
+ if (setuid(getuid()) < 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))
+ sscanf(buf, "Stripes %d", &stripes);
+
+ if (!strncmp(buf, "Stripe size", 11))
+ 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 000000000..c23219742
--- /dev/null
+++ b/libblkid/src/topology/md.c
@@ -0,0 +1,155 @@
+/*
+ * 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/sysmacros.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, 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 000000000..a04b20a37
--- /dev/null
+++ b/libblkid/src/topology/sysfs.c
@@ -0,0 +1,119 @@
+/*
+ * 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 struct topology_val {
+
+ /* /sys/dev/block/<maj>:<min>/<ATTR> */
+ const char *attr;
+
+ /* functions to set probing resut */
+ int (*set_ulong)(blkid_probe, unsigned long);
+ int (*set_int)(blkid_probe, int);
+
+} 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 },
+};
+
+static int probe_sysfs_tp(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ dev_t dev, disk = 0;
+ int rc;
+ struct sysfs_cxt sysfs = UL_SYSFSCXT_EMPTY,
+ parent = UL_SYSFSCXT_EMPTY;
+ size_t i, count = 0;
+
+ dev = blkid_probe_get_devno(pr);
+ if (!dev || sysfs_init(&sysfs, dev, NULL) != 0)
+ return 1;
+
+ rc = 1; /* nothing (default) */
+
+ for (i = 0; i < ARRAY_SIZE(topology_vals); i++) {
+ struct topology_val *val = &topology_vals[i];
+ int ok = sysfs_has_attribute(&sysfs, val->attr);
+
+ rc = 1; /* nothing */
+
+ if (!ok) {
+ if (!disk) {
+ /*
+ * Read atrributes from "disk" if the current
+ * device is a partition.
+ */
+ disk = blkid_probe_get_wholedisk_devno(pr);
+ if (disk && disk != dev) {
+ if (sysfs_init(&parent, disk, NULL) != 0)
+ goto done;
+
+ sysfs.parent = &parent;
+ ok = sysfs_has_attribute(&sysfs,
+ val->attr);
+ }
+ }
+ if (!ok)
+ continue; /* attribute does not exist */
+ }
+
+ if (val->set_ulong) {
+ uint64_t data;
+
+ if (sysfs_read_u64(&sysfs, val->attr, &data) != 0)
+ continue;
+ rc = val->set_ulong(pr, (unsigned long) data);
+
+ } else if (val->set_int) {
+ int64_t data;
+
+ if (sysfs_read_s64(&sysfs, val->attr, &data) != 0)
+ continue;
+ rc = val->set_int(pr, (int) data);
+ }
+
+ if (rc < 0)
+ goto done; /* error */
+ if (rc == 0)
+ count++;
+ }
+
+done:
+ sysfs_deinit(&sysfs);
+ sysfs_deinit(&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 000000000..93fa3802e
--- /dev/null
+++ b/libblkid/src/topology/topology.c
@@ -0,0 +1,362 @@
+/*
+ * 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 "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;
+};
+
+/*
+ * Topology chain probing functions
+ */
+static const struct blkid_idinfo *idinfos[] =
+{
+#ifdef __linux__
+ &ioctl_tp_idinfo,
+ &sysfs_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)
+{
+ if (!pr)
+ return -1;
+ 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_topopogy objects in the same time you have to create
+ * more blkid_probe handlers (see blkid_new_probe()).
+ *
+ * Returns: blkid_topopogy, 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 (!pr || 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_vals(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));
+ 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(chn->data + structoff, &data, sizeof(data));
+ return 0;
+ }
+ return blkid_probe_sprintf_value(pr, name, "%lu", 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);
+}
+
+/**
+ * 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;
+}
+
diff --git a/libblkid/src/topology/topology.h b/libblkid/src/topology/topology.h
new file mode 100644
index 000000000..6d2f43345
--- /dev/null
+++ b/libblkid/src/topology/topology.h
@@ -0,0 +1,24 @@
+#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);
+
+/*
+ * 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 000000000..06f6d53d5
--- /dev/null
+++ b/libblkid/src/verify.c
@@ -0,0 +1,224 @@
+/*
+ * 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(0);
+ diff = now - dev->bid_time;
+
+ if (stat(dev->bid_name, &st) < 0) {
+ DBG(PROBE, ul_debug("blkid_verify: error %m (%d) while "
+ "trying to stat %s", 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 < BLKID_PROBE_MIN ||
+ (dev->bid_flags & BLKID_BID_FL_VERIFIED &&
+ diff < BLKID_PROBE_INTERVAL)))
+ return dev;
+
+#ifndef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ DBG(PROBE, ul_debug("need to revalidate %s (cache time %lu, stat time %lu,\t"
+ "time since last check %lu)",
+ dev->bid_name, (unsigned long)dev->bid_time,
+ (unsigned long)st.st_mtime, (unsigned long)diff));
+#else
+ DBG(PROBE, ul_debug("need to revalidate %s (cache time %lu.%lu, stat time %lu.%lu,\t"
+ "time since last check %lu)",
+ dev->bid_name,
+ (unsigned long)dev->bid_time, (unsigned long)dev->bid_utime,
+ (unsigned long)st.st_mtime, (unsigned long)st.st_mtim.tv_nsec / 1000,
+ (unsigned long)diff));
+#endif
+
+ if (sysfs_devno_is_lvm_private(st.st_rdev)) {
+ 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);
+ if (fd < 0) {
+ DBG(PROBE, ul_debug("blkid_verify: error %m (%d) while "
+ "opening %s", 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(0);
+
+ 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));
+ }
+
+ blkid_reset_probe(cache->probe);
+ blkid_probe_reset_superblocks_filter(cache->probe);
+ 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 000000000..9d129f7ae
--- /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 relese 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);
+}
diff --git a/libblkid/src/widechar.h b/libblkid/src/widechar.h
new file mode 100644
index 000000000..b023b5fb2
--- /dev/null
+++ b/libblkid/src/widechar.h
@@ -0,0 +1,38 @@
+/* Declarations for wide characters */
+/* This file must be included last because the redefinition of wchar_t may
+ cause conflicts when system include files were included after it. */
+
+#ifdef HAVE_WIDECHAR
+
+# include <wchar.h>
+# include <wctype.h>
+
+#else /* !HAVE_WIDECHAR */
+
+# include <ctype.h>
+ /* Fallback for types */
+# define wchar_t char
+# define wint_t int
+# define WEOF EOF
+ /* Fallback for input operations */
+# define fgetwc fgetc
+# define getwc getc
+# define getwchar getchar
+# define fgetws fgets
+ /* Fallback for output operations */
+# define fputwc fputc
+# define putwc putc
+# define putwchar putchar
+# define fputws fputs
+ /* Fallback for character classification */
+# define iswgraph isgraph
+# define iswprint isprint
+# define iswspace isspace
+ /* Fallback for string functions */
+# define wcschr strchr
+# define wcsdup strdup
+# define wcslen strlen
+
+# define wcwidth(c) 1
+
+#endif /* HAVE_WIDECHAR */
diff --git a/libblkid/src/xalloc.h b/libblkid/src/xalloc.h
new file mode 100644
index 000000000..f012fb294
--- /dev/null
+++ b/libblkid/src/xalloc.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * General memory allocation wrappers for malloc, realloc, calloc and strdup
+ */
+
+#ifndef UTIL_LINUX_XALLOC_H
+#define UTIL_LINUX_XALLOC_H
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "c.h"
+
+#ifndef XALLOC_EXIT_CODE
+# define XALLOC_EXIT_CODE EXIT_FAILURE
+#endif
+
+static inline __ul_alloc_size(1)
+void *xmalloc(const size_t size)
+{
+ void *ret = malloc(size);
+
+ if (!ret && size)
+ err(XALLOC_EXIT_CODE, "cannot allocate %zu bytes", size);
+ return ret;
+}
+
+static inline __ul_alloc_size(2)
+void *xrealloc(void *ptr, const size_t size)
+{
+ void *ret = realloc(ptr, size);
+
+ if (!ret && size)
+ err(XALLOC_EXIT_CODE, "cannot allocate %zu bytes", size);
+ return ret;
+}
+
+static inline __ul_calloc_size(1, 2)
+void *xcalloc(const size_t nelems, const size_t size)
+{
+ void *ret = calloc(nelems, size);
+
+ if (!ret && size && nelems)
+ err(XALLOC_EXIT_CODE, "cannot allocate %zu bytes", size);
+ return ret;
+}
+
+static inline char __attribute__((warn_unused_result)) *xstrdup(const char *str)
+{
+ char *ret;
+
+ if (!str)
+ return NULL;
+
+ ret = strdup(str);
+
+ if (!ret)
+ err(XALLOC_EXIT_CODE, "cannot duplicate string");
+ return ret;
+}
+
+static inline char * __attribute__((warn_unused_result)) xstrndup(const char *str, size_t size)
+{
+ char *ret;
+
+ if (!str)
+ return NULL;
+
+ ret = strndup(str, size);
+
+ if (!ret)
+ err(XALLOC_EXIT_CODE, "cannot duplicate string");
+ return ret;
+}
+
+
+static inline int __attribute__ ((__format__(printf, 2, 3)))
+ xasprintf(char **strp, const char *fmt, ...)
+{
+ int ret;
+ va_list args;
+ va_start(args, fmt);
+ ret = vasprintf(&(*strp), fmt, args);
+ va_end(args);
+ if (ret < 0)
+ err(XALLOC_EXIT_CODE, "cannot allocate string");
+ return ret;
+}
+
+static inline int xvasprintf(char **strp, const char *fmt, va_list ap)
+{
+ int ret = vasprintf(&(*strp), fmt, ap);
+ if (ret < 0)
+ err(XALLOC_EXIT_CODE, "cannot allocate string");
+ return ret;
+}
+
+
+static inline char * __attribute__((warn_unused_result)) xgethostname(void)
+{
+ char *name;
+ size_t sz = get_hostname_max() + 1;
+
+ name = xmalloc(sizeof(char) * sz);
+
+ if (gethostname(name, sz) != 0) {
+ free(name);
+ return NULL;
+ }
+ name[sz - 1] = '\0';
+ return name;
+}
+
+#endif