diff options
Diffstat (limited to '')
-rw-r--r-- | libparted/labels/Makefile.am | 60 | ||||
-rw-r--r-- | libparted/labels/Makefile.in | 2063 | ||||
-rw-r--r-- | libparted/labels/aix.c | 237 | ||||
-rw-r--r-- | libparted/labels/atari.c | 1975 | ||||
-rw-r--r-- | libparted/labels/bsd.c | 654 | ||||
-rw-r--r-- | libparted/labels/dasd.c | 1032 | ||||
-rw-r--r-- | libparted/labels/dos.c | 2612 | ||||
-rw-r--r-- | libparted/labels/dvh.c | 896 | ||||
-rw-r--r-- | libparted/labels/dvh.h | 178 | ||||
-rw-r--r-- | libparted/labels/efi_crc32.c | 126 | ||||
-rw-r--r-- | libparted/labels/fdasd.c | 1442 | ||||
-rw-r--r-- | libparted/labels/gpt.c | 1941 | ||||
-rw-r--r-- | libparted/labels/loop.c | 316 | ||||
-rw-r--r-- | libparted/labels/mac.c | 1604 | ||||
-rw-r--r-- | libparted/labels/misc.h | 48 | ||||
-rw-r--r-- | libparted/labels/pc98.c | 813 | ||||
-rw-r--r-- | libparted/labels/pt-common.h | 55 | ||||
-rw-r--r-- | libparted/labels/pt-limit.c | 161 | ||||
-rw-r--r-- | libparted/labels/pt-limit.gperf | 28 | ||||
-rw-r--r-- | libparted/labels/pt-tools.c | 186 | ||||
-rw-r--r-- | libparted/labels/pt-tools.h | 31 | ||||
-rw-r--r-- | libparted/labels/rdb.c | 1167 | ||||
-rw-r--r-- | libparted/labels/sun.c | 910 | ||||
-rw-r--r-- | libparted/labels/vtoc.c | 1329 |
24 files changed, 19864 insertions, 0 deletions
diff --git a/libparted/labels/Makefile.am b/libparted/labels/Makefile.am new file mode 100644 index 0000000..edc3860 --- /dev/null +++ b/libparted/labels/Makefile.am @@ -0,0 +1,60 @@ +# This file is part of GNU Parted +# Copyright (C) 1999-2001, 2007-2014, 2019-2023 Free Software Foundation, Inc. +# +# This file may be modified and/or distributed without restriction. + +if COMPILE_FOR_S390 +S390_SRCS = dasd.c fdasd.c vtoc.c +else +S390_SRCS = +endif + +AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS) + +partedincludedir = \ + -I$(top_srcdir)/lib -I$(top_builddir)/include -I$(top_srcdir)/include \ + -I$(top_srcdir)/libparted +noinst_LTLIBRARIES = liblabels.la + +liblabels_la_SOURCES = \ + $(S390_SRCS) \ + aix.c \ + atari.c \ + bsd.c \ + dos.c \ + dvh.c \ + dvh.h \ + efi_crc32.c \ + gpt.c \ + loop.c \ + mac.c \ + misc.h \ + pc98.c \ + pt-common.h \ + pt-tools.c \ + pt-tools.h \ + rdb.c \ + sun.c + +liblabels_la_LIBADD = $(OS_LIBS) $(INTLLIBS) $(LIBICONV) + +AM_CPPFLAGS = $(partedincludedir) $(INTLINCS) + +# Included by 'pt-tools.c', so needs to be built early. +BUILT_SOURCES = pt-limit.c +MAINTAINERCLEANFILES = $(BUILT_SOURCES) +# DOn't add this to '_SOURCES', because it's not to be compiled! +EXTRA_DIST= pt-limit.c + +GPERF = gperf +GPERF_OPTIONS = \ + -C -N pt_limit_lookup -n -t -s 6 -k '*' --language=ANSI-C + +pt-limit.c: pt-limit.gperf + rm -f $@ $@-tmp + $(GPERF) $(GPERF_OPTIONS) $< \ + | perl -ne '/__GNUC_STDC_INLINE__/ and print "static\n"; print' \ + > $@-tmp + chmod a-w $@-tmp + mv $@-tmp $@ +EXTRA_DIST += pt-limit.gperf diff --git a/libparted/labels/Makefile.in b/libparted/labels/Makefile.in new file mode 100644 index 0000000..cbf6ef4 --- /dev/null +++ b/libparted/labels/Makefile.in @@ -0,0 +1,2063 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# This file is part of GNU Parted +# Copyright (C) 1999-2001, 2007-2014, 2019-2023 Free Software Foundation, Inc. +# +# This file may be modified and/or distributed without restriction. + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = libparted/labels +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/00gnulib.m4 \ + $(top_srcdir)/m4/__inline.m4 \ + $(top_srcdir)/m4/absolute-header.m4 $(top_srcdir)/m4/alloca.m4 \ + $(top_srcdir)/m4/arpa_inet_h.m4 $(top_srcdir)/m4/assert.m4 \ + $(top_srcdir)/m4/assert_h.m4 $(top_srcdir)/m4/btowc.m4 \ + $(top_srcdir)/m4/build-to-host.m4 \ + $(top_srcdir)/m4/builtin-expect.m4 $(top_srcdir)/m4/c-bool.m4 \ + $(top_srcdir)/m4/calloc.m4 $(top_srcdir)/m4/canonicalize.m4 \ + $(top_srcdir)/m4/clock_time.m4 $(top_srcdir)/m4/close.m4 \ + $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/config-h.m4 \ + $(top_srcdir)/m4/configmake.m4 $(top_srcdir)/m4/ctype_h.m4 \ + $(top_srcdir)/m4/double-slash-root.m4 $(top_srcdir)/m4/dup2.m4 \ + $(top_srcdir)/m4/eealloc.m4 $(top_srcdir)/m4/environ.m4 \ + $(top_srcdir)/m4/errno_h.m4 $(top_srcdir)/m4/error.m4 \ + $(top_srcdir)/m4/error_h.m4 $(top_srcdir)/m4/extensions.m4 \ + $(top_srcdir)/m4/extern-inline.m4 $(top_srcdir)/m4/fcntl-o.m4 \ + $(top_srcdir)/m4/fcntl.m4 $(top_srcdir)/m4/fcntl_h.m4 \ + $(top_srcdir)/m4/fdopen.m4 $(top_srcdir)/m4/flexmember.m4 \ + $(top_srcdir)/m4/fpending.m4 $(top_srcdir)/m4/free.m4 \ + $(top_srcdir)/m4/fstat.m4 $(top_srcdir)/m4/fsync.m4 \ + $(top_srcdir)/m4/ftruncate.m4 $(top_srcdir)/m4/getcwd.m4 \ + $(top_srcdir)/m4/getdtablesize.m4 $(top_srcdir)/m4/getopt.m4 \ + $(top_srcdir)/m4/getpagesize.m4 \ + $(top_srcdir)/m4/getprogname.m4 $(top_srcdir)/m4/getrandom.m4 \ + $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/gettimeofday.m4 \ + $(top_srcdir)/m4/gnulib-common.m4 \ + $(top_srcdir)/m4/gnulib-comp.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/include_next.m4 $(top_srcdir)/m4/inet_pton.m4 \ + $(top_srcdir)/m4/intl-thread-locale.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/inttypes.m4 \ + $(top_srcdir)/m4/ioctl.m4 $(top_srcdir)/m4/isblank.m4 \ + $(top_srcdir)/m4/langinfo_h.m4 $(top_srcdir)/m4/largefile.m4 \ + $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/lib-ignore.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/limits-h.m4 $(top_srcdir)/m4/localcharset.m4 \ + $(top_srcdir)/m4/locale-fr.m4 $(top_srcdir)/m4/locale-ja.m4 \ + $(top_srcdir)/m4/locale-tr.m4 $(top_srcdir)/m4/locale-zh.m4 \ + $(top_srcdir)/m4/locale_h.m4 $(top_srcdir)/m4/localeconv.m4 \ + $(top_srcdir)/m4/localename.m4 $(top_srcdir)/m4/lock.m4 \ + $(top_srcdir)/m4/lseek.m4 $(top_srcdir)/m4/lstat.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/malloc.m4 $(top_srcdir)/m4/malloca.m4 \ + $(top_srcdir)/m4/manywarnings.m4 $(top_srcdir)/m4/mbrtowc.m4 \ + $(top_srcdir)/m4/mbsinit.m4 $(top_srcdir)/m4/mbstate_t.m4 \ + $(top_srcdir)/m4/mbtowc.m4 $(top_srcdir)/m4/memchr.m4 \ + $(top_srcdir)/m4/mempcpy.m4 $(top_srcdir)/m4/minmax.m4 \ + $(top_srcdir)/m4/mkdir.m4 $(top_srcdir)/m4/mkstemp.m4 \ + $(top_srcdir)/m4/mmap-anon.m4 $(top_srcdir)/m4/mode_t.m4 \ + $(top_srcdir)/m4/msvc-inval.m4 \ + $(top_srcdir)/m4/msvc-nothrow.m4 $(top_srcdir)/m4/multiarch.m4 \ + $(top_srcdir)/m4/musl.m4 $(top_srcdir)/m4/nanosleep.m4 \ + $(top_srcdir)/m4/netinet_in_h.m4 \ + $(top_srcdir)/m4/nl_langinfo.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/nocrash.m4 $(top_srcdir)/m4/o-direct.m4 \ + $(top_srcdir)/m4/off_t.m4 $(top_srcdir)/m4/open-cloexec.m4 \ + $(top_srcdir)/m4/open-slash.m4 $(top_srcdir)/m4/open.m4 \ + $(top_srcdir)/m4/pathmax.m4 $(top_srcdir)/m4/perror.m4 \ + $(top_srcdir)/m4/pipe.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/priv-set.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/pselect.m4 $(top_srcdir)/m4/pthread-thread.m4 \ + $(top_srcdir)/m4/pthread_h.m4 \ + $(top_srcdir)/m4/pthread_rwlock_rdlock.m4 \ + $(top_srcdir)/m4/pthread_sigmask.m4 $(top_srcdir)/m4/putenv.m4 \ + $(top_srcdir)/m4/quote.m4 $(top_srcdir)/m4/quotearg.m4 \ + $(top_srcdir)/m4/raise.m4 $(top_srcdir)/m4/rawmemchr.m4 \ + $(top_srcdir)/m4/read.m4 $(top_srcdir)/m4/readlink.m4 \ + $(top_srcdir)/m4/realloc.m4 $(top_srcdir)/m4/reallocarray.m4 \ + $(top_srcdir)/m4/regex.m4 $(top_srcdir)/m4/rpmatch.m4 \ + $(top_srcdir)/m4/safe-read.m4 $(top_srcdir)/m4/sched_h.m4 \ + $(top_srcdir)/m4/sched_yield.m4 $(top_srcdir)/m4/select.m4 \ + $(top_srcdir)/m4/semaphore.m4 $(top_srcdir)/m4/setenv.m4 \ + $(top_srcdir)/m4/setlocale.m4 \ + $(top_srcdir)/m4/setlocale_null.m4 \ + $(top_srcdir)/m4/signal_h.m4 \ + $(top_srcdir)/m4/signalblocking.m4 $(top_srcdir)/m4/sleep.m4 \ + $(top_srcdir)/m4/socketlib.m4 $(top_srcdir)/m4/sockets.m4 \ + $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sockpfaf.m4 \ + $(top_srcdir)/m4/ssize_t.m4 $(top_srcdir)/m4/stat-time.m4 \ + $(top_srcdir)/m4/stat.m4 $(top_srcdir)/m4/stdalign.m4 \ + $(top_srcdir)/m4/stdarg.m4 $(top_srcdir)/m4/stddef_h.m4 \ + $(top_srcdir)/m4/stdint.m4 $(top_srcdir)/m4/stdio_h.m4 \ + $(top_srcdir)/m4/stdlib_h.m4 $(top_srcdir)/m4/strdup.m4 \ + $(top_srcdir)/m4/strerror.m4 $(top_srcdir)/m4/strerror_r.m4 \ + $(top_srcdir)/m4/string_h.m4 $(top_srcdir)/m4/strtoll.m4 \ + $(top_srcdir)/m4/strtoull.m4 $(top_srcdir)/m4/symlink.m4 \ + $(top_srcdir)/m4/sys_ioctl_h.m4 \ + $(top_srcdir)/m4/sys_random_h.m4 \ + $(top_srcdir)/m4/sys_select_h.m4 \ + $(top_srcdir)/m4/sys_socket_h.m4 \ + $(top_srcdir)/m4/sys_stat_h.m4 $(top_srcdir)/m4/sys_time_h.m4 \ + $(top_srcdir)/m4/sys_types_h.m4 $(top_srcdir)/m4/sys_uio_h.m4 \ + $(top_srcdir)/m4/tempname.m4 $(top_srcdir)/m4/thread.m4 \ + $(top_srcdir)/m4/threadlib.m4 $(top_srcdir)/m4/time.m4 \ + $(top_srcdir)/m4/time_h.m4 $(top_srcdir)/m4/unistd_h.m4 \ + $(top_srcdir)/m4/unlink.m4 $(top_srcdir)/m4/unlinkdir.m4 \ + $(top_srcdir)/m4/usleep.m4 $(top_srcdir)/m4/version-etc.m4 \ + $(top_srcdir)/m4/visibility.m4 $(top_srcdir)/m4/warn-on-use.m4 \ + $(top_srcdir)/m4/warnings.m4 $(top_srcdir)/m4/wchar_h.m4 \ + $(top_srcdir)/m4/wchar_t.m4 $(top_srcdir)/m4/wcrtomb.m4 \ + $(top_srcdir)/m4/wctob.m4 $(top_srcdir)/m4/wctomb.m4 \ + $(top_srcdir)/m4/wctype_h.m4 $(top_srcdir)/m4/wint_t.m4 \ + $(top_srcdir)/m4/xalloc.m4 $(top_srcdir)/m4/xstrtol.m4 \ + $(top_srcdir)/m4/yield.m4 $(top_srcdir)/m4/zzgnulib.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/lib/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +liblabels_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am__liblabels_la_SOURCES_DIST = dasd.c fdasd.c vtoc.c aix.c atari.c \ + bsd.c dos.c dvh.c dvh.h efi_crc32.c gpt.c loop.c mac.c misc.h \ + pc98.c pt-common.h pt-tools.c pt-tools.h rdb.c sun.c +@COMPILE_FOR_S390_TRUE@am__objects_1 = dasd.lo fdasd.lo vtoc.lo +am_liblabels_la_OBJECTS = $(am__objects_1) aix.lo atari.lo bsd.lo \ + dos.lo dvh.lo efi_crc32.lo gpt.lo loop.lo mac.lo pc98.lo \ + pt-tools.lo rdb.lo sun.lo +liblabels_la_OBJECTS = $(am_liblabels_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/lib +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/aix.Plo ./$(DEPDIR)/atari.Plo \ + ./$(DEPDIR)/bsd.Plo ./$(DEPDIR)/dasd.Plo ./$(DEPDIR)/dos.Plo \ + ./$(DEPDIR)/dvh.Plo ./$(DEPDIR)/efi_crc32.Plo \ + ./$(DEPDIR)/fdasd.Plo ./$(DEPDIR)/gpt.Plo ./$(DEPDIR)/loop.Plo \ + ./$(DEPDIR)/mac.Plo ./$(DEPDIR)/pc98.Plo \ + ./$(DEPDIR)/pt-tools.Plo ./$(DEPDIR)/rdb.Plo \ + ./$(DEPDIR)/sun.Plo ./$(DEPDIR)/vtoc.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(liblabels_la_SOURCES) +DIST_SOURCES = $(am__liblabels_la_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build-aux/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +pkgdatadir = @pkgdatadir@ +pkgincludedir = @pkgincludedir@ +pkglibdir = @pkglibdir@ +pkglibexecdir = @pkglibexecdir@ +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +ALLOCA_H = @ALLOCA_H@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@ +AR = @AR@ +ARFLAGS = @ARFLAGS@ +ASSERT_H = @ASSERT_H@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@ +BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@ +BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@ +BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@ +BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@ +BUILDINFO = @BUILDINFO@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CFLAG_VISIBILITY = @CFLAG_VISIBILITY@ +CHECK_CFLAGS = @CHECK_CFLAGS@ +CHECK_LIBS = @CHECK_LIBS@ +CLOCK_TIME_LIB = @CLOCK_TIME_LIB@ +CONFIG_INCLUDE = @CONFIG_INCLUDE@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DM_LIBS = @DM_LIBS@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EMULTIHOP_HIDDEN = @EMULTIHOP_HIDDEN@ +EMULTIHOP_VALUE = @EMULTIHOP_VALUE@ +ENABLE_DEVICE_MAPPER = @ENABLE_DEVICE_MAPPER@ +ENOLINK_HIDDEN = @ENOLINK_HIDDEN@ +ENOLINK_VALUE = @ENOLINK_VALUE@ +EOVERFLOW_HIDDEN = @EOVERFLOW_HIDDEN@ +EOVERFLOW_VALUE = @EOVERFLOW_VALUE@ +ERRNO_H = @ERRNO_H@ +ERROR_H = @ERROR_H@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +GETOPT_CDEFS_H = @GETOPT_CDEFS_H@ +GETOPT_H = @GETOPT_H@ +GETRANDOM_LIB = @GETRANDOM_LIB@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GL_CFLAG_ALLOW_WARNINGS = @GL_CFLAG_ALLOW_WARNINGS@ +GL_CFLAG_GNULIB_WARNINGS = @GL_CFLAG_GNULIB_WARNINGS@ +GL_CXXFLAG_ALLOW_WARNINGS = @GL_CXXFLAG_ALLOW_WARNINGS@ +GL_GNULIB_ACCEPT = @GL_GNULIB_ACCEPT@ +GL_GNULIB_ACCEPT4 = @GL_GNULIB_ACCEPT4@ +GL_GNULIB_ACCESS = @GL_GNULIB_ACCESS@ +GL_GNULIB_ALIGNED_ALLOC = @GL_GNULIB_ALIGNED_ALLOC@ +GL_GNULIB_ATOLL = @GL_GNULIB_ATOLL@ +GL_GNULIB_BIND = @GL_GNULIB_BIND@ +GL_GNULIB_BTOWC = @GL_GNULIB_BTOWC@ +GL_GNULIB_CALLOC_GNU = @GL_GNULIB_CALLOC_GNU@ +GL_GNULIB_CALLOC_POSIX = @GL_GNULIB_CALLOC_POSIX@ +GL_GNULIB_CANONICALIZE_FILE_NAME = @GL_GNULIB_CANONICALIZE_FILE_NAME@ +GL_GNULIB_CHDIR = @GL_GNULIB_CHDIR@ +GL_GNULIB_CHMOD = @GL_GNULIB_CHMOD@ +GL_GNULIB_CHOWN = @GL_GNULIB_CHOWN@ +GL_GNULIB_CLOSE = @GL_GNULIB_CLOSE@ +GL_GNULIB_CONNECT = @GL_GNULIB_CONNECT@ +GL_GNULIB_COPY_FILE_RANGE = @GL_GNULIB_COPY_FILE_RANGE@ +GL_GNULIB_CREAT = @GL_GNULIB_CREAT@ +GL_GNULIB_CTIME = @GL_GNULIB_CTIME@ +GL_GNULIB_DPRINTF = @GL_GNULIB_DPRINTF@ +GL_GNULIB_DUP = @GL_GNULIB_DUP@ +GL_GNULIB_DUP2 = @GL_GNULIB_DUP2@ +GL_GNULIB_DUP3 = @GL_GNULIB_DUP3@ +GL_GNULIB_DUPLOCALE = @GL_GNULIB_DUPLOCALE@ +GL_GNULIB_ENVIRON = @GL_GNULIB_ENVIRON@ +GL_GNULIB_EUIDACCESS = @GL_GNULIB_EUIDACCESS@ +GL_GNULIB_EXECL = @GL_GNULIB_EXECL@ +GL_GNULIB_EXECLE = @GL_GNULIB_EXECLE@ +GL_GNULIB_EXECLP = @GL_GNULIB_EXECLP@ +GL_GNULIB_EXECV = @GL_GNULIB_EXECV@ +GL_GNULIB_EXECVE = @GL_GNULIB_EXECVE@ +GL_GNULIB_EXECVP = @GL_GNULIB_EXECVP@ +GL_GNULIB_EXECVPE = @GL_GNULIB_EXECVPE@ +GL_GNULIB_EXPLICIT_BZERO = @GL_GNULIB_EXPLICIT_BZERO@ +GL_GNULIB_FACCESSAT = @GL_GNULIB_FACCESSAT@ +GL_GNULIB_FCHDIR = @GL_GNULIB_FCHDIR@ +GL_GNULIB_FCHMODAT = @GL_GNULIB_FCHMODAT@ +GL_GNULIB_FCHOWNAT = @GL_GNULIB_FCHOWNAT@ +GL_GNULIB_FCLOSE = @GL_GNULIB_FCLOSE@ +GL_GNULIB_FCNTL = @GL_GNULIB_FCNTL@ +GL_GNULIB_FDATASYNC = @GL_GNULIB_FDATASYNC@ +GL_GNULIB_FDOPEN = @GL_GNULIB_FDOPEN@ +GL_GNULIB_FFLUSH = @GL_GNULIB_FFLUSH@ +GL_GNULIB_FFSL = @GL_GNULIB_FFSL@ +GL_GNULIB_FFSLL = @GL_GNULIB_FFSLL@ +GL_GNULIB_FGETC = @GL_GNULIB_FGETC@ +GL_GNULIB_FGETS = @GL_GNULIB_FGETS@ +GL_GNULIB_FOPEN = @GL_GNULIB_FOPEN@ +GL_GNULIB_FOPEN_GNU = @GL_GNULIB_FOPEN_GNU@ +GL_GNULIB_FPRINTF = @GL_GNULIB_FPRINTF@ +GL_GNULIB_FPRINTF_POSIX = @GL_GNULIB_FPRINTF_POSIX@ +GL_GNULIB_FPURGE = @GL_GNULIB_FPURGE@ +GL_GNULIB_FPUTC = @GL_GNULIB_FPUTC@ +GL_GNULIB_FPUTS = @GL_GNULIB_FPUTS@ +GL_GNULIB_FREAD = @GL_GNULIB_FREAD@ +GL_GNULIB_FREE_POSIX = @GL_GNULIB_FREE_POSIX@ +GL_GNULIB_FREOPEN = @GL_GNULIB_FREOPEN@ +GL_GNULIB_FSCANF = @GL_GNULIB_FSCANF@ +GL_GNULIB_FSEEK = @GL_GNULIB_FSEEK@ +GL_GNULIB_FSEEKO = @GL_GNULIB_FSEEKO@ +GL_GNULIB_FSTAT = @GL_GNULIB_FSTAT@ +GL_GNULIB_FSTATAT = @GL_GNULIB_FSTATAT@ +GL_GNULIB_FSYNC = @GL_GNULIB_FSYNC@ +GL_GNULIB_FTELL = @GL_GNULIB_FTELL@ +GL_GNULIB_FTELLO = @GL_GNULIB_FTELLO@ +GL_GNULIB_FTRUNCATE = @GL_GNULIB_FTRUNCATE@ +GL_GNULIB_FUTIMENS = @GL_GNULIB_FUTIMENS@ +GL_GNULIB_FWRITE = @GL_GNULIB_FWRITE@ +GL_GNULIB_GETC = @GL_GNULIB_GETC@ +GL_GNULIB_GETCHAR = @GL_GNULIB_GETCHAR@ +GL_GNULIB_GETCWD = @GL_GNULIB_GETCWD@ +GL_GNULIB_GETDELIM = @GL_GNULIB_GETDELIM@ +GL_GNULIB_GETDOMAINNAME = @GL_GNULIB_GETDOMAINNAME@ +GL_GNULIB_GETDTABLESIZE = @GL_GNULIB_GETDTABLESIZE@ +GL_GNULIB_GETENTROPY = @GL_GNULIB_GETENTROPY@ +GL_GNULIB_GETGROUPS = @GL_GNULIB_GETGROUPS@ +GL_GNULIB_GETHOSTNAME = @GL_GNULIB_GETHOSTNAME@ +GL_GNULIB_GETLINE = @GL_GNULIB_GETLINE@ +GL_GNULIB_GETLOADAVG = @GL_GNULIB_GETLOADAVG@ +GL_GNULIB_GETLOGIN = @GL_GNULIB_GETLOGIN@ +GL_GNULIB_GETLOGIN_R = @GL_GNULIB_GETLOGIN_R@ +GL_GNULIB_GETOPT_POSIX = @GL_GNULIB_GETOPT_POSIX@ +GL_GNULIB_GETPAGESIZE = @GL_GNULIB_GETPAGESIZE@ +GL_GNULIB_GETPASS = @GL_GNULIB_GETPASS@ +GL_GNULIB_GETPASS_GNU = @GL_GNULIB_GETPASS_GNU@ +GL_GNULIB_GETPEERNAME = @GL_GNULIB_GETPEERNAME@ +GL_GNULIB_GETPROGNAME = @GL_GNULIB_GETPROGNAME@ +GL_GNULIB_GETRANDOM = @GL_GNULIB_GETRANDOM@ +GL_GNULIB_GETSOCKNAME = @GL_GNULIB_GETSOCKNAME@ +GL_GNULIB_GETSOCKOPT = @GL_GNULIB_GETSOCKOPT@ +GL_GNULIB_GETSUBOPT = @GL_GNULIB_GETSUBOPT@ +GL_GNULIB_GETTIMEOFDAY = @GL_GNULIB_GETTIMEOFDAY@ +GL_GNULIB_GETUMASK = @GL_GNULIB_GETUMASK@ +GL_GNULIB_GETUSERSHELL = @GL_GNULIB_GETUSERSHELL@ +GL_GNULIB_GRANTPT = @GL_GNULIB_GRANTPT@ +GL_GNULIB_GROUP_MEMBER = @GL_GNULIB_GROUP_MEMBER@ +GL_GNULIB_IMAXABS = @GL_GNULIB_IMAXABS@ +GL_GNULIB_IMAXDIV = @GL_GNULIB_IMAXDIV@ +GL_GNULIB_INET_NTOP = @GL_GNULIB_INET_NTOP@ +GL_GNULIB_INET_PTON = @GL_GNULIB_INET_PTON@ +GL_GNULIB_IOCTL = @GL_GNULIB_IOCTL@ +GL_GNULIB_ISATTY = @GL_GNULIB_ISATTY@ +GL_GNULIB_ISBLANK = @GL_GNULIB_ISBLANK@ +GL_GNULIB_ISWBLANK = @GL_GNULIB_ISWBLANK@ +GL_GNULIB_ISWCTYPE = @GL_GNULIB_ISWCTYPE@ +GL_GNULIB_ISWDIGIT = @GL_GNULIB_ISWDIGIT@ +GL_GNULIB_ISWXDIGIT = @GL_GNULIB_ISWXDIGIT@ +GL_GNULIB_LCHMOD = @GL_GNULIB_LCHMOD@ +GL_GNULIB_LCHOWN = @GL_GNULIB_LCHOWN@ +GL_GNULIB_LINK = @GL_GNULIB_LINK@ +GL_GNULIB_LINKAT = @GL_GNULIB_LINKAT@ +GL_GNULIB_LISTEN = @GL_GNULIB_LISTEN@ +GL_GNULIB_LOCALECONV = @GL_GNULIB_LOCALECONV@ +GL_GNULIB_LOCALENAME = @GL_GNULIB_LOCALENAME@ +GL_GNULIB_LOCALTIME = @GL_GNULIB_LOCALTIME@ +GL_GNULIB_LSEEK = @GL_GNULIB_LSEEK@ +GL_GNULIB_LSTAT = @GL_GNULIB_LSTAT@ +GL_GNULIB_MALLOC_GNU = @GL_GNULIB_MALLOC_GNU@ +GL_GNULIB_MALLOC_POSIX = @GL_GNULIB_MALLOC_POSIX@ +GL_GNULIB_MBRLEN = @GL_GNULIB_MBRLEN@ +GL_GNULIB_MBRTOWC = @GL_GNULIB_MBRTOWC@ +GL_GNULIB_MBSCASECMP = @GL_GNULIB_MBSCASECMP@ +GL_GNULIB_MBSCASESTR = @GL_GNULIB_MBSCASESTR@ +GL_GNULIB_MBSCHR = @GL_GNULIB_MBSCHR@ +GL_GNULIB_MBSCSPN = @GL_GNULIB_MBSCSPN@ +GL_GNULIB_MBSINIT = @GL_GNULIB_MBSINIT@ +GL_GNULIB_MBSLEN = @GL_GNULIB_MBSLEN@ +GL_GNULIB_MBSNCASECMP = @GL_GNULIB_MBSNCASECMP@ +GL_GNULIB_MBSNLEN = @GL_GNULIB_MBSNLEN@ +GL_GNULIB_MBSNRTOWCS = @GL_GNULIB_MBSNRTOWCS@ +GL_GNULIB_MBSPBRK = @GL_GNULIB_MBSPBRK@ +GL_GNULIB_MBSPCASECMP = @GL_GNULIB_MBSPCASECMP@ +GL_GNULIB_MBSRCHR = @GL_GNULIB_MBSRCHR@ +GL_GNULIB_MBSRTOWCS = @GL_GNULIB_MBSRTOWCS@ +GL_GNULIB_MBSSEP = @GL_GNULIB_MBSSEP@ +GL_GNULIB_MBSSPN = @GL_GNULIB_MBSSPN@ +GL_GNULIB_MBSSTR = @GL_GNULIB_MBSSTR@ +GL_GNULIB_MBSTOK_R = @GL_GNULIB_MBSTOK_R@ +GL_GNULIB_MBTOWC = @GL_GNULIB_MBTOWC@ +GL_GNULIB_MDA_ACCESS = @GL_GNULIB_MDA_ACCESS@ +GL_GNULIB_MDA_CHDIR = @GL_GNULIB_MDA_CHDIR@ +GL_GNULIB_MDA_CHMOD = @GL_GNULIB_MDA_CHMOD@ +GL_GNULIB_MDA_CLOSE = @GL_GNULIB_MDA_CLOSE@ +GL_GNULIB_MDA_CREAT = @GL_GNULIB_MDA_CREAT@ +GL_GNULIB_MDA_DUP = @GL_GNULIB_MDA_DUP@ +GL_GNULIB_MDA_DUP2 = @GL_GNULIB_MDA_DUP2@ +GL_GNULIB_MDA_ECVT = @GL_GNULIB_MDA_ECVT@ +GL_GNULIB_MDA_EXECL = @GL_GNULIB_MDA_EXECL@ +GL_GNULIB_MDA_EXECLE = @GL_GNULIB_MDA_EXECLE@ +GL_GNULIB_MDA_EXECLP = @GL_GNULIB_MDA_EXECLP@ +GL_GNULIB_MDA_EXECV = @GL_GNULIB_MDA_EXECV@ +GL_GNULIB_MDA_EXECVE = @GL_GNULIB_MDA_EXECVE@ +GL_GNULIB_MDA_EXECVP = @GL_GNULIB_MDA_EXECVP@ +GL_GNULIB_MDA_EXECVPE = @GL_GNULIB_MDA_EXECVPE@ +GL_GNULIB_MDA_FCLOSEALL = @GL_GNULIB_MDA_FCLOSEALL@ +GL_GNULIB_MDA_FCVT = @GL_GNULIB_MDA_FCVT@ +GL_GNULIB_MDA_FDOPEN = @GL_GNULIB_MDA_FDOPEN@ +GL_GNULIB_MDA_FILENO = @GL_GNULIB_MDA_FILENO@ +GL_GNULIB_MDA_GCVT = @GL_GNULIB_MDA_GCVT@ +GL_GNULIB_MDA_GETCWD = @GL_GNULIB_MDA_GETCWD@ +GL_GNULIB_MDA_GETPID = @GL_GNULIB_MDA_GETPID@ +GL_GNULIB_MDA_GETW = @GL_GNULIB_MDA_GETW@ +GL_GNULIB_MDA_ISATTY = @GL_GNULIB_MDA_ISATTY@ +GL_GNULIB_MDA_LSEEK = @GL_GNULIB_MDA_LSEEK@ +GL_GNULIB_MDA_MEMCCPY = @GL_GNULIB_MDA_MEMCCPY@ +GL_GNULIB_MDA_MKDIR = @GL_GNULIB_MDA_MKDIR@ +GL_GNULIB_MDA_MKTEMP = @GL_GNULIB_MDA_MKTEMP@ +GL_GNULIB_MDA_OPEN = @GL_GNULIB_MDA_OPEN@ +GL_GNULIB_MDA_PUTENV = @GL_GNULIB_MDA_PUTENV@ +GL_GNULIB_MDA_PUTW = @GL_GNULIB_MDA_PUTW@ +GL_GNULIB_MDA_READ = @GL_GNULIB_MDA_READ@ +GL_GNULIB_MDA_RMDIR = @GL_GNULIB_MDA_RMDIR@ +GL_GNULIB_MDA_STRDUP = @GL_GNULIB_MDA_STRDUP@ +GL_GNULIB_MDA_SWAB = @GL_GNULIB_MDA_SWAB@ +GL_GNULIB_MDA_TEMPNAM = @GL_GNULIB_MDA_TEMPNAM@ +GL_GNULIB_MDA_TZSET = @GL_GNULIB_MDA_TZSET@ +GL_GNULIB_MDA_UMASK = @GL_GNULIB_MDA_UMASK@ +GL_GNULIB_MDA_UNLINK = @GL_GNULIB_MDA_UNLINK@ +GL_GNULIB_MDA_WCSDUP = @GL_GNULIB_MDA_WCSDUP@ +GL_GNULIB_MDA_WRITE = @GL_GNULIB_MDA_WRITE@ +GL_GNULIB_MEMCHR = @GL_GNULIB_MEMCHR@ +GL_GNULIB_MEMMEM = @GL_GNULIB_MEMMEM@ +GL_GNULIB_MEMPCPY = @GL_GNULIB_MEMPCPY@ +GL_GNULIB_MEMRCHR = @GL_GNULIB_MEMRCHR@ +GL_GNULIB_MEMSET_EXPLICIT = @GL_GNULIB_MEMSET_EXPLICIT@ +GL_GNULIB_MKDIR = @GL_GNULIB_MKDIR@ +GL_GNULIB_MKDIRAT = @GL_GNULIB_MKDIRAT@ +GL_GNULIB_MKDTEMP = @GL_GNULIB_MKDTEMP@ +GL_GNULIB_MKFIFO = @GL_GNULIB_MKFIFO@ +GL_GNULIB_MKFIFOAT = @GL_GNULIB_MKFIFOAT@ +GL_GNULIB_MKNOD = @GL_GNULIB_MKNOD@ +GL_GNULIB_MKNODAT = @GL_GNULIB_MKNODAT@ +GL_GNULIB_MKOSTEMP = @GL_GNULIB_MKOSTEMP@ +GL_GNULIB_MKOSTEMPS = @GL_GNULIB_MKOSTEMPS@ +GL_GNULIB_MKSTEMP = @GL_GNULIB_MKSTEMP@ +GL_GNULIB_MKSTEMPS = @GL_GNULIB_MKSTEMPS@ +GL_GNULIB_MKTIME = @GL_GNULIB_MKTIME@ +GL_GNULIB_NANOSLEEP = @GL_GNULIB_NANOSLEEP@ +GL_GNULIB_NL_LANGINFO = @GL_GNULIB_NL_LANGINFO@ +GL_GNULIB_NONBLOCKING = @GL_GNULIB_NONBLOCKING@ +GL_GNULIB_OBSTACK_PRINTF = @GL_GNULIB_OBSTACK_PRINTF@ +GL_GNULIB_OBSTACK_PRINTF_POSIX = @GL_GNULIB_OBSTACK_PRINTF_POSIX@ +GL_GNULIB_OPEN = @GL_GNULIB_OPEN@ +GL_GNULIB_OPENAT = @GL_GNULIB_OPENAT@ +GL_GNULIB_OVERRIDES_STRUCT_STAT = @GL_GNULIB_OVERRIDES_STRUCT_STAT@ +GL_GNULIB_PCLOSE = @GL_GNULIB_PCLOSE@ +GL_GNULIB_PERROR = @GL_GNULIB_PERROR@ +GL_GNULIB_PIPE = @GL_GNULIB_PIPE@ +GL_GNULIB_PIPE2 = @GL_GNULIB_PIPE2@ +GL_GNULIB_POPEN = @GL_GNULIB_POPEN@ +GL_GNULIB_POSIX_MEMALIGN = @GL_GNULIB_POSIX_MEMALIGN@ +GL_GNULIB_POSIX_OPENPT = @GL_GNULIB_POSIX_OPENPT@ +GL_GNULIB_PREAD = @GL_GNULIB_PREAD@ +GL_GNULIB_PRINTF = @GL_GNULIB_PRINTF@ +GL_GNULIB_PRINTF_POSIX = @GL_GNULIB_PRINTF_POSIX@ +GL_GNULIB_PSELECT = @GL_GNULIB_PSELECT@ +GL_GNULIB_PTHREAD_COND = @GL_GNULIB_PTHREAD_COND@ +GL_GNULIB_PTHREAD_MUTEX = @GL_GNULIB_PTHREAD_MUTEX@ +GL_GNULIB_PTHREAD_MUTEX_TIMEDLOCK = @GL_GNULIB_PTHREAD_MUTEX_TIMEDLOCK@ +GL_GNULIB_PTHREAD_ONCE = @GL_GNULIB_PTHREAD_ONCE@ +GL_GNULIB_PTHREAD_RWLOCK = @GL_GNULIB_PTHREAD_RWLOCK@ +GL_GNULIB_PTHREAD_SIGMASK = @GL_GNULIB_PTHREAD_SIGMASK@ +GL_GNULIB_PTHREAD_SPIN = @GL_GNULIB_PTHREAD_SPIN@ +GL_GNULIB_PTHREAD_THREAD = @GL_GNULIB_PTHREAD_THREAD@ +GL_GNULIB_PTHREAD_TSS = @GL_GNULIB_PTHREAD_TSS@ +GL_GNULIB_PTSNAME = @GL_GNULIB_PTSNAME@ +GL_GNULIB_PTSNAME_R = @GL_GNULIB_PTSNAME_R@ +GL_GNULIB_PUTC = @GL_GNULIB_PUTC@ +GL_GNULIB_PUTCHAR = @GL_GNULIB_PUTCHAR@ +GL_GNULIB_PUTENV = @GL_GNULIB_PUTENV@ +GL_GNULIB_PUTS = @GL_GNULIB_PUTS@ +GL_GNULIB_PWRITE = @GL_GNULIB_PWRITE@ +GL_GNULIB_QSORT_R = @GL_GNULIB_QSORT_R@ +GL_GNULIB_RAISE = @GL_GNULIB_RAISE@ +GL_GNULIB_RANDOM = @GL_GNULIB_RANDOM@ +GL_GNULIB_RANDOM_R = @GL_GNULIB_RANDOM_R@ +GL_GNULIB_RAWMEMCHR = @GL_GNULIB_RAWMEMCHR@ +GL_GNULIB_READ = @GL_GNULIB_READ@ +GL_GNULIB_READLINK = @GL_GNULIB_READLINK@ +GL_GNULIB_READLINKAT = @GL_GNULIB_READLINKAT@ +GL_GNULIB_REALLOCARRAY = @GL_GNULIB_REALLOCARRAY@ +GL_GNULIB_REALLOC_GNU = @GL_GNULIB_REALLOC_GNU@ +GL_GNULIB_REALLOC_POSIX = @GL_GNULIB_REALLOC_POSIX@ +GL_GNULIB_REALPATH = @GL_GNULIB_REALPATH@ +GL_GNULIB_RECV = @GL_GNULIB_RECV@ +GL_GNULIB_RECVFROM = @GL_GNULIB_RECVFROM@ +GL_GNULIB_REMOVE = @GL_GNULIB_REMOVE@ +GL_GNULIB_RENAME = @GL_GNULIB_RENAME@ +GL_GNULIB_RENAMEAT = @GL_GNULIB_RENAMEAT@ +GL_GNULIB_RMDIR = @GL_GNULIB_RMDIR@ +GL_GNULIB_RPMATCH = @GL_GNULIB_RPMATCH@ +GL_GNULIB_SCANF = @GL_GNULIB_SCANF@ +GL_GNULIB_SCHED_YIELD = @GL_GNULIB_SCHED_YIELD@ +GL_GNULIB_SECURE_GETENV = @GL_GNULIB_SECURE_GETENV@ +GL_GNULIB_SELECT = @GL_GNULIB_SELECT@ +GL_GNULIB_SEND = @GL_GNULIB_SEND@ +GL_GNULIB_SENDTO = @GL_GNULIB_SENDTO@ +GL_GNULIB_SETENV = @GL_GNULIB_SETENV@ +GL_GNULIB_SETHOSTNAME = @GL_GNULIB_SETHOSTNAME@ +GL_GNULIB_SETLOCALE = @GL_GNULIB_SETLOCALE@ +GL_GNULIB_SETLOCALE_NULL = @GL_GNULIB_SETLOCALE_NULL@ +GL_GNULIB_SETSOCKOPT = @GL_GNULIB_SETSOCKOPT@ +GL_GNULIB_SHUTDOWN = @GL_GNULIB_SHUTDOWN@ +GL_GNULIB_SIGABBREV_NP = @GL_GNULIB_SIGABBREV_NP@ +GL_GNULIB_SIGACTION = @GL_GNULIB_SIGACTION@ +GL_GNULIB_SIGDESCR_NP = @GL_GNULIB_SIGDESCR_NP@ +GL_GNULIB_SIGNAL_H_SIGPIPE = @GL_GNULIB_SIGNAL_H_SIGPIPE@ +GL_GNULIB_SIGPROCMASK = @GL_GNULIB_SIGPROCMASK@ +GL_GNULIB_SLEEP = @GL_GNULIB_SLEEP@ +GL_GNULIB_SNPRINTF = @GL_GNULIB_SNPRINTF@ +GL_GNULIB_SOCKET = @GL_GNULIB_SOCKET@ +GL_GNULIB_SPRINTF_POSIX = @GL_GNULIB_SPRINTF_POSIX@ +GL_GNULIB_STAT = @GL_GNULIB_STAT@ +GL_GNULIB_STDIO_H_NONBLOCKING = @GL_GNULIB_STDIO_H_NONBLOCKING@ +GL_GNULIB_STDIO_H_SIGPIPE = @GL_GNULIB_STDIO_H_SIGPIPE@ +GL_GNULIB_STPCPY = @GL_GNULIB_STPCPY@ +GL_GNULIB_STPNCPY = @GL_GNULIB_STPNCPY@ +GL_GNULIB_STRCASESTR = @GL_GNULIB_STRCASESTR@ +GL_GNULIB_STRCHRNUL = @GL_GNULIB_STRCHRNUL@ +GL_GNULIB_STRDUP = @GL_GNULIB_STRDUP@ +GL_GNULIB_STRERROR = @GL_GNULIB_STRERROR@ +GL_GNULIB_STRERRORNAME_NP = @GL_GNULIB_STRERRORNAME_NP@ +GL_GNULIB_STRERROR_R = @GL_GNULIB_STRERROR_R@ +GL_GNULIB_STRFTIME = @GL_GNULIB_STRFTIME@ +GL_GNULIB_STRNCAT = @GL_GNULIB_STRNCAT@ +GL_GNULIB_STRNDUP = @GL_GNULIB_STRNDUP@ +GL_GNULIB_STRNLEN = @GL_GNULIB_STRNLEN@ +GL_GNULIB_STRPBRK = @GL_GNULIB_STRPBRK@ +GL_GNULIB_STRPTIME = @GL_GNULIB_STRPTIME@ +GL_GNULIB_STRSEP = @GL_GNULIB_STRSEP@ +GL_GNULIB_STRSIGNAL = @GL_GNULIB_STRSIGNAL@ +GL_GNULIB_STRSTR = @GL_GNULIB_STRSTR@ +GL_GNULIB_STRTOD = @GL_GNULIB_STRTOD@ +GL_GNULIB_STRTOIMAX = @GL_GNULIB_STRTOIMAX@ +GL_GNULIB_STRTOK_R = @GL_GNULIB_STRTOK_R@ +GL_GNULIB_STRTOL = @GL_GNULIB_STRTOL@ +GL_GNULIB_STRTOLD = @GL_GNULIB_STRTOLD@ +GL_GNULIB_STRTOLL = @GL_GNULIB_STRTOLL@ +GL_GNULIB_STRTOUL = @GL_GNULIB_STRTOUL@ +GL_GNULIB_STRTOULL = @GL_GNULIB_STRTOULL@ +GL_GNULIB_STRTOUMAX = @GL_GNULIB_STRTOUMAX@ +GL_GNULIB_STRVERSCMP = @GL_GNULIB_STRVERSCMP@ +GL_GNULIB_SYMLINK = @GL_GNULIB_SYMLINK@ +GL_GNULIB_SYMLINKAT = @GL_GNULIB_SYMLINKAT@ +GL_GNULIB_SYSTEM_POSIX = @GL_GNULIB_SYSTEM_POSIX@ +GL_GNULIB_TIME = @GL_GNULIB_TIME@ +GL_GNULIB_TIMEGM = @GL_GNULIB_TIMEGM@ +GL_GNULIB_TIMESPEC_GET = @GL_GNULIB_TIMESPEC_GET@ +GL_GNULIB_TIMESPEC_GETRES = @GL_GNULIB_TIMESPEC_GETRES@ +GL_GNULIB_TIME_R = @GL_GNULIB_TIME_R@ +GL_GNULIB_TIME_RZ = @GL_GNULIB_TIME_RZ@ +GL_GNULIB_TMPFILE = @GL_GNULIB_TMPFILE@ +GL_GNULIB_TOWCTRANS = @GL_GNULIB_TOWCTRANS@ +GL_GNULIB_TRUNCATE = @GL_GNULIB_TRUNCATE@ +GL_GNULIB_TTYNAME_R = @GL_GNULIB_TTYNAME_R@ +GL_GNULIB_TZSET = @GL_GNULIB_TZSET@ +GL_GNULIB_UNISTD_H_GETOPT = @GL_GNULIB_UNISTD_H_GETOPT@ +GL_GNULIB_UNISTD_H_NONBLOCKING = @GL_GNULIB_UNISTD_H_NONBLOCKING@ +GL_GNULIB_UNISTD_H_SIGPIPE = @GL_GNULIB_UNISTD_H_SIGPIPE@ +GL_GNULIB_UNLINK = @GL_GNULIB_UNLINK@ +GL_GNULIB_UNLINKAT = @GL_GNULIB_UNLINKAT@ +GL_GNULIB_UNLOCKPT = @GL_GNULIB_UNLOCKPT@ +GL_GNULIB_UNSETENV = @GL_GNULIB_UNSETENV@ +GL_GNULIB_USLEEP = @GL_GNULIB_USLEEP@ +GL_GNULIB_UTIMENSAT = @GL_GNULIB_UTIMENSAT@ +GL_GNULIB_VASPRINTF = @GL_GNULIB_VASPRINTF@ +GL_GNULIB_VDPRINTF = @GL_GNULIB_VDPRINTF@ +GL_GNULIB_VFPRINTF = @GL_GNULIB_VFPRINTF@ +GL_GNULIB_VFPRINTF_POSIX = @GL_GNULIB_VFPRINTF_POSIX@ +GL_GNULIB_VFSCANF = @GL_GNULIB_VFSCANF@ +GL_GNULIB_VPRINTF = @GL_GNULIB_VPRINTF@ +GL_GNULIB_VPRINTF_POSIX = @GL_GNULIB_VPRINTF_POSIX@ +GL_GNULIB_VSCANF = @GL_GNULIB_VSCANF@ +GL_GNULIB_VSNPRINTF = @GL_GNULIB_VSNPRINTF@ +GL_GNULIB_VSPRINTF_POSIX = @GL_GNULIB_VSPRINTF_POSIX@ +GL_GNULIB_WCPCPY = @GL_GNULIB_WCPCPY@ +GL_GNULIB_WCPNCPY = @GL_GNULIB_WCPNCPY@ +GL_GNULIB_WCRTOMB = @GL_GNULIB_WCRTOMB@ +GL_GNULIB_WCSCASECMP = @GL_GNULIB_WCSCASECMP@ +GL_GNULIB_WCSCAT = @GL_GNULIB_WCSCAT@ +GL_GNULIB_WCSCHR = @GL_GNULIB_WCSCHR@ +GL_GNULIB_WCSCMP = @GL_GNULIB_WCSCMP@ +GL_GNULIB_WCSCOLL = @GL_GNULIB_WCSCOLL@ +GL_GNULIB_WCSCPY = @GL_GNULIB_WCSCPY@ +GL_GNULIB_WCSCSPN = @GL_GNULIB_WCSCSPN@ +GL_GNULIB_WCSDUP = @GL_GNULIB_WCSDUP@ +GL_GNULIB_WCSFTIME = @GL_GNULIB_WCSFTIME@ +GL_GNULIB_WCSLEN = @GL_GNULIB_WCSLEN@ +GL_GNULIB_WCSNCASECMP = @GL_GNULIB_WCSNCASECMP@ +GL_GNULIB_WCSNCAT = @GL_GNULIB_WCSNCAT@ +GL_GNULIB_WCSNCMP = @GL_GNULIB_WCSNCMP@ +GL_GNULIB_WCSNCPY = @GL_GNULIB_WCSNCPY@ +GL_GNULIB_WCSNLEN = @GL_GNULIB_WCSNLEN@ +GL_GNULIB_WCSNRTOMBS = @GL_GNULIB_WCSNRTOMBS@ +GL_GNULIB_WCSPBRK = @GL_GNULIB_WCSPBRK@ +GL_GNULIB_WCSRCHR = @GL_GNULIB_WCSRCHR@ +GL_GNULIB_WCSRTOMBS = @GL_GNULIB_WCSRTOMBS@ +GL_GNULIB_WCSSPN = @GL_GNULIB_WCSSPN@ +GL_GNULIB_WCSSTR = @GL_GNULIB_WCSSTR@ +GL_GNULIB_WCSTOK = @GL_GNULIB_WCSTOK@ +GL_GNULIB_WCSWIDTH = @GL_GNULIB_WCSWIDTH@ +GL_GNULIB_WCSXFRM = @GL_GNULIB_WCSXFRM@ +GL_GNULIB_WCTOB = @GL_GNULIB_WCTOB@ +GL_GNULIB_WCTOMB = @GL_GNULIB_WCTOMB@ +GL_GNULIB_WCTRANS = @GL_GNULIB_WCTRANS@ +GL_GNULIB_WCTYPE = @GL_GNULIB_WCTYPE@ +GL_GNULIB_WCWIDTH = @GL_GNULIB_WCWIDTH@ +GL_GNULIB_WMEMCHR = @GL_GNULIB_WMEMCHR@ +GL_GNULIB_WMEMCMP = @GL_GNULIB_WMEMCMP@ +GL_GNULIB_WMEMCPY = @GL_GNULIB_WMEMCPY@ +GL_GNULIB_WMEMMOVE = @GL_GNULIB_WMEMMOVE@ +GL_GNULIB_WMEMPCPY = @GL_GNULIB_WMEMPCPY@ +GL_GNULIB_WMEMSET = @GL_GNULIB_WMEMSET@ +GL_GNULIB_WRITE = @GL_GNULIB_WRITE@ +GL_GNULIB__EXIT = @GL_GNULIB__EXIT@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNULIBHEADERS_OVERRIDE_WINT_T = @GNULIBHEADERS_OVERRIDE_WINT_T@ +GNULIB_GETTIMEOFDAY = @GNULIB_GETTIMEOFDAY@ +GREP = @GREP@ +HARD_LOCALE_LIB = @HARD_LOCALE_LIB@ +HAVE_ACCEPT4 = @HAVE_ACCEPT4@ +HAVE_ALIGNED_ALLOC = @HAVE_ALIGNED_ALLOC@ +HAVE_ALLOCA_H = @HAVE_ALLOCA_H@ +HAVE_ARPA_INET_H = @HAVE_ARPA_INET_H@ +HAVE_ATOLL = @HAVE_ATOLL@ +HAVE_BTOWC = @HAVE_BTOWC@ +HAVE_C99_STDINT_H = @HAVE_C99_STDINT_H@ +HAVE_CANONICALIZE_FILE_NAME = @HAVE_CANONICALIZE_FILE_NAME@ +HAVE_CHOWN = @HAVE_CHOWN@ +HAVE_COPY_FILE_RANGE = @HAVE_COPY_FILE_RANGE@ +HAVE_CRTDEFS_H = @HAVE_CRTDEFS_H@ +HAVE_DECL_ECVT = @HAVE_DECL_ECVT@ +HAVE_DECL_ENVIRON = @HAVE_DECL_ENVIRON@ +HAVE_DECL_EXECVPE = @HAVE_DECL_EXECVPE@ +HAVE_DECL_FCHDIR = @HAVE_DECL_FCHDIR@ +HAVE_DECL_FCLOSEALL = @HAVE_DECL_FCLOSEALL@ +HAVE_DECL_FCVT = @HAVE_DECL_FCVT@ +HAVE_DECL_FDATASYNC = @HAVE_DECL_FDATASYNC@ +HAVE_DECL_FPURGE = @HAVE_DECL_FPURGE@ +HAVE_DECL_FSEEKO = @HAVE_DECL_FSEEKO@ +HAVE_DECL_FTELLO = @HAVE_DECL_FTELLO@ +HAVE_DECL_GCVT = @HAVE_DECL_GCVT@ +HAVE_DECL_GETDELIM = @HAVE_DECL_GETDELIM@ +HAVE_DECL_GETDOMAINNAME = @HAVE_DECL_GETDOMAINNAME@ +HAVE_DECL_GETLINE = @HAVE_DECL_GETLINE@ +HAVE_DECL_GETLOADAVG = @HAVE_DECL_GETLOADAVG@ +HAVE_DECL_GETLOGIN = @HAVE_DECL_GETLOGIN@ +HAVE_DECL_GETLOGIN_R = @HAVE_DECL_GETLOGIN_R@ +HAVE_DECL_GETPAGESIZE = @HAVE_DECL_GETPAGESIZE@ +HAVE_DECL_GETUSERSHELL = @HAVE_DECL_GETUSERSHELL@ +HAVE_DECL_GETW = @HAVE_DECL_GETW@ +HAVE_DECL_IMAXABS = @HAVE_DECL_IMAXABS@ +HAVE_DECL_IMAXDIV = @HAVE_DECL_IMAXDIV@ +HAVE_DECL_INET_NTOP = @HAVE_DECL_INET_NTOP@ +HAVE_DECL_INET_PTON = @HAVE_DECL_INET_PTON@ +HAVE_DECL_INITSTATE = @HAVE_DECL_INITSTATE@ +HAVE_DECL_LOCALTIME_R = @HAVE_DECL_LOCALTIME_R@ +HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@ +HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@ +HAVE_DECL_OBSTACK_PRINTF = @HAVE_DECL_OBSTACK_PRINTF@ +HAVE_DECL_PUTW = @HAVE_DECL_PUTW@ +HAVE_DECL_SETENV = @HAVE_DECL_SETENV@ +HAVE_DECL_SETHOSTNAME = @HAVE_DECL_SETHOSTNAME@ +HAVE_DECL_SETSTATE = @HAVE_DECL_SETSTATE@ +HAVE_DECL_SNPRINTF = @HAVE_DECL_SNPRINTF@ +HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@ +HAVE_DECL_STRERROR_R = @HAVE_DECL_STRERROR_R@ +HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@ +HAVE_DECL_STRNLEN = @HAVE_DECL_STRNLEN@ +HAVE_DECL_STRSIGNAL = @HAVE_DECL_STRSIGNAL@ +HAVE_DECL_STRTOIMAX = @HAVE_DECL_STRTOIMAX@ +HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@ +HAVE_DECL_STRTOUMAX = @HAVE_DECL_STRTOUMAX@ +HAVE_DECL_TRUNCATE = @HAVE_DECL_TRUNCATE@ +HAVE_DECL_TTYNAME_R = @HAVE_DECL_TTYNAME_R@ +HAVE_DECL_UNSETENV = @HAVE_DECL_UNSETENV@ +HAVE_DECL_VSNPRINTF = @HAVE_DECL_VSNPRINTF@ +HAVE_DECL_WCSDUP = @HAVE_DECL_WCSDUP@ +HAVE_DECL_WCTOB = @HAVE_DECL_WCTOB@ +HAVE_DECL_WCWIDTH = @HAVE_DECL_WCWIDTH@ +HAVE_DPRINTF = @HAVE_DPRINTF@ +HAVE_DUP3 = @HAVE_DUP3@ +HAVE_DUPLOCALE = @HAVE_DUPLOCALE@ +HAVE_ERROR = @HAVE_ERROR@ +HAVE_ERROR_AT_LINE = @HAVE_ERROR_AT_LINE@ +HAVE_ERROR_H = @HAVE_ERROR_H@ +HAVE_EUIDACCESS = @HAVE_EUIDACCESS@ +HAVE_EXECVPE = @HAVE_EXECVPE@ +HAVE_EXPLICIT_BZERO = @HAVE_EXPLICIT_BZERO@ +HAVE_FACCESSAT = @HAVE_FACCESSAT@ +HAVE_FCHDIR = @HAVE_FCHDIR@ +HAVE_FCHMODAT = @HAVE_FCHMODAT@ +HAVE_FCHOWNAT = @HAVE_FCHOWNAT@ +HAVE_FCNTL = @HAVE_FCNTL@ +HAVE_FDATASYNC = @HAVE_FDATASYNC@ +HAVE_FEATURES_H = @HAVE_FEATURES_H@ +HAVE_FFSL = @HAVE_FFSL@ +HAVE_FFSLL = @HAVE_FFSLL@ +HAVE_FREELOCALE = @HAVE_FREELOCALE@ +HAVE_FSEEKO = @HAVE_FSEEKO@ +HAVE_FSTATAT = @HAVE_FSTATAT@ +HAVE_FSYNC = @HAVE_FSYNC@ +HAVE_FTELLO = @HAVE_FTELLO@ +HAVE_FTRUNCATE = @HAVE_FTRUNCATE@ +HAVE_FUTIMENS = @HAVE_FUTIMENS@ +HAVE_GETDTABLESIZE = @HAVE_GETDTABLESIZE@ +HAVE_GETENTROPY = @HAVE_GETENTROPY@ +HAVE_GETGROUPS = @HAVE_GETGROUPS@ +HAVE_GETHOSTNAME = @HAVE_GETHOSTNAME@ +HAVE_GETLOGIN = @HAVE_GETLOGIN@ +HAVE_GETOPT_H = @HAVE_GETOPT_H@ +HAVE_GETPAGESIZE = @HAVE_GETPAGESIZE@ +HAVE_GETPASS = @HAVE_GETPASS@ +HAVE_GETPROGNAME = @HAVE_GETPROGNAME@ +HAVE_GETRANDOM = @HAVE_GETRANDOM@ +HAVE_GETSUBOPT = @HAVE_GETSUBOPT@ +HAVE_GETTIMEOFDAY = @HAVE_GETTIMEOFDAY@ +HAVE_GETUMASK = @HAVE_GETUMASK@ +HAVE_GRANTPT = @HAVE_GRANTPT@ +HAVE_GROUP_MEMBER = @HAVE_GROUP_MEMBER@ +HAVE_IMAXABS = @HAVE_IMAXABS@ +HAVE_IMAXDIV = @HAVE_IMAXDIV@ +HAVE_IMAXDIV_T = @HAVE_IMAXDIV_T@ +HAVE_INITSTATE = @HAVE_INITSTATE@ +HAVE_INTTYPES_H = @HAVE_INTTYPES_H@ +HAVE_ISBLANK = @HAVE_ISBLANK@ +HAVE_ISWBLANK = @HAVE_ISWBLANK@ +HAVE_ISWCNTRL = @HAVE_ISWCNTRL@ +HAVE_LANGINFO_ALTMON = @HAVE_LANGINFO_ALTMON@ +HAVE_LANGINFO_CODESET = @HAVE_LANGINFO_CODESET@ +HAVE_LANGINFO_ERA = @HAVE_LANGINFO_ERA@ +HAVE_LANGINFO_H = @HAVE_LANGINFO_H@ +HAVE_LANGINFO_T_FMT_AMPM = @HAVE_LANGINFO_T_FMT_AMPM@ +HAVE_LANGINFO_YESEXPR = @HAVE_LANGINFO_YESEXPR@ +HAVE_LCHMOD = @HAVE_LCHMOD@ +HAVE_LCHOWN = @HAVE_LCHOWN@ +HAVE_LINK = @HAVE_LINK@ +HAVE_LINKAT = @HAVE_LINKAT@ +HAVE_LSTAT = @HAVE_LSTAT@ +HAVE_MAX_ALIGN_T = @HAVE_MAX_ALIGN_T@ +HAVE_MBRLEN = @HAVE_MBRLEN@ +HAVE_MBRTOWC = @HAVE_MBRTOWC@ +HAVE_MBSINIT = @HAVE_MBSINIT@ +HAVE_MBSLEN = @HAVE_MBSLEN@ +HAVE_MBSNRTOWCS = @HAVE_MBSNRTOWCS@ +HAVE_MBSRTOWCS = @HAVE_MBSRTOWCS@ +HAVE_MBTOWC = @HAVE_MBTOWC@ +HAVE_MEMPCPY = @HAVE_MEMPCPY@ +HAVE_MEMSET_EXPLICIT = @HAVE_MEMSET_EXPLICIT@ +HAVE_MKDIRAT = @HAVE_MKDIRAT@ +HAVE_MKDTEMP = @HAVE_MKDTEMP@ +HAVE_MKFIFO = @HAVE_MKFIFO@ +HAVE_MKFIFOAT = @HAVE_MKFIFOAT@ +HAVE_MKNOD = @HAVE_MKNOD@ +HAVE_MKNODAT = @HAVE_MKNODAT@ +HAVE_MKOSTEMP = @HAVE_MKOSTEMP@ +HAVE_MKOSTEMPS = @HAVE_MKOSTEMPS@ +HAVE_MKSTEMP = @HAVE_MKSTEMP@ +HAVE_MKSTEMPS = @HAVE_MKSTEMPS@ +HAVE_MSVC_INVALID_PARAMETER_HANDLER = @HAVE_MSVC_INVALID_PARAMETER_HANDLER@ +HAVE_NANOSLEEP = @HAVE_NANOSLEEP@ +HAVE_NETINET_IN_H = @HAVE_NETINET_IN_H@ +HAVE_NEWLOCALE = @HAVE_NEWLOCALE@ +HAVE_NL_LANGINFO = @HAVE_NL_LANGINFO@ +HAVE_OPENAT = @HAVE_OPENAT@ +HAVE_OS_H = @HAVE_OS_H@ +HAVE_PCLOSE = @HAVE_PCLOSE@ +HAVE_PIPE = @HAVE_PIPE@ +HAVE_PIPE2 = @HAVE_PIPE2@ +HAVE_POPEN = @HAVE_POPEN@ +HAVE_POSIX_MEMALIGN = @HAVE_POSIX_MEMALIGN@ +HAVE_POSIX_OPENPT = @HAVE_POSIX_OPENPT@ +HAVE_POSIX_SIGNALBLOCKING = @HAVE_POSIX_SIGNALBLOCKING@ +HAVE_PREAD = @HAVE_PREAD@ +HAVE_PSELECT = @HAVE_PSELECT@ +HAVE_PTHREAD_ATTR_DESTROY = @HAVE_PTHREAD_ATTR_DESTROY@ +HAVE_PTHREAD_ATTR_GETDETACHSTATE = @HAVE_PTHREAD_ATTR_GETDETACHSTATE@ +HAVE_PTHREAD_ATTR_INIT = @HAVE_PTHREAD_ATTR_INIT@ +HAVE_PTHREAD_ATTR_SETDETACHSTATE = @HAVE_PTHREAD_ATTR_SETDETACHSTATE@ +HAVE_PTHREAD_CONDATTR_DESTROY = @HAVE_PTHREAD_CONDATTR_DESTROY@ +HAVE_PTHREAD_CONDATTR_INIT = @HAVE_PTHREAD_CONDATTR_INIT@ +HAVE_PTHREAD_COND_BROADCAST = @HAVE_PTHREAD_COND_BROADCAST@ +HAVE_PTHREAD_COND_DESTROY = @HAVE_PTHREAD_COND_DESTROY@ +HAVE_PTHREAD_COND_INIT = @HAVE_PTHREAD_COND_INIT@ +HAVE_PTHREAD_COND_SIGNAL = @HAVE_PTHREAD_COND_SIGNAL@ +HAVE_PTHREAD_COND_TIMEDWAIT = @HAVE_PTHREAD_COND_TIMEDWAIT@ +HAVE_PTHREAD_COND_WAIT = @HAVE_PTHREAD_COND_WAIT@ +HAVE_PTHREAD_CREATE = @HAVE_PTHREAD_CREATE@ +HAVE_PTHREAD_CREATE_DETACHED = @HAVE_PTHREAD_CREATE_DETACHED@ +HAVE_PTHREAD_DETACH = @HAVE_PTHREAD_DETACH@ +HAVE_PTHREAD_EQUAL = @HAVE_PTHREAD_EQUAL@ +HAVE_PTHREAD_EXIT = @HAVE_PTHREAD_EXIT@ +HAVE_PTHREAD_GETSPECIFIC = @HAVE_PTHREAD_GETSPECIFIC@ +HAVE_PTHREAD_H = @HAVE_PTHREAD_H@ +HAVE_PTHREAD_JOIN = @HAVE_PTHREAD_JOIN@ +HAVE_PTHREAD_KEY_CREATE = @HAVE_PTHREAD_KEY_CREATE@ +HAVE_PTHREAD_KEY_DELETE = @HAVE_PTHREAD_KEY_DELETE@ +HAVE_PTHREAD_MUTEXATTR_DESTROY = @HAVE_PTHREAD_MUTEXATTR_DESTROY@ +HAVE_PTHREAD_MUTEXATTR_GETROBUST = @HAVE_PTHREAD_MUTEXATTR_GETROBUST@ +HAVE_PTHREAD_MUTEXATTR_GETTYPE = @HAVE_PTHREAD_MUTEXATTR_GETTYPE@ +HAVE_PTHREAD_MUTEXATTR_INIT = @HAVE_PTHREAD_MUTEXATTR_INIT@ +HAVE_PTHREAD_MUTEXATTR_SETROBUST = @HAVE_PTHREAD_MUTEXATTR_SETROBUST@ +HAVE_PTHREAD_MUTEXATTR_SETTYPE = @HAVE_PTHREAD_MUTEXATTR_SETTYPE@ +HAVE_PTHREAD_MUTEX_DESTROY = @HAVE_PTHREAD_MUTEX_DESTROY@ +HAVE_PTHREAD_MUTEX_INIT = @HAVE_PTHREAD_MUTEX_INIT@ +HAVE_PTHREAD_MUTEX_LOCK = @HAVE_PTHREAD_MUTEX_LOCK@ +HAVE_PTHREAD_MUTEX_RECURSIVE = @HAVE_PTHREAD_MUTEX_RECURSIVE@ +HAVE_PTHREAD_MUTEX_ROBUST = @HAVE_PTHREAD_MUTEX_ROBUST@ +HAVE_PTHREAD_MUTEX_TIMEDLOCK = @HAVE_PTHREAD_MUTEX_TIMEDLOCK@ +HAVE_PTHREAD_MUTEX_TRYLOCK = @HAVE_PTHREAD_MUTEX_TRYLOCK@ +HAVE_PTHREAD_MUTEX_UNLOCK = @HAVE_PTHREAD_MUTEX_UNLOCK@ +HAVE_PTHREAD_ONCE = @HAVE_PTHREAD_ONCE@ +HAVE_PTHREAD_PROCESS_SHARED = @HAVE_PTHREAD_PROCESS_SHARED@ +HAVE_PTHREAD_RWLOCKATTR_DESTROY = @HAVE_PTHREAD_RWLOCKATTR_DESTROY@ +HAVE_PTHREAD_RWLOCKATTR_INIT = @HAVE_PTHREAD_RWLOCKATTR_INIT@ +HAVE_PTHREAD_RWLOCK_DESTROY = @HAVE_PTHREAD_RWLOCK_DESTROY@ +HAVE_PTHREAD_RWLOCK_INIT = @HAVE_PTHREAD_RWLOCK_INIT@ +HAVE_PTHREAD_RWLOCK_RDLOCK = @HAVE_PTHREAD_RWLOCK_RDLOCK@ +HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK = @HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK@ +HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK = @HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK@ +HAVE_PTHREAD_RWLOCK_TRYRDLOCK = @HAVE_PTHREAD_RWLOCK_TRYRDLOCK@ +HAVE_PTHREAD_RWLOCK_TRYWRLOCK = @HAVE_PTHREAD_RWLOCK_TRYWRLOCK@ +HAVE_PTHREAD_RWLOCK_UNLOCK = @HAVE_PTHREAD_RWLOCK_UNLOCK@ +HAVE_PTHREAD_RWLOCK_WRLOCK = @HAVE_PTHREAD_RWLOCK_WRLOCK@ +HAVE_PTHREAD_SELF = @HAVE_PTHREAD_SELF@ +HAVE_PTHREAD_SETSPECIFIC = @HAVE_PTHREAD_SETSPECIFIC@ +HAVE_PTHREAD_SIGMASK = @HAVE_PTHREAD_SIGMASK@ +HAVE_PTHREAD_SPINLOCK_T = @HAVE_PTHREAD_SPINLOCK_T@ +HAVE_PTHREAD_SPIN_DESTROY = @HAVE_PTHREAD_SPIN_DESTROY@ +HAVE_PTHREAD_SPIN_INIT = @HAVE_PTHREAD_SPIN_INIT@ +HAVE_PTHREAD_SPIN_LOCK = @HAVE_PTHREAD_SPIN_LOCK@ +HAVE_PTHREAD_SPIN_TRYLOCK = @HAVE_PTHREAD_SPIN_TRYLOCK@ +HAVE_PTHREAD_SPIN_UNLOCK = @HAVE_PTHREAD_SPIN_UNLOCK@ +HAVE_PTHREAD_T = @HAVE_PTHREAD_T@ +HAVE_PTSNAME = @HAVE_PTSNAME@ +HAVE_PTSNAME_R = @HAVE_PTSNAME_R@ +HAVE_PWRITE = @HAVE_PWRITE@ +HAVE_QSORT_R = @HAVE_QSORT_R@ +HAVE_RAISE = @HAVE_RAISE@ +HAVE_RANDOM = @HAVE_RANDOM@ +HAVE_RANDOM_H = @HAVE_RANDOM_H@ +HAVE_RANDOM_R = @HAVE_RANDOM_R@ +HAVE_RAWMEMCHR = @HAVE_RAWMEMCHR@ +HAVE_READLINK = @HAVE_READLINK@ +HAVE_READLINKAT = @HAVE_READLINKAT@ +HAVE_REALLOCARRAY = @HAVE_REALLOCARRAY@ +HAVE_REALPATH = @HAVE_REALPATH@ +HAVE_RENAMEAT = @HAVE_RENAMEAT@ +HAVE_RPMATCH = @HAVE_RPMATCH@ +HAVE_SA_FAMILY_T = @HAVE_SA_FAMILY_T@ +HAVE_SCHED_H = @HAVE_SCHED_H@ +HAVE_SCHED_YIELD = @HAVE_SCHED_YIELD@ +HAVE_SECURE_GETENV = @HAVE_SECURE_GETENV@ +HAVE_SETENV = @HAVE_SETENV@ +HAVE_SETHOSTNAME = @HAVE_SETHOSTNAME@ +HAVE_SETSTATE = @HAVE_SETSTATE@ +HAVE_SIGABBREV_NP = @HAVE_SIGABBREV_NP@ +HAVE_SIGACTION = @HAVE_SIGACTION@ +HAVE_SIGDESCR_NP = @HAVE_SIGDESCR_NP@ +HAVE_SIGHANDLER_T = @HAVE_SIGHANDLER_T@ +HAVE_SIGINFO_T = @HAVE_SIGINFO_T@ +HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@ +HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@ +HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@ +HAVE_SIGSET_T = @HAVE_SIGSET_T@ +HAVE_SLEEP = @HAVE_SLEEP@ +HAVE_STDINT_H = @HAVE_STDINT_H@ +HAVE_STPCPY = @HAVE_STPCPY@ +HAVE_STPNCPY = @HAVE_STPNCPY@ +HAVE_STRCASESTR = @HAVE_STRCASESTR@ +HAVE_STRCHRNUL = @HAVE_STRCHRNUL@ +HAVE_STRERRORNAME_NP = @HAVE_STRERRORNAME_NP@ +HAVE_STRPBRK = @HAVE_STRPBRK@ +HAVE_STRPTIME = @HAVE_STRPTIME@ +HAVE_STRSEP = @HAVE_STRSEP@ +HAVE_STRTOD = @HAVE_STRTOD@ +HAVE_STRTOL = @HAVE_STRTOL@ +HAVE_STRTOLD = @HAVE_STRTOLD@ +HAVE_STRTOLL = @HAVE_STRTOLL@ +HAVE_STRTOUL = @HAVE_STRTOUL@ +HAVE_STRTOULL = @HAVE_STRTOULL@ +HAVE_STRUCT_RANDOM_DATA = @HAVE_STRUCT_RANDOM_DATA@ +HAVE_STRUCT_SCHED_PARAM = @HAVE_STRUCT_SCHED_PARAM@ +HAVE_STRUCT_SIGACTION_SA_SIGACTION = @HAVE_STRUCT_SIGACTION_SA_SIGACTION@ +HAVE_STRUCT_SOCKADDR_STORAGE = @HAVE_STRUCT_SOCKADDR_STORAGE@ +HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY = @HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY@ +HAVE_STRUCT_TIMEVAL = @HAVE_STRUCT_TIMEVAL@ +HAVE_STRVERSCMP = @HAVE_STRVERSCMP@ +HAVE_SYMLINK = @HAVE_SYMLINK@ +HAVE_SYMLINKAT = @HAVE_SYMLINKAT@ +HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@ +HAVE_SYS_CDEFS_H = @HAVE_SYS_CDEFS_H@ +HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@ +HAVE_SYS_IOCTL_H = @HAVE_SYS_IOCTL_H@ +HAVE_SYS_LOADAVG_H = @HAVE_SYS_LOADAVG_H@ +HAVE_SYS_PARAM_H = @HAVE_SYS_PARAM_H@ +HAVE_SYS_RANDOM_H = @HAVE_SYS_RANDOM_H@ +HAVE_SYS_SELECT_H = @HAVE_SYS_SELECT_H@ +HAVE_SYS_SOCKET_H = @HAVE_SYS_SOCKET_H@ +HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@ +HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@ +HAVE_SYS_UIO_H = @HAVE_SYS_UIO_H@ +HAVE_TIMEGM = @HAVE_TIMEGM@ +HAVE_TIMESPEC_GET = @HAVE_TIMESPEC_GET@ +HAVE_TIMESPEC_GETRES = @HAVE_TIMESPEC_GETRES@ +HAVE_TIMEZONE_T = @HAVE_TIMEZONE_T@ +HAVE_TYPE_VOLATILE_SIG_ATOMIC_T = @HAVE_TYPE_VOLATILE_SIG_ATOMIC_T@ +HAVE_UNISTD_H = @HAVE_UNISTD_H@ +HAVE_UNLINKAT = @HAVE_UNLINKAT@ +HAVE_UNLOCKPT = @HAVE_UNLOCKPT@ +HAVE_USLEEP = @HAVE_USLEEP@ +HAVE_UTIMENSAT = @HAVE_UTIMENSAT@ +HAVE_VASPRINTF = @HAVE_VASPRINTF@ +HAVE_VDPRINTF = @HAVE_VDPRINTF@ +HAVE_VISIBILITY = @HAVE_VISIBILITY@ +HAVE_WCHAR_H = @HAVE_WCHAR_H@ +HAVE_WCHAR_T = @HAVE_WCHAR_T@ +HAVE_WCPCPY = @HAVE_WCPCPY@ +HAVE_WCPNCPY = @HAVE_WCPNCPY@ +HAVE_WCRTOMB = @HAVE_WCRTOMB@ +HAVE_WCSCASECMP = @HAVE_WCSCASECMP@ +HAVE_WCSCAT = @HAVE_WCSCAT@ +HAVE_WCSCHR = @HAVE_WCSCHR@ +HAVE_WCSCMP = @HAVE_WCSCMP@ +HAVE_WCSCOLL = @HAVE_WCSCOLL@ +HAVE_WCSCPY = @HAVE_WCSCPY@ +HAVE_WCSCSPN = @HAVE_WCSCSPN@ +HAVE_WCSDUP = @HAVE_WCSDUP@ +HAVE_WCSFTIME = @HAVE_WCSFTIME@ +HAVE_WCSLEN = @HAVE_WCSLEN@ +HAVE_WCSNCASECMP = @HAVE_WCSNCASECMP@ +HAVE_WCSNCAT = @HAVE_WCSNCAT@ +HAVE_WCSNCMP = @HAVE_WCSNCMP@ +HAVE_WCSNCPY = @HAVE_WCSNCPY@ +HAVE_WCSNLEN = @HAVE_WCSNLEN@ +HAVE_WCSNRTOMBS = @HAVE_WCSNRTOMBS@ +HAVE_WCSPBRK = @HAVE_WCSPBRK@ +HAVE_WCSRCHR = @HAVE_WCSRCHR@ +HAVE_WCSRTOMBS = @HAVE_WCSRTOMBS@ +HAVE_WCSSPN = @HAVE_WCSSPN@ +HAVE_WCSSTR = @HAVE_WCSSTR@ +HAVE_WCSTOK = @HAVE_WCSTOK@ +HAVE_WCSWIDTH = @HAVE_WCSWIDTH@ +HAVE_WCSXFRM = @HAVE_WCSXFRM@ +HAVE_WCTRANS_T = @HAVE_WCTRANS_T@ +HAVE_WCTYPE_H = @HAVE_WCTYPE_H@ +HAVE_WCTYPE_T = @HAVE_WCTYPE_T@ +HAVE_WINSOCK2_H = @HAVE_WINSOCK2_H@ +HAVE_WINT_T = @HAVE_WINT_T@ +HAVE_WMEMCHR = @HAVE_WMEMCHR@ +HAVE_WMEMCMP = @HAVE_WMEMCMP@ +HAVE_WMEMCPY = @HAVE_WMEMCPY@ +HAVE_WMEMMOVE = @HAVE_WMEMMOVE@ +HAVE_WMEMPCPY = @HAVE_WMEMPCPY@ +HAVE_WMEMSET = @HAVE_WMEMSET@ +HAVE_WS2TCPIP_H = @HAVE_WS2TCPIP_H@ +HAVE_XLOCALE_H = @HAVE_XLOCALE_H@ +HAVE__EXIT = @HAVE__EXIT@ +IGNORE_UNUSED_LIBRARIES_CFLAGS = @IGNORE_UNUSED_LIBRARIES_CFLAGS@ +INCLUDE_NEXT = @INCLUDE_NEXT@ +INCLUDE_NEXT_AS_FIRST_DIRECTIVE = @INCLUDE_NEXT_AS_FIRST_DIRECTIVE@ +INET_PTON_LIB = @INET_PTON_LIB@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INT32_MAX_LT_INTMAX_MAX = @INT32_MAX_LT_INTMAX_MAX@ +INT64_MAX_EQ_LONG_MAX = @INT64_MAX_EQ_LONG_MAX@ +INTLINCS = @INTLINCS@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBMULTITHREAD = @LIBMULTITHREAD@ +LIBOBJS = @LIBOBJS@ +LIBPMULTITHREAD = @LIBPMULTITHREAD@ +LIBPTHREAD = @LIBPTHREAD@ +LIBS = @LIBS@ +LIBSOCKET = @LIBSOCKET@ +LIBSTDTHREAD = @LIBSTDTHREAD@ +LIBTESTS_LIBDEPS = @LIBTESTS_LIBDEPS@ +LIBTHREAD = @LIBTHREAD@ +LIBTOOL = @LIBTOOL@ +LIB_BLKID = @LIB_BLKID@ +LIB_CLOCK_GETTIME = @LIB_CLOCK_GETTIME@ +LIB_GETRANDOM = @LIB_GETRANDOM@ +LIB_HARD_LOCALE = @LIB_HARD_LOCALE@ +LIB_MBRTOWC = @LIB_MBRTOWC@ +LIB_NANOSLEEP = @LIB_NANOSLEEP@ +LIB_NL_LANGINFO = @LIB_NL_LANGINFO@ +LIB_PTHREAD = @LIB_PTHREAD@ +LIB_PTHREAD_SIGMASK = @LIB_PTHREAD_SIGMASK@ +LIB_SCHED_YIELD = @LIB_SCHED_YIELD@ +LIB_SELECT = @LIB_SELECT@ +LIB_SEMAPHORE = @LIB_SEMAPHORE@ +LIB_SETLOCALE = @LIB_SETLOCALE@ +LIB_SETLOCALE_NULL = @LIB_SETLOCALE_NULL@ +LIMITS_H = @LIMITS_H@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOCALCHARSET_TESTS_ENVIRONMENT = @LOCALCHARSET_TESTS_ENVIRONMENT@ +LOCALENAME_ENHANCE_LOCALE_FUNCS = @LOCALENAME_ENHANCE_LOCALE_FUNCS@ +LOCALE_FR = @LOCALE_FR@ +LOCALE_FR_UTF8 = @LOCALE_FR_UTF8@ +LOCALE_JA = @LOCALE_JA@ +LOCALE_TR_UTF8 = @LOCALE_TR_UTF8@ +LOCALE_ZH_CN = @LOCALE_ZH_CN@ +LTALLOCA = @LTALLOCA@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBMULTITHREAD = @LTLIBMULTITHREAD@ +LTLIBOBJS = @LTLIBOBJS@ +LTLIBTHREAD = @LTLIBTHREAD@ +LT_AGE = @LT_AGE@ +LT_CURRENT = @LT_CURRENT@ +LT_RELEASE = @LT_RELEASE@ +LT_REVISION = @LT_REVISION@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MBRTOWC_LIB = @MBRTOWC_LIB@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NANOSLEEP_LIB = @NANOSLEEP_LIB@ +NETINET_IN_H = @NETINET_IN_H@ +NEXT_ARPA_INET_H = @NEXT_ARPA_INET_H@ +NEXT_ASSERT_H = @NEXT_ASSERT_H@ +NEXT_AS_FIRST_DIRECTIVE_ARPA_INET_H = @NEXT_AS_FIRST_DIRECTIVE_ARPA_INET_H@ +NEXT_AS_FIRST_DIRECTIVE_ASSERT_H = @NEXT_AS_FIRST_DIRECTIVE_ASSERT_H@ +NEXT_AS_FIRST_DIRECTIVE_CTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_CTYPE_H@ +NEXT_AS_FIRST_DIRECTIVE_ERRNO_H = @NEXT_AS_FIRST_DIRECTIVE_ERRNO_H@ +NEXT_AS_FIRST_DIRECTIVE_ERROR_H = @NEXT_AS_FIRST_DIRECTIVE_ERROR_H@ +NEXT_AS_FIRST_DIRECTIVE_FCNTL_H = @NEXT_AS_FIRST_DIRECTIVE_FCNTL_H@ +NEXT_AS_FIRST_DIRECTIVE_GETOPT_H = @NEXT_AS_FIRST_DIRECTIVE_GETOPT_H@ +NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H = @NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H@ +NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H = @NEXT_AS_FIRST_DIRECTIVE_LANGINFO_H@ +NEXT_AS_FIRST_DIRECTIVE_LIMITS_H = @NEXT_AS_FIRST_DIRECTIVE_LIMITS_H@ +NEXT_AS_FIRST_DIRECTIVE_LOCALE_H = @NEXT_AS_FIRST_DIRECTIVE_LOCALE_H@ +NEXT_AS_FIRST_DIRECTIVE_NETINET_IN_H = @NEXT_AS_FIRST_DIRECTIVE_NETINET_IN_H@ +NEXT_AS_FIRST_DIRECTIVE_PTHREAD_H = @NEXT_AS_FIRST_DIRECTIVE_PTHREAD_H@ +NEXT_AS_FIRST_DIRECTIVE_SCHED_H = @NEXT_AS_FIRST_DIRECTIVE_SCHED_H@ +NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H = @NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H@ +NEXT_AS_FIRST_DIRECTIVE_STDARG_H = @NEXT_AS_FIRST_DIRECTIVE_STDARG_H@ +NEXT_AS_FIRST_DIRECTIVE_STDDEF_H = @NEXT_AS_FIRST_DIRECTIVE_STDDEF_H@ +NEXT_AS_FIRST_DIRECTIVE_STDINT_H = @NEXT_AS_FIRST_DIRECTIVE_STDINT_H@ +NEXT_AS_FIRST_DIRECTIVE_STDIO_H = @NEXT_AS_FIRST_DIRECTIVE_STDIO_H@ +NEXT_AS_FIRST_DIRECTIVE_STDLIB_H = @NEXT_AS_FIRST_DIRECTIVE_STDLIB_H@ +NEXT_AS_FIRST_DIRECTIVE_STRING_H = @NEXT_AS_FIRST_DIRECTIVE_STRING_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_IOCTL_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_IOCTL_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_RANDOM_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_RANDOM_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SELECT_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_SOCKET_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_SOCKET_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_UIO_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_UIO_H@ +NEXT_AS_FIRST_DIRECTIVE_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_TIME_H@ +NEXT_AS_FIRST_DIRECTIVE_UNISTD_H = @NEXT_AS_FIRST_DIRECTIVE_UNISTD_H@ +NEXT_AS_FIRST_DIRECTIVE_WCHAR_H = @NEXT_AS_FIRST_DIRECTIVE_WCHAR_H@ +NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H@ +NEXT_CTYPE_H = @NEXT_CTYPE_H@ +NEXT_ERRNO_H = @NEXT_ERRNO_H@ +NEXT_ERROR_H = @NEXT_ERROR_H@ +NEXT_FCNTL_H = @NEXT_FCNTL_H@ +NEXT_GETOPT_H = @NEXT_GETOPT_H@ +NEXT_INTTYPES_H = @NEXT_INTTYPES_H@ +NEXT_LANGINFO_H = @NEXT_LANGINFO_H@ +NEXT_LIMITS_H = @NEXT_LIMITS_H@ +NEXT_LOCALE_H = @NEXT_LOCALE_H@ +NEXT_NETINET_IN_H = @NEXT_NETINET_IN_H@ +NEXT_PTHREAD_H = @NEXT_PTHREAD_H@ +NEXT_SCHED_H = @NEXT_SCHED_H@ +NEXT_SIGNAL_H = @NEXT_SIGNAL_H@ +NEXT_STDARG_H = @NEXT_STDARG_H@ +NEXT_STDDEF_H = @NEXT_STDDEF_H@ +NEXT_STDINT_H = @NEXT_STDINT_H@ +NEXT_STDIO_H = @NEXT_STDIO_H@ +NEXT_STDLIB_H = @NEXT_STDLIB_H@ +NEXT_STRING_H = @NEXT_STRING_H@ +NEXT_SYS_IOCTL_H = @NEXT_SYS_IOCTL_H@ +NEXT_SYS_RANDOM_H = @NEXT_SYS_RANDOM_H@ +NEXT_SYS_SELECT_H = @NEXT_SYS_SELECT_H@ +NEXT_SYS_SOCKET_H = @NEXT_SYS_SOCKET_H@ +NEXT_SYS_STAT_H = @NEXT_SYS_STAT_H@ +NEXT_SYS_TIME_H = @NEXT_SYS_TIME_H@ +NEXT_SYS_TYPES_H = @NEXT_SYS_TYPES_H@ +NEXT_SYS_UIO_H = @NEXT_SYS_UIO_H@ +NEXT_TIME_H = @NEXT_TIME_H@ +NEXT_UNISTD_H = @NEXT_UNISTD_H@ +NEXT_WCHAR_H = @NEXT_WCHAR_H@ +NEXT_WCTYPE_H = @NEXT_WCTYPE_H@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OS = @OS@ +OS_LIBS = @OS_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PARTEDLDFLAGS = @PARTEDLDFLAGS@ +PARTED_LIBS = @PARTED_LIBS@ +PARTED_USABLE_TEST_DIR = @PARTED_USABLE_TEST_DIR@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POSUB = @POSUB@ +PRAGMA_COLUMNS = @PRAGMA_COLUMNS@ +PRAGMA_SYSTEM_HEADER = @PRAGMA_SYSTEM_HEADER@ +PRIPTR_PREFIX = @PRIPTR_PREFIX@ +PTHREAD_H_DEFINES_STRUCT_TIMESPEC = @PTHREAD_H_DEFINES_STRUCT_TIMESPEC@ +PTHREAD_SIGMASK_LIB = @PTHREAD_SIGMASK_LIB@ +PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@ +RANLIB = @RANLIB@ +REPLACE_ACCESS = @REPLACE_ACCESS@ +REPLACE_ALIGNED_ALLOC = @REPLACE_ALIGNED_ALLOC@ +REPLACE_BTOWC = @REPLACE_BTOWC@ +REPLACE_CALLOC_FOR_CALLOC_GNU = @REPLACE_CALLOC_FOR_CALLOC_GNU@ +REPLACE_CALLOC_FOR_CALLOC_POSIX = @REPLACE_CALLOC_FOR_CALLOC_POSIX@ +REPLACE_CANONICALIZE_FILE_NAME = @REPLACE_CANONICALIZE_FILE_NAME@ +REPLACE_CHMOD = @REPLACE_CHMOD@ +REPLACE_CHOWN = @REPLACE_CHOWN@ +REPLACE_CLOSE = @REPLACE_CLOSE@ +REPLACE_COPY_FILE_RANGE = @REPLACE_COPY_FILE_RANGE@ +REPLACE_CREAT = @REPLACE_CREAT@ +REPLACE_CTIME = @REPLACE_CTIME@ +REPLACE_DPRINTF = @REPLACE_DPRINTF@ +REPLACE_DUP = @REPLACE_DUP@ +REPLACE_DUP2 = @REPLACE_DUP2@ +REPLACE_DUP3 = @REPLACE_DUP3@ +REPLACE_DUPLOCALE = @REPLACE_DUPLOCALE@ +REPLACE_ERROR = @REPLACE_ERROR@ +REPLACE_ERROR_AT_LINE = @REPLACE_ERROR_AT_LINE@ +REPLACE_EXECL = @REPLACE_EXECL@ +REPLACE_EXECLE = @REPLACE_EXECLE@ +REPLACE_EXECLP = @REPLACE_EXECLP@ +REPLACE_EXECV = @REPLACE_EXECV@ +REPLACE_EXECVE = @REPLACE_EXECVE@ +REPLACE_EXECVP = @REPLACE_EXECVP@ +REPLACE_EXECVPE = @REPLACE_EXECVPE@ +REPLACE_FACCESSAT = @REPLACE_FACCESSAT@ +REPLACE_FCHMODAT = @REPLACE_FCHMODAT@ +REPLACE_FCHOWNAT = @REPLACE_FCHOWNAT@ +REPLACE_FCLOSE = @REPLACE_FCLOSE@ +REPLACE_FCNTL = @REPLACE_FCNTL@ +REPLACE_FDATASYNC = @REPLACE_FDATASYNC@ +REPLACE_FDOPEN = @REPLACE_FDOPEN@ +REPLACE_FFLUSH = @REPLACE_FFLUSH@ +REPLACE_FFSLL = @REPLACE_FFSLL@ +REPLACE_FOPEN = @REPLACE_FOPEN@ +REPLACE_FOPEN_FOR_FOPEN_GNU = @REPLACE_FOPEN_FOR_FOPEN_GNU@ +REPLACE_FPRINTF = @REPLACE_FPRINTF@ +REPLACE_FPURGE = @REPLACE_FPURGE@ +REPLACE_FREE = @REPLACE_FREE@ +REPLACE_FREELOCALE = @REPLACE_FREELOCALE@ +REPLACE_FREOPEN = @REPLACE_FREOPEN@ +REPLACE_FSEEK = @REPLACE_FSEEK@ +REPLACE_FSEEKO = @REPLACE_FSEEKO@ +REPLACE_FSTAT = @REPLACE_FSTAT@ +REPLACE_FSTATAT = @REPLACE_FSTATAT@ +REPLACE_FTELL = @REPLACE_FTELL@ +REPLACE_FTELLO = @REPLACE_FTELLO@ +REPLACE_FTRUNCATE = @REPLACE_FTRUNCATE@ +REPLACE_FUTIMENS = @REPLACE_FUTIMENS@ +REPLACE_GETCWD = @REPLACE_GETCWD@ +REPLACE_GETDELIM = @REPLACE_GETDELIM@ +REPLACE_GETDOMAINNAME = @REPLACE_GETDOMAINNAME@ +REPLACE_GETDTABLESIZE = @REPLACE_GETDTABLESIZE@ +REPLACE_GETENTROPY = @REPLACE_GETENTROPY@ +REPLACE_GETGROUPS = @REPLACE_GETGROUPS@ +REPLACE_GETLINE = @REPLACE_GETLINE@ +REPLACE_GETLOADAVG = @REPLACE_GETLOADAVG@ +REPLACE_GETLOGIN_R = @REPLACE_GETLOGIN_R@ +REPLACE_GETPAGESIZE = @REPLACE_GETPAGESIZE@ +REPLACE_GETPASS = @REPLACE_GETPASS@ +REPLACE_GETPASS_FOR_GETPASS_GNU = @REPLACE_GETPASS_FOR_GETPASS_GNU@ +REPLACE_GETPROGNAME = @REPLACE_GETPROGNAME@ +REPLACE_GETRANDOM = @REPLACE_GETRANDOM@ +REPLACE_GETSUBOPT = @REPLACE_GETSUBOPT@ +REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@ +REPLACE_GMTIME = @REPLACE_GMTIME@ +REPLACE_IMAXABS = @REPLACE_IMAXABS@ +REPLACE_IMAXDIV = @REPLACE_IMAXDIV@ +REPLACE_INET_NTOP = @REPLACE_INET_NTOP@ +REPLACE_INET_PTON = @REPLACE_INET_PTON@ +REPLACE_INITSTATE = @REPLACE_INITSTATE@ +REPLACE_IOCTL = @REPLACE_IOCTL@ +REPLACE_ISATTY = @REPLACE_ISATTY@ +REPLACE_ISWBLANK = @REPLACE_ISWBLANK@ +REPLACE_ISWCNTRL = @REPLACE_ISWCNTRL@ +REPLACE_ISWDIGIT = @REPLACE_ISWDIGIT@ +REPLACE_ISWXDIGIT = @REPLACE_ISWXDIGIT@ +REPLACE_LCHOWN = @REPLACE_LCHOWN@ +REPLACE_LINK = @REPLACE_LINK@ +REPLACE_LINKAT = @REPLACE_LINKAT@ +REPLACE_LOCALECONV = @REPLACE_LOCALECONV@ +REPLACE_LOCALTIME = @REPLACE_LOCALTIME@ +REPLACE_LOCALTIME_R = @REPLACE_LOCALTIME_R@ +REPLACE_LSEEK = @REPLACE_LSEEK@ +REPLACE_LSTAT = @REPLACE_LSTAT@ +REPLACE_MALLOC_FOR_MALLOC_GNU = @REPLACE_MALLOC_FOR_MALLOC_GNU@ +REPLACE_MALLOC_FOR_MALLOC_POSIX = @REPLACE_MALLOC_FOR_MALLOC_POSIX@ +REPLACE_MBRLEN = @REPLACE_MBRLEN@ +REPLACE_MBRTOWC = @REPLACE_MBRTOWC@ +REPLACE_MBSINIT = @REPLACE_MBSINIT@ +REPLACE_MBSNRTOWCS = @REPLACE_MBSNRTOWCS@ +REPLACE_MBSRTOWCS = @REPLACE_MBSRTOWCS@ +REPLACE_MBSTATE_T = @REPLACE_MBSTATE_T@ +REPLACE_MBTOWC = @REPLACE_MBTOWC@ +REPLACE_MEMCHR = @REPLACE_MEMCHR@ +REPLACE_MEMMEM = @REPLACE_MEMMEM@ +REPLACE_MEMPCPY = @REPLACE_MEMPCPY@ +REPLACE_MKDIR = @REPLACE_MKDIR@ +REPLACE_MKFIFO = @REPLACE_MKFIFO@ +REPLACE_MKFIFOAT = @REPLACE_MKFIFOAT@ +REPLACE_MKNOD = @REPLACE_MKNOD@ +REPLACE_MKNODAT = @REPLACE_MKNODAT@ +REPLACE_MKOSTEMP = @REPLACE_MKOSTEMP@ +REPLACE_MKOSTEMPS = @REPLACE_MKOSTEMPS@ +REPLACE_MKSTEMP = @REPLACE_MKSTEMP@ +REPLACE_MKTIME = @REPLACE_MKTIME@ +REPLACE_NANOSLEEP = @REPLACE_NANOSLEEP@ +REPLACE_NEWLOCALE = @REPLACE_NEWLOCALE@ +REPLACE_NL_LANGINFO = @REPLACE_NL_LANGINFO@ +REPLACE_NULL = @REPLACE_NULL@ +REPLACE_OBSTACK_PRINTF = @REPLACE_OBSTACK_PRINTF@ +REPLACE_OPEN = @REPLACE_OPEN@ +REPLACE_OPENAT = @REPLACE_OPENAT@ +REPLACE_PERROR = @REPLACE_PERROR@ +REPLACE_PIPE2 = @REPLACE_PIPE2@ +REPLACE_POPEN = @REPLACE_POPEN@ +REPLACE_POSIX_MEMALIGN = @REPLACE_POSIX_MEMALIGN@ +REPLACE_POSIX_OPENPT = @REPLACE_POSIX_OPENPT@ +REPLACE_PREAD = @REPLACE_PREAD@ +REPLACE_PRINTF = @REPLACE_PRINTF@ +REPLACE_PSELECT = @REPLACE_PSELECT@ +REPLACE_PTHREAD_ATTR_DESTROY = @REPLACE_PTHREAD_ATTR_DESTROY@ +REPLACE_PTHREAD_ATTR_GETDETACHSTATE = @REPLACE_PTHREAD_ATTR_GETDETACHSTATE@ +REPLACE_PTHREAD_ATTR_INIT = @REPLACE_PTHREAD_ATTR_INIT@ +REPLACE_PTHREAD_ATTR_SETDETACHSTATE = @REPLACE_PTHREAD_ATTR_SETDETACHSTATE@ +REPLACE_PTHREAD_CONDATTR_DESTROY = @REPLACE_PTHREAD_CONDATTR_DESTROY@ +REPLACE_PTHREAD_CONDATTR_INIT = @REPLACE_PTHREAD_CONDATTR_INIT@ +REPLACE_PTHREAD_COND_BROADCAST = @REPLACE_PTHREAD_COND_BROADCAST@ +REPLACE_PTHREAD_COND_DESTROY = @REPLACE_PTHREAD_COND_DESTROY@ +REPLACE_PTHREAD_COND_INIT = @REPLACE_PTHREAD_COND_INIT@ +REPLACE_PTHREAD_COND_SIGNAL = @REPLACE_PTHREAD_COND_SIGNAL@ +REPLACE_PTHREAD_COND_TIMEDWAIT = @REPLACE_PTHREAD_COND_TIMEDWAIT@ +REPLACE_PTHREAD_COND_WAIT = @REPLACE_PTHREAD_COND_WAIT@ +REPLACE_PTHREAD_CREATE = @REPLACE_PTHREAD_CREATE@ +REPLACE_PTHREAD_DETACH = @REPLACE_PTHREAD_DETACH@ +REPLACE_PTHREAD_EQUAL = @REPLACE_PTHREAD_EQUAL@ +REPLACE_PTHREAD_EXIT = @REPLACE_PTHREAD_EXIT@ +REPLACE_PTHREAD_GETSPECIFIC = @REPLACE_PTHREAD_GETSPECIFIC@ +REPLACE_PTHREAD_JOIN = @REPLACE_PTHREAD_JOIN@ +REPLACE_PTHREAD_KEY_CREATE = @REPLACE_PTHREAD_KEY_CREATE@ +REPLACE_PTHREAD_KEY_DELETE = @REPLACE_PTHREAD_KEY_DELETE@ +REPLACE_PTHREAD_MUTEXATTR_DESTROY = @REPLACE_PTHREAD_MUTEXATTR_DESTROY@ +REPLACE_PTHREAD_MUTEXATTR_GETROBUST = @REPLACE_PTHREAD_MUTEXATTR_GETROBUST@ +REPLACE_PTHREAD_MUTEXATTR_GETTYPE = @REPLACE_PTHREAD_MUTEXATTR_GETTYPE@ +REPLACE_PTHREAD_MUTEXATTR_INIT = @REPLACE_PTHREAD_MUTEXATTR_INIT@ +REPLACE_PTHREAD_MUTEXATTR_SETROBUST = @REPLACE_PTHREAD_MUTEXATTR_SETROBUST@ +REPLACE_PTHREAD_MUTEXATTR_SETTYPE = @REPLACE_PTHREAD_MUTEXATTR_SETTYPE@ +REPLACE_PTHREAD_MUTEX_DESTROY = @REPLACE_PTHREAD_MUTEX_DESTROY@ +REPLACE_PTHREAD_MUTEX_INIT = @REPLACE_PTHREAD_MUTEX_INIT@ +REPLACE_PTHREAD_MUTEX_LOCK = @REPLACE_PTHREAD_MUTEX_LOCK@ +REPLACE_PTHREAD_MUTEX_TIMEDLOCK = @REPLACE_PTHREAD_MUTEX_TIMEDLOCK@ +REPLACE_PTHREAD_MUTEX_TRYLOCK = @REPLACE_PTHREAD_MUTEX_TRYLOCK@ +REPLACE_PTHREAD_MUTEX_UNLOCK = @REPLACE_PTHREAD_MUTEX_UNLOCK@ +REPLACE_PTHREAD_ONCE = @REPLACE_PTHREAD_ONCE@ +REPLACE_PTHREAD_RWLOCKATTR_DESTROY = @REPLACE_PTHREAD_RWLOCKATTR_DESTROY@ +REPLACE_PTHREAD_RWLOCKATTR_INIT = @REPLACE_PTHREAD_RWLOCKATTR_INIT@ +REPLACE_PTHREAD_RWLOCK_DESTROY = @REPLACE_PTHREAD_RWLOCK_DESTROY@ +REPLACE_PTHREAD_RWLOCK_INIT = @REPLACE_PTHREAD_RWLOCK_INIT@ +REPLACE_PTHREAD_RWLOCK_RDLOCK = @REPLACE_PTHREAD_RWLOCK_RDLOCK@ +REPLACE_PTHREAD_RWLOCK_TIMEDRDLOCK = @REPLACE_PTHREAD_RWLOCK_TIMEDRDLOCK@ +REPLACE_PTHREAD_RWLOCK_TIMEDWRLOCK = @REPLACE_PTHREAD_RWLOCK_TIMEDWRLOCK@ +REPLACE_PTHREAD_RWLOCK_TRYRDLOCK = @REPLACE_PTHREAD_RWLOCK_TRYRDLOCK@ +REPLACE_PTHREAD_RWLOCK_TRYWRLOCK = @REPLACE_PTHREAD_RWLOCK_TRYWRLOCK@ +REPLACE_PTHREAD_RWLOCK_UNLOCK = @REPLACE_PTHREAD_RWLOCK_UNLOCK@ +REPLACE_PTHREAD_RWLOCK_WRLOCK = @REPLACE_PTHREAD_RWLOCK_WRLOCK@ +REPLACE_PTHREAD_SELF = @REPLACE_PTHREAD_SELF@ +REPLACE_PTHREAD_SETSPECIFIC = @REPLACE_PTHREAD_SETSPECIFIC@ +REPLACE_PTHREAD_SIGMASK = @REPLACE_PTHREAD_SIGMASK@ +REPLACE_PTHREAD_SPIN_DESTROY = @REPLACE_PTHREAD_SPIN_DESTROY@ +REPLACE_PTHREAD_SPIN_INIT = @REPLACE_PTHREAD_SPIN_INIT@ +REPLACE_PTHREAD_SPIN_LOCK = @REPLACE_PTHREAD_SPIN_LOCK@ +REPLACE_PTHREAD_SPIN_TRYLOCK = @REPLACE_PTHREAD_SPIN_TRYLOCK@ +REPLACE_PTHREAD_SPIN_UNLOCK = @REPLACE_PTHREAD_SPIN_UNLOCK@ +REPLACE_PTSNAME = @REPLACE_PTSNAME@ +REPLACE_PTSNAME_R = @REPLACE_PTSNAME_R@ +REPLACE_PUTENV = @REPLACE_PUTENV@ +REPLACE_PWRITE = @REPLACE_PWRITE@ +REPLACE_QSORT_R = @REPLACE_QSORT_R@ +REPLACE_RAISE = @REPLACE_RAISE@ +REPLACE_RANDOM = @REPLACE_RANDOM@ +REPLACE_RANDOM_R = @REPLACE_RANDOM_R@ +REPLACE_READ = @REPLACE_READ@ +REPLACE_READLINK = @REPLACE_READLINK@ +REPLACE_READLINKAT = @REPLACE_READLINKAT@ +REPLACE_REALLOCARRAY = @REPLACE_REALLOCARRAY@ +REPLACE_REALLOC_FOR_REALLOC_GNU = @REPLACE_REALLOC_FOR_REALLOC_GNU@ +REPLACE_REALLOC_FOR_REALLOC_POSIX = @REPLACE_REALLOC_FOR_REALLOC_POSIX@ +REPLACE_REALPATH = @REPLACE_REALPATH@ +REPLACE_REMOVE = @REPLACE_REMOVE@ +REPLACE_RENAME = @REPLACE_RENAME@ +REPLACE_RENAMEAT = @REPLACE_RENAMEAT@ +REPLACE_RMDIR = @REPLACE_RMDIR@ +REPLACE_SCHED_YIELD = @REPLACE_SCHED_YIELD@ +REPLACE_SELECT = @REPLACE_SELECT@ +REPLACE_SETENV = @REPLACE_SETENV@ +REPLACE_SETHOSTNAME = @REPLACE_SETHOSTNAME@ +REPLACE_SETLOCALE = @REPLACE_SETLOCALE@ +REPLACE_SETSTATE = @REPLACE_SETSTATE@ +REPLACE_SLEEP = @REPLACE_SLEEP@ +REPLACE_SNPRINTF = @REPLACE_SNPRINTF@ +REPLACE_SPRINTF = @REPLACE_SPRINTF@ +REPLACE_STAT = @REPLACE_STAT@ +REPLACE_STDIO_READ_FUNCS = @REPLACE_STDIO_READ_FUNCS@ +REPLACE_STDIO_WRITE_FUNCS = @REPLACE_STDIO_WRITE_FUNCS@ +REPLACE_STPCPY = @REPLACE_STPCPY@ +REPLACE_STPNCPY = @REPLACE_STPNCPY@ +REPLACE_STRCASESTR = @REPLACE_STRCASESTR@ +REPLACE_STRCHRNUL = @REPLACE_STRCHRNUL@ +REPLACE_STRDUP = @REPLACE_STRDUP@ +REPLACE_STRERROR = @REPLACE_STRERROR@ +REPLACE_STRERRORNAME_NP = @REPLACE_STRERRORNAME_NP@ +REPLACE_STRERROR_R = @REPLACE_STRERROR_R@ +REPLACE_STRFTIME = @REPLACE_STRFTIME@ +REPLACE_STRNCAT = @REPLACE_STRNCAT@ +REPLACE_STRNDUP = @REPLACE_STRNDUP@ +REPLACE_STRNLEN = @REPLACE_STRNLEN@ +REPLACE_STRSIGNAL = @REPLACE_STRSIGNAL@ +REPLACE_STRSTR = @REPLACE_STRSTR@ +REPLACE_STRTOD = @REPLACE_STRTOD@ +REPLACE_STRTOIMAX = @REPLACE_STRTOIMAX@ +REPLACE_STRTOK_R = @REPLACE_STRTOK_R@ +REPLACE_STRTOL = @REPLACE_STRTOL@ +REPLACE_STRTOLD = @REPLACE_STRTOLD@ +REPLACE_STRTOLL = @REPLACE_STRTOLL@ +REPLACE_STRTOUL = @REPLACE_STRTOUL@ +REPLACE_STRTOULL = @REPLACE_STRTOULL@ +REPLACE_STRTOUMAX = @REPLACE_STRTOUMAX@ +REPLACE_STRUCT_LCONV = @REPLACE_STRUCT_LCONV@ +REPLACE_STRUCT_TIMEVAL = @REPLACE_STRUCT_TIMEVAL@ +REPLACE_SYMLINK = @REPLACE_SYMLINK@ +REPLACE_SYMLINKAT = @REPLACE_SYMLINKAT@ +REPLACE_TIME = @REPLACE_TIME@ +REPLACE_TIMEGM = @REPLACE_TIMEGM@ +REPLACE_TIMESPEC_GET = @REPLACE_TIMESPEC_GET@ +REPLACE_TMPFILE = @REPLACE_TMPFILE@ +REPLACE_TOWLOWER = @REPLACE_TOWLOWER@ +REPLACE_TRUNCATE = @REPLACE_TRUNCATE@ +REPLACE_TTYNAME_R = @REPLACE_TTYNAME_R@ +REPLACE_TZSET = @REPLACE_TZSET@ +REPLACE_UNLINK = @REPLACE_UNLINK@ +REPLACE_UNLINKAT = @REPLACE_UNLINKAT@ +REPLACE_UNSETENV = @REPLACE_UNSETENV@ +REPLACE_USLEEP = @REPLACE_USLEEP@ +REPLACE_UTIMENSAT = @REPLACE_UTIMENSAT@ +REPLACE_VASPRINTF = @REPLACE_VASPRINTF@ +REPLACE_VDPRINTF = @REPLACE_VDPRINTF@ +REPLACE_VFPRINTF = @REPLACE_VFPRINTF@ +REPLACE_VPRINTF = @REPLACE_VPRINTF@ +REPLACE_VSNPRINTF = @REPLACE_VSNPRINTF@ +REPLACE_VSPRINTF = @REPLACE_VSPRINTF@ +REPLACE_WCRTOMB = @REPLACE_WCRTOMB@ +REPLACE_WCSFTIME = @REPLACE_WCSFTIME@ +REPLACE_WCSNRTOMBS = @REPLACE_WCSNRTOMBS@ +REPLACE_WCSRTOMBS = @REPLACE_WCSRTOMBS@ +REPLACE_WCSTOK = @REPLACE_WCSTOK@ +REPLACE_WCSWIDTH = @REPLACE_WCSWIDTH@ +REPLACE_WCTOB = @REPLACE_WCTOB@ +REPLACE_WCTOMB = @REPLACE_WCTOMB@ +REPLACE_WCWIDTH = @REPLACE_WCWIDTH@ +REPLACE_WMEMPCPY = @REPLACE_WMEMPCPY@ +REPLACE_WRITE = @REPLACE_WRITE@ +REPLACE__EXIT = @REPLACE__EXIT@ +SCHED_YIELD_LIB = @SCHED_YIELD_LIB@ +SED = @SED@ +SELECT_LIB = @SELECT_LIB@ +SETLOCALE_LIB = @SETLOCALE_LIB@ +SETLOCALE_NULL_LIB = @SETLOCALE_NULL_LIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@ +SIZE_T_SUFFIX = @SIZE_T_SUFFIX@ +STDARG_H = @STDARG_H@ +STDCKDINT_H = @STDCKDINT_H@ +STDDEF_H = @STDDEF_H@ +STDINT_H = @STDINT_H@ +STRIP = @STRIP@ +SYS_IOCTL_H_HAVE_WINSOCK2_H = @SYS_IOCTL_H_HAVE_WINSOCK2_H@ +SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @SYS_IOCTL_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@ +SYS_TIME_H_DEFINES_STRUCT_TIMESPEC = @SYS_TIME_H_DEFINES_STRUCT_TIMESPEC@ +TIME_H_DEFINES_STRUCT_TIMESPEC = @TIME_H_DEFINES_STRUCT_TIMESPEC@ +TIME_H_DEFINES_TIME_UTC = @TIME_H_DEFINES_TIME_UTC@ +UINT32_MAX_LT_UINTMAX_MAX = @UINT32_MAX_LT_UINTMAX_MAX@ +UINT64_MAX_EQ_ULONG_MAX = @UINT64_MAX_EQ_ULONG_MAX@ +UNDEFINE_STRTOK_R = @UNDEFINE_STRTOK_R@ +UNISTD_H_DEFINES_STRUCT_TIMESPEC = @UNISTD_H_DEFINES_STRUCT_TIMESPEC@ +UNISTD_H_HAVE_SYS_RANDOM_H = @UNISTD_H_HAVE_SYS_RANDOM_H@ +UNISTD_H_HAVE_WINSOCK2_H = @UNISTD_H_HAVE_WINSOCK2_H@ +UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@ +USE_NLS = @USE_NLS@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +WARN_CFLAGS = @WARN_CFLAGS@ +WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@ +WINDOWS_64_BIT_OFF_T = @WINDOWS_64_BIT_OFF_T@ +WINDOWS_64_BIT_ST_SIZE = @WINDOWS_64_BIT_ST_SIZE@ +WINDOWS_STAT_INODES = @WINDOWS_STAT_INODES@ +WINDOWS_STAT_TIMESPEC = @WINDOWS_STAT_TIMESPEC@ +WINT_T_SUFFIX = @WINT_T_SUFFIX@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +YIELD_LIB = @YIELD_LIB@ +abs_aux_dir = @abs_aux_dir@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +bindir_c = @bindir_c@ +bindir_c_make = @bindir_c_make@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datadir_c = @datadir_c@ +datadir_c_make = @datadir_c_make@ +datarootdir = @datarootdir@ +datarootdir_c = @datarootdir_c@ +datarootdir_c_make = @datarootdir_c_make@ +docdir = @docdir@ +docdir_c = @docdir_c@ +docdir_c_make = @docdir_c_make@ +dvidir = @dvidir@ +dvidir_c = @dvidir_c@ +dvidir_c_make = @dvidir_c_make@ +exec_prefix = @exec_prefix@ +exec_prefix_c = @exec_prefix_c@ +exec_prefix_c_make = @exec_prefix_c_make@ +gl_LIBOBJDEPS = @gl_LIBOBJDEPS@ +gl_LIBOBJS = @gl_LIBOBJS@ +gl_LTLIBOBJS = @gl_LTLIBOBJS@ +gltests_LIBOBJDEPS = @gltests_LIBOBJDEPS@ +gltests_LIBOBJS = @gltests_LIBOBJS@ +gltests_LTLIBOBJS = @gltests_LTLIBOBJS@ +gltests_WITNESS = @gltests_WITNESS@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +htmldir_c = @htmldir_c@ +htmldir_c_make = @htmldir_c_make@ +includedir = @includedir@ +includedir_c = @includedir_c@ +includedir_c_make = @includedir_c_make@ +infodir = @infodir@ +infodir_c = @infodir_c@ +infodir_c_make = @infodir_c_make@ +install_sh = @install_sh@ +libdir = @libdir@ +libdir_c = @libdir_c@ +libdir_c_make = @libdir_c_make@ +libexecdir = @libexecdir@ +libexecdir_c = @libexecdir_c@ +libexecdir_c_make = @libexecdir_c_make@ +lispdir = @lispdir@ +lispdir_c = @lispdir_c@ +lispdir_c_make = @lispdir_c_make@ +localedir = @localedir@ +localedir_c = @localedir_c@ +localedir_c_make = @localedir_c_make@ +localstatedir = @localstatedir@ +localstatedir_c = @localstatedir_c@ +localstatedir_c_make = @localstatedir_c_make@ +mandir = @mandir@ +mandir_c = @mandir_c@ +mandir_c_make = @mandir_c_make@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +oldincludedir_c = @oldincludedir_c@ +oldincludedir_c_make = @oldincludedir_c_make@ +pdfdir = @pdfdir@ +pdfdir_c = @pdfdir_c@ +pdfdir_c_make = @pdfdir_c_make@ +pkgdatadir_c = @pkgdatadir_c@ +pkgdatadir_c_make = @pkgdatadir_c_make@ +pkgincludedir_c = @pkgincludedir_c@ +pkgincludedir_c_make = @pkgincludedir_c_make@ +pkglibdir_c = @pkglibdir_c@ +pkglibdir_c_make = @pkglibdir_c_make@ +pkglibexecdir_c = @pkglibexecdir_c@ +pkglibexecdir_c_make = @pkglibexecdir_c_make@ +prefix = @prefix@ +prefix_c = @prefix_c@ +prefix_c_make = @prefix_c_make@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +psdir_c = @psdir_c@ +psdir_c_make = @psdir_c_make@ +runstatedir = @runstatedir@ +runstatedir_c = @runstatedir_c@ +runstatedir_c_make = @runstatedir_c_make@ +sbindir = @sbindir@ +sbindir_c = @sbindir_c@ +sbindir_c_make = @sbindir_c_make@ +sharedstatedir = @sharedstatedir@ +sharedstatedir_c = @sharedstatedir_c@ +sharedstatedir_c_make = @sharedstatedir_c_make@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +sysconfdir_c = @sysconfdir_c@ +sysconfdir_c_make = @sysconfdir_c_make@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +@COMPILE_FOR_S390_FALSE@S390_SRCS = +@COMPILE_FOR_S390_TRUE@S390_SRCS = dasd.c fdasd.c vtoc.c +AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS) +partedincludedir = \ + -I$(top_srcdir)/lib -I$(top_builddir)/include -I$(top_srcdir)/include \ + -I$(top_srcdir)/libparted + +noinst_LTLIBRARIES = liblabels.la +liblabels_la_SOURCES = \ + $(S390_SRCS) \ + aix.c \ + atari.c \ + bsd.c \ + dos.c \ + dvh.c \ + dvh.h \ + efi_crc32.c \ + gpt.c \ + loop.c \ + mac.c \ + misc.h \ + pc98.c \ + pt-common.h \ + pt-tools.c \ + pt-tools.h \ + rdb.c \ + sun.c + +liblabels_la_LIBADD = $(OS_LIBS) $(INTLLIBS) $(LIBICONV) +AM_CPPFLAGS = $(partedincludedir) $(INTLINCS) + +# Included by 'pt-tools.c', so needs to be built early. +BUILT_SOURCES = pt-limit.c +MAINTAINERCLEANFILES = $(BUILT_SOURCES) +# DOn't add this to '_SOURCES', because it's not to be compiled! +EXTRA_DIST = pt-limit.c pt-limit.gperf +GPERF = gperf +GPERF_OPTIONS = \ + -C -N pt_limit_lookup -n -t -s 6 -k '*' --language=ANSI-C + +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libparted/labels/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu libparted/labels/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +liblabels.la: $(liblabels_la_OBJECTS) $(liblabels_la_DEPENDENCIES) $(EXTRA_liblabels_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(liblabels_la_OBJECTS) $(liblabels_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aix.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atari.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bsd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dasd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dos.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dvh.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/efi_crc32.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdasd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/loop.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mac.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pc98.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pt-tools.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rdb.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sun.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vtoc.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/aix.Plo + -rm -f ./$(DEPDIR)/atari.Plo + -rm -f ./$(DEPDIR)/bsd.Plo + -rm -f ./$(DEPDIR)/dasd.Plo + -rm -f ./$(DEPDIR)/dos.Plo + -rm -f ./$(DEPDIR)/dvh.Plo + -rm -f ./$(DEPDIR)/efi_crc32.Plo + -rm -f ./$(DEPDIR)/fdasd.Plo + -rm -f ./$(DEPDIR)/gpt.Plo + -rm -f ./$(DEPDIR)/loop.Plo + -rm -f ./$(DEPDIR)/mac.Plo + -rm -f ./$(DEPDIR)/pc98.Plo + -rm -f ./$(DEPDIR)/pt-tools.Plo + -rm -f ./$(DEPDIR)/rdb.Plo + -rm -f ./$(DEPDIR)/sun.Plo + -rm -f ./$(DEPDIR)/vtoc.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/aix.Plo + -rm -f ./$(DEPDIR)/atari.Plo + -rm -f ./$(DEPDIR)/bsd.Plo + -rm -f ./$(DEPDIR)/dasd.Plo + -rm -f ./$(DEPDIR)/dos.Plo + -rm -f ./$(DEPDIR)/dvh.Plo + -rm -f ./$(DEPDIR)/efi_crc32.Plo + -rm -f ./$(DEPDIR)/fdasd.Plo + -rm -f ./$(DEPDIR)/gpt.Plo + -rm -f ./$(DEPDIR)/loop.Plo + -rm -f ./$(DEPDIR)/mac.Plo + -rm -f ./$(DEPDIR)/pc98.Plo + -rm -f ./$(DEPDIR)/pt-tools.Plo + -rm -f ./$(DEPDIR)/rdb.Plo + -rm -f ./$(DEPDIR)/sun.Plo + -rm -f ./$(DEPDIR)/vtoc.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: all check install install-am install-exec install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +pt-limit.c: pt-limit.gperf + rm -f $@ $@-tmp + $(GPERF) $(GPERF_OPTIONS) $< \ + | perl -ne '/__GNUC_STDC_INLINE__/ and print "static\n"; print' \ + > $@-tmp + chmod a-w $@-tmp + mv $@-tmp $@ + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/libparted/labels/aix.c b/libparted/labels/aix.c new file mode 100644 index 0000000..8adb4db --- /dev/null +++ b/libparted/labels/aix.c @@ -0,0 +1,237 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + + libparted - a library for manipulating disk partitions + Copyright (C) 2000-2001, 2007-2014, 2019-2023 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/>. + + Contributor: Matt Wilson <msw@redhat.com> +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <stdbool.h> +#include "pt-tools.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#define AIX_LABEL_MAGIC (0xc9c2d4c1UL) +#define MAX_TOTAL_PART 16 + +static PedDiskType aix_disk_type; + +static int +aix_probe (const PedDevice *dev) +{ + PED_ASSERT (dev != NULL); + + void *label; + if (!ptt_read_sector (dev, 0, &label)) + return 0; + bool found = PED_BE32_TO_CPU(*(uint32_t *)label) == AIX_LABEL_MAGIC; + free (label); + return found; +} + +static PedDisk* +aix_alloc (const PedDevice* dev) +{ + PedDisk* disk; + + disk = _ped_disk_alloc (dev, &aix_disk_type); + if (!disk) + return NULL; + + return disk; +} + +static PedDisk* +aix_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + + new_disk = ped_disk_new_fresh (disk->dev, &aix_disk_type); + if (!new_disk) + return NULL; + + return new_disk; +} + +static void +aix_free (PedDisk *disk) +{ + _ped_disk_free (disk); +} + +static int +aix_read (PedDisk* disk) +{ + ped_disk_delete_all (disk); + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for reading AIX disk labels is " + "is not implemented yet.")); + return 0; +} + +#ifndef DISCOVER_ONLY +static int +aix_write (const PedDisk* disk) +{ + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for writing AIX disk labels is " + "is not implemented yet.")); + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +aix_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for adding partitions to AIX disk " + "labels is not implemented yet.")); + return NULL; +} + +static PedPartition* +aix_partition_duplicate (const PedPartition* part) +{ + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for duplicating partitions in AIX " + "disk labels is not implemented yet.")); + return NULL; +} + +static void +aix_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL); + + _ped_partition_free (part); +} + +static int +aix_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for setting system type of partitions " + "in AIX disk labels is not implemented yet.")); + return 0; +} + +static int +aix_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Support for setting flags " + "in AIX disk labels is not implemented yet.")); + return 0; +} + +static int _GL_ATTRIBUTE_CONST +aix_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + return 0; +} + + +static int +aix_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + return 0; +} + + +static int +aix_get_max_primary_partition_count (const PedDisk* disk) +{ + return 4; +} + +static bool +aix_get_max_supported_partition_count (const PedDisk* disk, int *max_n) +{ + *max_n = MAX_TOTAL_PART; + return true; +} + +static int _GL_ATTRIBUTE_PURE +aix_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL); + + return 1; +} + +static int _GL_ATTRIBUTE_PURE +aix_partition_enumerate (PedPartition* part) +{ + return 1; +} + +static int _GL_ATTRIBUTE_PURE +aix_alloc_metadata (PedDisk* disk) +{ + return 1; +} + +#include "pt-common.h" +PT_define_limit_functions (aix) + +static PedDiskOps aix_disk_ops = { + clobber: NULL, + write: NULL_IF_DISCOVER_ONLY (aix_write), + + partition_set_name: NULL, + partition_get_name: NULL, + + PT_op_function_initializers (aix) +}; + +static PedDiskType aix_disk_type = { + next: NULL, + name: "aix", + ops: &aix_disk_ops, + features: 0 +}; + +void +ped_disk_aix_init () +{ + ped_disk_type_register (&aix_disk_type); +} + +void +ped_disk_aix_done () +{ + ped_disk_type_unregister (&aix_disk_type); +} diff --git a/libparted/labels/atari.c b/libparted/labels/atari.c new file mode 100644 index 0000000..8ab3720 --- /dev/null +++ b/libparted/labels/atari.c @@ -0,0 +1,1975 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + + libparted - a library for manipulating disk partitions + atari.c - libparted module to manipulate Atari partition tables. + Copyright (C) 2000-2001, 2004, 2007-2014, 2019-2023 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/>. + + Contributor: Guillaume Knispel <k_guillaume@libertysurf.fr> + John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> +*/ + +/* + Documentation : + README file of atari-fdisk + atari-fdisk source code + Linux atari partitions parser source code + ( fs/partitions/atari.[ch] ) +*/ + +#include <config.h> + +#include <string.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <string.h> +#include <locale.h> +#include <stdint.h> +#include <ctype.h> +#include <stddef.h> + +#include "pt-tools.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + + +/********************** Atari data and structure stuff **********************/ + +#define BOOTABLE_CKSUM 0x1234 +#define NONBOOT_CKSUM 0x4321 + +#define GEM_MAX ((32*1024*1024)/PED_SECTOR_SIZE_DEFAULT) + +#define PART_FLAG_USED 0x01 +#define PART_FLAG_BOOT_GEM 0x80 /* GEMDOS */ +#define PART_FLAG_BOOT_ASV 0x40 /* Atari System V */ +#define PART_FLAG_BOOT_BSD 0x20 /* Net(?)BSD */ +#define PART_FLAG_BOOT_LNX 0x10 /* Linux */ +#define PART_FLAG_BOOT_UNK 0x08 /* unknown / other */ + +#define N_AHDI 4 +#define N_ICD 8 + +#define MAXIMUM_PARTS 64 + +/* what we put instead of id, start and size in empty */ +/* partition tables, to be able to detect it */ +#define SIGNATURE_EMPTY_TABLE "PARTEDATARI" +#define SIGNATURE_EMPTY_SIZE 11 + +/* to be compared to the last two bytes of 1st sector (Big Endian) */ +static const uint16_t atr_forbidden_sign[] = { + 0x55AA, + 0 +}; + +static const char *atr_known_icd_pid[] = { + "BGM", "GEM", "LNX", "SWP", "RAW", NULL +}; + +/* static const char *atr_known_pid[] = { */ +/* "BGM", "GEM", "LNX", "MAC", "MIX", "MNX", "RAW", "SWP", "UNX", */ +/* "F32", "SV4", NULL */ +/* }; */ + +struct _AtariPartID2BootFlag { + const char pid[4]; + uint8_t flag; +}; +typedef struct _AtariPartID2BootFlag AtariPartID2BootFlag; + +static AtariPartID2BootFlag atr_pid2bf[] = { + { "GEM", PART_FLAG_BOOT_GEM }, + { "BGM", PART_FLAG_BOOT_GEM }, + { "UNX", PART_FLAG_BOOT_ASV }, + { "LNX", PART_FLAG_BOOT_LNX }, + { "", PART_FLAG_BOOT_UNK }, +}; + +struct _AtariFS2PartId { + const char* fs; + const char pid[4]; + PedSector max_sectors; +}; +typedef struct _AtariFS2PartId AtariFS2PartId; + +static AtariFS2PartId atr_fs2pid[] = { +/* Other ID are available : MIX MNX <= minix + UNX <= Atari SysV Unix + SV4 <= Univ System 4 */ + { "ext2", "LNX", INT32_MAX }, + { "ext3", "LNX", INT32_MAX }, + { "fat16", "GEM", GEM_MAX }, /* small partitions */ + { "fat16", "BGM", INT32_MAX }, /* big partitions */ + { "fat32", "F32", INT32_MAX }, + { "hfs", "MAC", INT32_MAX }, + { "hfs+", "MAC", INT32_MAX }, + { "hfsx", "MAC", INT32_MAX }, + { "jfs", "LNX", INT32_MAX }, + { "linux-swap", "SWP", INT32_MAX }, + { "reiserfs", "LNX", INT32_MAX }, + { "hp-ufs", "LNX", INT32_MAX }, + { "sun-ufs", "LNX", INT32_MAX }, + { "xfs", "LNX", INT32_MAX }, + { "ntfs", "RAW", INT32_MAX }, + { "", "RAW", INT32_MAX }, /* default entry */ + { NULL, "" , 0 } /* end of list */ +}; + +struct __attribute__ ((packed)) _AtariRawPartition { + uint8_t flag; /* bit 0: active; bit 7: bootable */ + union { + uint8_t empty[11]; /* Empty table */ + struct __attribute__ ((packed)) { + uint8_t id[3]; /* "GEM", "BGM", "XGM", ... */ + uint32_t start; /* start of partition */ + uint32_t size; /* length of partition */ + }; + }; +}; +typedef struct _AtariRawPartition AtariRawPartition; + +struct __attribute__ ((packed,aligned(2))) _AtariRawTable { + uint8_t boot_code[0x156]; /* room for boot code */ + AtariRawPartition icd_part[N_ICD]; /* info for ICD-partitions 5..12 */ + uint8_t unused[0xc]; + uint32_t hd_size; /* size of disk in blocks */ + AtariRawPartition part[N_AHDI]; /* the four primary partitions */ + uint32_t bsl_start; /* start of bad sector list */ + uint32_t bsl_count; /* length of bad sector list */ + uint16_t checksum; /* checksum for bootable disks */ +}; +typedef struct _AtariRawTable AtariRawTable; + +typedef enum { + FMT_AHDI = 0, /* AHDI v1 compatible, no ICD and no XGM */ + FMT_XGM = 1, /* AHDI v3 with XGM / this disable ICD */ + FMT_ICD = 2 /* ICD detected / requested because more than 4 prim */ + /* no XGM allowed */ +} AtrFmt; + +struct _AtariDisk { + AtrFmt format; + int has_been_read; /* actually means has been read or written... */ + uint32_t bsl_start; /* first sector of the Bad Sectors List */ + uint32_t bsl_count; /* number of sectors of the BSL */ + uint8_t HDX_comp; /* if set to one, atari_write will initialize */ + /* the bsl area */ +}; +typedef struct _AtariDisk AtariDisk; + +struct _AtariPart { + char part_id[4]; /* ASCIIZ */ + char icd_id[4]; /* Linux only parse a limited set of ID */ + /* in ICD (why???), so everything else */ + /* is translated to RAW. */ + uint8_t flag; /* without bit 0 (entry used) */ +}; +typedef struct _AtariPart AtariPart; + +/* set by initialisation code to C locale */ +static locale_t atr_c_locale; + +static PedDiskType atari_disk_type; + + + +/******************************** Atari Code ********************************/ + +#define ATARI_DISK(disk) ((AtariDisk*)((disk)->disk_specific)) +#define ATARI_PART(part) ((AtariPart*)((part)->disk_specific)) + +#define atr_pid_eq(a,b) (!memcmp( (a), (b), 3 )) + +#define atr_pid_assign(a, b) (memcpy ( (a), (b), 3 )) + +#define atr_part_used(part) (((part)->flag) & PART_FLAG_USED) + +static int +atr_start_size_correct (uint32_t start, uint32_t size, uint32_t hd_size) +{ + uint32_t end = start + size; + + return end >= start + && 0 < start && start <= hd_size + && 0 < size && size <= hd_size + && 0 < end && end <= hd_size; +} + +static int +atr_part_correct (AtariRawPartition* part, uint32_t hd_size) +{ + uint32_t start, size; + + start = PED_BE32_TO_CPU (part->start); + size = PED_BE32_TO_CPU (part->size); + + return isalnum_l(part->id[0], atr_c_locale) + && isalnum_l(part->id[1], atr_c_locale) + && isalnum_l(part->id[2], atr_c_locale) + && atr_start_size_correct (start, size, hd_size); +} + +static int _GL_ATTRIBUTE_PURE +atr_pid_known (const char* pid, const char** pid_list) +{ + for (; *pid_list; pid_list++) { + if (atr_pid_eq(pid, *pid_list)) + return 1; + } + + return 0; +} + +/* Recognize Parted signature in an AHDI entry, used to + * identify empty Atari partition tables */ +static int +atr_is_signature_entry (AtariRawPartition* part) +{ + return part->flag == 0 + && !memcmp (part->empty, SIGNATURE_EMPTY_TABLE, + SIGNATURE_EMPTY_SIZE ); +} + +/* Set Parted signature in an AHDI entry */ +static void +atr_put_signature_entry (AtariRawPartition* part) +{ + part->flag = 0; + memcpy (part->empty, SIGNATURE_EMPTY_TABLE, SIGNATURE_EMPTY_SIZE); +} + +#define atr_part_known(part, pid_list) (atr_pid_known ((part)->id, pid_list)) + +#define atr_part_valid(part, sz) (atr_part_used(part)\ + && atr_part_correct((part), (sz))) +#define atr_part_trash(part, sz) (atr_part_used(part)\ + && !atr_part_correct((part), (sz))) + +/* Check if this device can be used with an Atari label */ +static int +atr_can_use_dev (const PedDevice *dev) +{ + /* i really don't know how atari behave with non 512 bytes */ + /* sectors... */ + if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't use Atari partition tables on disks with a " + "sector size not equal to %d bytes."), + (int)PED_SECTOR_SIZE_DEFAULT ); + return 0; + } + + /* the format isn't well defined enough to support > 0x7FFFFFFF */ + /* sectors */ + if (dev->length > INT32_MAX) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't use Atari partition tables on disks with more " + "than %d sectors."), + INT32_MAX ); + return 0; + } + + return 1; +} + +/* + * The Atari disk label doesn't have any magic id + * so we must completely parse the layout to be sure + * we are really dealing with it. + */ +static int +atari_probe (const PedDevice *dev) +{ + AtariRawTable table; + uint32_t rs_hd_size, parts, exts; + int valid_count, xgm_part, xgm_num, i; + int num_sign, total_count = 0; + + PED_ASSERT (dev != NULL); + PED_ASSERT (sizeof(table) == 512); + + /* Device Spec ok for Atari label? */ + if (!atr_can_use_dev (dev)) + return 0; + + /* read the root sector */ + if (!ped_device_read (dev, &table, 0, 1)) + return 0; + + /* number of sectors stored in the root sector > device length ? */ + /* => just reject the Atari disk label */ + rs_hd_size = PED_BE32_TO_CPU (table.hd_size); + if (rs_hd_size > dev->length + || rs_hd_size < 2) + return 0; + + /* check the BSL fields */ + if ((table.bsl_start || table.bsl_count) + && !atr_start_size_correct (PED_BE32_TO_CPU (table.bsl_start), + PED_BE32_TO_CPU (table.bsl_count), + rs_hd_size ) ) + return 0; + + /* scan the main AHDI fields */ + num_sign = 0; xgm_num = 0; + valid_count = 0; xgm_part = 0; + for (i = 0; i < N_AHDI; i++) { + if (atr_part_valid (&table.part[i], rs_hd_size)) { + valid_count++; + total_count++; + if (atr_pid_eq(table.part[i].id, "XGM")) { + xgm_part++; + xgm_num = i; + } + } else if (atr_part_trash (&table.part[i], rs_hd_size)) { + return 0; + } + if (atr_is_signature_entry (&table.part[i])) + num_sign++; + } + + /* no way to reliably detect empty Atari disk labels if + * they aren't using parted signature in 4 prim fields + * && reject multi XGM labels because Parted can't handle + * multiple extended partitions + * && reject if xgm partition in slot 0 because not allowed */ + if ((!valid_count && num_sign != N_AHDI) + || xgm_part > 1 + || (xgm_part == 1 && xgm_num == 0) ) + return 0; + + /* check coherency of each logical partitions and ARS */ + if (xgm_part) { /* ! WARNING ! reuses "table" */ + /* we must allow empty ext partition even if they're */ + /* not valid because parted write the layout to the HD */ + /* at each operation, and we can't create ext and log */ + /* at the same time */ + int empty_ars_allowed = 1; + + parts = exts = PED_BE32_TO_CPU (table.part[xgm_num].start); + while (1) { + if (!ped_device_read (dev, &table, parts, 1)) + return 0; + + for (i = 0; i < N_AHDI-1; ++i) { + if (atr_part_used (&table.part[i])) + break; + } + + /* we allow the ext part to be empty (see above) */ + if (i == N_AHDI-1 && empty_ars_allowed) + break; + + /* data partition must be in slot 0, 1 or 2 */ + if (i == N_AHDI-1 + || !atr_part_correct (&table.part[i], rs_hd_size + - parts ) + || atr_pid_eq (table.part[i].id, "XGM")) + return 0; + + /* If there is at least one logical partition */ + /* then next ARS should not be empty */ + empty_ars_allowed = 0; + + total_count++; + if (total_count > MAXIMUM_PARTS) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Too many Atari partitions detected. " + " Maybe there is a loop in the XGM " + "linked list. Aborting.") ); + return 0; + } + + /* end of logical partitions? */ + if (!atr_part_used (&table.part[i+1])) + break; + + /* is this really the descriptor of the next ARS? */ + if (!atr_part_correct (&table.part[i+1], rs_hd_size + - exts ) + || !atr_pid_eq (table.part[i+1].id, "XGM")) + return 0; + + parts = exts + PED_BE32_TO_CPU (table.part[i+1].start); + } + } /* no XGM so try ICD */ + else if (atr_part_valid (&table.icd_part[0], rs_hd_size) + && atr_part_known (&table.icd_part[0], atr_known_icd_pid)) { + for (i = 1; i < N_ICD; i++) { + if (atr_part_trash (&table.icd_part[i], rs_hd_size)) + return 0; + } + } + + return 1; +} + +static void +atr_disk_reset (AtariDisk* atr_disk) +{ + /* Empty partition table => only AHDI needed right now */ + atr_disk->format = FMT_AHDI; + /* The disk is not in sync with the actual content of the label */ + atr_disk->has_been_read = 0; + /* Create an empty BSL for HDX compatibility */ + atr_disk->bsl_start = 1; + atr_disk->bsl_count = 1; + atr_disk->HDX_comp = 1; +} + +/* + * Must set up the PedDisk and the associated AtariDisk as if + * the user is doing mklabel, since in this case atari_alloc + * is called alone whereas when reading an existing partition + * table atari_read is called after atari_alloc and can overwrite + * the settings. + */ +static PedDisk* +atari_alloc (const PedDevice* dev) +{ + PedDisk* disk; + AtariDisk* atr_disk; + + PED_ASSERT (dev != NULL); + + if (!atr_can_use_dev (dev) + || !(disk = _ped_disk_alloc (dev, &atari_disk_type))) + return NULL; + + if (!(disk->disk_specific = atr_disk = ped_malloc (sizeof (AtariDisk)))) + goto error_free_disk; + + atr_disk_reset (atr_disk); + + return disk; + +error_free_disk: + free (disk); + return NULL; +} + +static PedDisk* +atari_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + AtariDisk* old_atr_dsk; + AtariDisk* new_atr_dsk; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + PED_ASSERT (disk->disk_specific != NULL); + + old_atr_dsk = ATARI_DISK (disk); + if (!(new_disk = ped_disk_new_fresh (disk->dev, &atari_disk_type))) + return NULL; + new_atr_dsk = ATARI_DISK (new_disk); + + memcpy (new_atr_dsk, old_atr_dsk, sizeof(*old_atr_dsk)); + + return new_disk; +} + +static void +atari_free (PedDisk* disk) +{ + AtariDisk* atr_disk; + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->disk_specific != NULL); + atr_disk = ATARI_DISK (disk); + + _ped_disk_free (disk); + free (atr_disk); +} + +/* Warning : ID not ASCIIZ but 3 chars long */ +static void +atr_part_sysraw (PedPartition* part, const char* id, uint8_t flag) +{ + AtariPart* atr_part = ATARI_PART (part); + + atr_part->flag = flag & ~PART_FLAG_USED; + + atr_pid_assign (atr_part->part_id, id); + atr_part->part_id[3] = 0; + + if (atr_pid_known (id, atr_known_icd_pid)) { + atr_pid_assign (atr_part->icd_id, id); + atr_part->icd_id[3] = 0; + } else { + atr_pid_assign (atr_part->icd_id, "RAW"); + atr_part->icd_id[3] = 0; + } +} + +static int +atr_parse_add_rawpart (PedDisk* disk, PedPartitionType type, PedSector st_off, + int num, const AtariRawPartition* rawpart ) +{ + PedSector start, end; + PedPartition* part; + PedConstraint* const_exact; + int added; + + start = st_off + PED_BE32_TO_CPU (rawpart->start); + end = start + PED_BE32_TO_CPU (rawpart->size) - 1; + + part = ped_partition_new (disk, type, NULL, start, end); + if (!part) + return 0; + + /*part->num = num;*/ /* Enumeration will take care of that */ + part->num = -1; /* Indeed we can't enumerate here + * because the enumerate function uses + * -1 do detect new partition being + * inserted and update the atrdisk->format */ + if (type != PED_PARTITION_EXTENDED) + part->fs_type = ped_file_system_probe (&part->geom); + else + part->fs_type = NULL; + atr_part_sysraw (part, rawpart->id, rawpart->flag); + + const_exact = ped_constraint_exact (&part->geom); + added = ped_disk_add_partition (disk, part, const_exact); + ped_constraint_destroy (const_exact); + if (!added) { + ped_partition_destroy (part); + return 0; + } + + PED_ASSERT (part->num == num); + return 1; +} + +/* + * Read the chained list of logical partitions. + * exts points to the first Auxiliary Root Sector, at the start + * of the extended partition. + * In each ARS one partition entry describes to the logical partition + * (start relative to the ARS position) and the next entry with ID "XGM" + * points to the next ARS (start relative to exts). + */ +static int +atr_read_logicals (PedDisk* disk, PedSector exts, int* pnum) +{ + AtariRawTable table; + PedSector parts = exts; + int i, empty_ars_allowed = 1; + + while (1) { + if (!ped_device_read (disk->dev, &table, parts, 1)) + return 0; + + for (i = 0; i < N_AHDI-1; ++i) + if (atr_part_used (&table.part[i])) + break; + + if (i == N_AHDI-1 && empty_ars_allowed) + break; + + /* data partition must be in slot 0, 1 or 2 */ + if (i == N_AHDI-1 + || atr_pid_eq (table.part[i].id, "XGM")) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("No data partition found in the ARS at " + "sector %lli."), parts ); + return 0; + } + + empty_ars_allowed = 0; + + if (!atr_parse_add_rawpart (disk, PED_PARTITION_LOGICAL, + parts, *pnum, &table.part[i] ) ) + return 0; + + (*pnum)++; + + /* end of logical partitions? */ + if (!atr_part_used (&table.part[i+1])) + break; + + if (!atr_pid_eq (table.part[i+1].id, "XGM")) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The entry of the next logical ARS is not of " + "type XGM in ARS at sector %lli."), parts ); + return 0; + } + + parts = exts + PED_BE32_TO_CPU (table.part[i+1].start); + } + + return 1; +} + +static int +atari_read (PedDisk* disk) +{ + AtariRawTable table; + AtariDisk* atr_disk; + uint32_t rs_hd_size; + int i, pnum, xgm, pcount; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + PED_ASSERT (disk->disk_specific != NULL); + atr_disk = ATARI_DISK (disk); + + ped_disk_delete_all (disk); + atr_disk_reset (atr_disk); + + if (!atari_probe (disk->dev)) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("There doesn't seem to be an Atari partition table " + "on this disk (%s), or it is corrupted."), + disk->dev->path ) + != PED_EXCEPTION_IGNORE) + return 0; + } + + if (!ped_device_read (disk->dev, (void*) &table, 0, 1)) + goto error; + + /* We are sure that the layout looks coherent so we + don't need to check too much */ + + rs_hd_size = PED_BE32_TO_CPU (table.hd_size); + atr_disk->bsl_start = PED_BE32_TO_CPU (table.bsl_start); + atr_disk->bsl_count = PED_BE32_TO_CPU (table.bsl_count); + atr_disk->HDX_comp = 0; + + /* AHDI primary partitions */ + pnum = 1; xgm = 0; pcount = 0; + for (i = 0; i < N_AHDI; i++) { + if (!atr_part_used (&table.part[i])) + continue; + + pcount++; + + if (atr_pid_eq (table.part[i].id, "XGM")) { + + atr_disk->format = FMT_XGM; + xgm = 1; + if (!atr_parse_add_rawpart(disk, PED_PARTITION_EXTENDED, + 0, 0, &table.part[i] ) + || !atr_read_logicals ( + disk, + PED_BE32_TO_CPU (table.part[i].start), + &pnum ) ) + goto error; + + } else { + + if (!atr_parse_add_rawpart (disk, PED_PARTITION_NORMAL, + 0, pnum, &table.part[i] ) ) + goto error; + pnum++; + } + } + + /* If no XGM partition has been found, the AHDI table is not empty, */ + /* the first entry is valid and its ID ok for ICD, then we parse the */ + /* ICD table. */ + if (!xgm && pcount != 0 + && atr_part_valid (&table.icd_part[0], rs_hd_size) + && atr_part_known (&table.icd_part[0], atr_known_icd_pid)) + for (i = 0; i < N_ICD; i++) { + + if (!atr_part_known (&table.icd_part[i], atr_known_icd_pid) + || !atr_part_used (&table.icd_part[i])) + continue; + atr_disk->format = FMT_ICD; + + if (!atr_parse_add_rawpart (disk, PED_PARTITION_NORMAL, + 0, pnum, &table.icd_part[i] ) ) + goto error; + pnum++; + } + + atr_disk->has_been_read = 1; + return 1; + +error: + ped_disk_delete_all (disk); + atr_disk_reset (atr_disk); + return 0; +} + +/* Returns the number of the first logical partition or -1 if not found */ +static int +atr_find_first_log (const PedDisk* disk) +{ + PedPartition* part; + int first_log, last; + + last = ped_disk_get_last_partition_num (disk); + + for (first_log = 1; first_log <= last; first_log++) { + if ((part = ped_disk_get_partition (disk, first_log)) + && (part->type & PED_PARTITION_LOGICAL)) + break; + } + + return first_log > last ? -1 : first_log; +} + +#ifndef DISCOVER_ONLY +static int +atari_clobber (PedDevice* dev) +{ + AtariRawTable table; + + PED_ASSERT (dev != NULL); + PED_ASSERT (atari_probe (dev)); + + if (!ped_device_read (dev, &table, 0, 1)) + return 0; + + /* clear anything but the boot code and the optional ICD table */ + memset (table.boot_code + offsetof (AtariRawTable, hd_size), + 0, + PED_SECTOR_SIZE_DEFAULT - offsetof (AtariRawTable, hd_size)); + + return ped_device_write (dev, &table, 0, 1); +} + +/* Computes the checksum of the root sector */ +static uint16_t +atr_calc_rs_sum (const AtariRawTable* table) +{ + const uint16_t* word = (uint16_t*)(table); + const uint16_t* end = (uint16_t*)(table + 1); + uint16_t sum; + + for (sum = 0; word < end; word++) + sum += PED_BE16_TO_CPU(*word); + + return sum; +} + +/* Returns 1 if the root sector is bootable, else returns 0 */ +static int +atr_is_boot_table (const AtariRawTable* table) +{ + return atr_calc_rs_sum (table) == BOOTABLE_CKSUM; +} + +/* + * Returns 1 if sign belongs to a set of `forbidden' signatures. + * (e.g.: 55AA which is the MSDOS siganture...) + * Only used for non bootable root sector since the signature of + * a bootable one is unique. + */ +static int _GL_ATTRIBUTE_PURE +atr_sign_is_forbidden (uint16_t sign) +{ + const uint16_t* forbidden; + + for (forbidden = atr_forbidden_sign; *forbidden; forbidden++) { + if (sign == *forbidden) + return 1; + } + + return 0; +} + +/* Updates table->checksum so the RS will be considered bootable (or not) */ +static void +atr_table_set_boot (AtariRawTable* table, int boot) +{ + uint16_t boot_cksum, noboot_cksum; + uint16_t sum; + + table->checksum = 0; + sum = atr_calc_rs_sum (table); + boot_cksum = BOOTABLE_CKSUM - sum; + + if (boot) { + table->checksum = PED_CPU_TO_BE16 (boot_cksum); + return; + } + + noboot_cksum = NONBOOT_CKSUM - sum; + + while (atr_sign_is_forbidden (noboot_cksum) + || noboot_cksum == boot_cksum) + noboot_cksum++; + + table->checksum = PED_CPU_TO_BE16 (noboot_cksum); +} + +/* Fill an used partition entry */ +static void +atr_fill_raw_entry (AtariRawPartition* rawpart, uint8_t flag, const char* id, + uint32_t start, uint32_t size ) +{ + rawpart->flag = PART_FLAG_USED | flag; + atr_pid_assign (rawpart->id, id); + rawpart->start = PED_CPU_TO_BE32 (start); + rawpart->size = PED_CPU_TO_BE32 (size); +} + +static int +atr_write_logicals (const PedDisk* disk) +{ + AtariRawTable table; + PedPartition* log_curr; + PedPartition* log_next; + PedPartition* ext; + PedPartition* part; + PedSector exts; + PedSector parts; + AtariPart* atr_part; + int first_log, pnum, i; + + PED_ASSERT (disk != NULL); + + ext = ped_disk_extended_partition (disk); + exts = parts = ext->geom.start; + + pnum = first_log = atr_find_first_log (disk); + + while (1) { + if (pnum != -1) { + log_curr = ped_disk_get_partition (disk, pnum); + log_next = ped_disk_get_partition (disk, pnum + 1); + } else { + log_curr = log_next = NULL; + } + + if (log_curr && !(log_curr->type & PED_PARTITION_LOGICAL)) + log_curr = NULL; + if (log_next && !(log_next->type & PED_PARTITION_LOGICAL)) + log_next = NULL; + + PED_ASSERT (pnum == first_log || log_curr); + + part = ped_disk_get_partition_by_sector (disk, parts); + if (part && ped_partition_is_active (part)) { + if (log_curr) + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("No room at sector %lli to store ARS " + "of logical partition %d."), + parts, pnum ); + else + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("No room at sector %lli to store ARS."), + parts ); + return 0; + } + + if (!ped_device_read (disk->dev, &table, parts, 1)) + return 0; + + if (!log_curr) { + PED_ASSERT (!log_next); + + for (i = 0; i < N_AHDI; i++) + table.part[i].flag &= ~PART_FLAG_USED; + } else { + atr_part = ATARI_PART (log_curr); + atr_fill_raw_entry (&table.part[0], atr_part->flag, + atr_part->part_id, + log_curr->geom.start - parts, + log_curr->geom.length ); + + for (i = 1; i < N_AHDI; i++) + table.part[i].flag &= ~PART_FLAG_USED; + + if (log_next) { + atr_fill_raw_entry (&table.part[1], 0, "XGM", + log_next->geom.start - 1 - exts, + log_next->geom.length + 1 ); + } + } + + /* TODO: check if we can set that bootable, and when */ + atr_table_set_boot (&table, 0); + + if (!ped_device_write (disk->dev, &table, parts, 1)) + return 0; + + if (!log_next) + break; + + parts = log_next->geom.start - 1; + pnum++; + } + + return 1; +} + +static int _GL_ATTRIBUTE_PURE +_disk_logical_partition_count (const PedDisk* disk) +{ + PedPartition* walk; + + int count = 0; + + PED_ASSERT (disk != NULL); + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (ped_partition_is_active (walk) + && (walk->type & PED_PARTITION_LOGICAL)) + count++; + } + + return count; +} + +/* Load the HD size from the table and ask to fix it if != device size. */ +static int +atr_load_fix_hdsize (const PedDisk* disk, uint32_t* rs_hd_size, AtariRawTable* table) +{ + AtariDisk* atr_disk = ATARI_DISK (disk); + int result = PED_EXCEPTION_UNHANDLED; + + *rs_hd_size = PED_BE32_TO_CPU (table->hd_size); + if (*rs_hd_size != disk->dev->length) { + if (atr_disk->has_been_read) { + result = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE_CANCEL, + _("The sector count that is stored in the " + "partition table does not correspond " + "to the size of your device. Do you " + "want to fix the partition table?") ); + if (result == PED_EXCEPTION_CANCEL) + return 0; + } + + if (result == PED_EXCEPTION_UNHANDLED) + result = PED_EXCEPTION_FIX; + + if (result == PED_EXCEPTION_FIX) { + *rs_hd_size = disk->dev->length; + table->hd_size = PED_CPU_TO_BE32(*rs_hd_size); + } + } + return 1; +} + +/* Try to init the HDX compatibility Bad Sectors List. */ +static int +atr_empty_init_bsl (const PedDisk* disk) +{ + uint8_t zeros[PED_SECTOR_SIZE_DEFAULT]; + PedSector sec; + PedPartition* part; + AtariDisk* atr_disk = ATARI_DISK (disk); + + memset (zeros, 0, PED_SECTOR_SIZE_DEFAULT); + for (sec = atr_disk->bsl_start; + sec < atr_disk->bsl_start + atr_disk->bsl_count; + sec++ ) { + if (sec == atr_disk->bsl_start) + zeros[3] = 0xA5; + else + zeros[3] = 0; + part = ped_disk_get_partition_by_sector (disk, sec); + if (part && ped_partition_is_active (part)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("No room at sector %lli to store BSL."), + sec ); + return 0; + } + ped_device_write (disk->dev, zeros, sec, 1); + } + atr_disk->HDX_comp = 0; + return 1; +} + +static int +atari_write (const PedDisk* disk) +{ + AtariRawTable table; + AtariDisk* atr_disk; + AtariPart* atr_part; + PedPartition* log; + PedPartition* ext_part; + PedPartition* part = NULL; + uint32_t rs_hd_size; + int i, xgm_begin, pnum, append_ext; + int put_sign, boot, prim_count, last_num; + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + atr_disk = ATARI_DISK (disk); + PED_ASSERT (atr_disk != NULL); + + prim_count = ped_disk_get_primary_partition_count (disk); + last_num = ped_disk_get_last_partition_num (disk); + ext_part = ped_disk_extended_partition (disk); + + /* WARNING: similar/related code in atari_enumerate */ + xgm_begin = ((log = ped_disk_get_partition (disk, 1)) + && (log->type & PED_PARTITION_LOGICAL)); + PED_ASSERT (atr_disk->format != FMT_ICD || ext_part == NULL); + PED_ASSERT (atr_disk->format != FMT_XGM || prim_count + xgm_begin <= N_AHDI); + PED_ASSERT (atr_disk->format != FMT_AHDI || (ext_part == NULL && prim_count + xgm_begin <= N_AHDI)); + + /* Device Spec ok for Atari label? */ + if (!atr_can_use_dev (disk->dev)) + goto error; + + if (!ped_device_read (disk->dev, (void*) &table, 0, 1)) + goto error; + + boot = atr_is_boot_table (&table); + + table.bsl_start = PED_CPU_TO_BE32 (atr_disk->bsl_start); + table.bsl_count = PED_CPU_TO_BE32 (atr_disk->bsl_count); + + /* Before anything else check the sector count and */ + /* fix it if necessary */ + if (!atr_load_fix_hdsize (disk, &rs_hd_size, &table)) + goto error; + + append_ext = (ext_part != NULL) + && (_disk_logical_partition_count (disk) == 0); + + /* Fill the AHDI table */ + put_sign = (prim_count == 0); + pnum = 1; + for (i = 0; i < N_AHDI; i++) { + if (pnum > last_num) + part = NULL; + else while (pnum <= last_num + && !(part = ped_disk_get_partition (disk, pnum))) + pnum++; + + if (put_sign) { + atr_put_signature_entry (&table.part[i]); + continue; + } + + if (!part && i != 0 && append_ext) { + part = ext_part; + append_ext = 0; + } + + if (!part || (i == 0 && xgm_begin)) { + table.part[i].flag &= ~PART_FLAG_USED; + continue; + } + + if (part->type & PED_PARTITION_LOGICAL) + part = ext_part; + + PED_ASSERT (part != NULL); + + atr_part = ATARI_PART (part); + atr_fill_raw_entry (&table.part[i], atr_part->flag, + atr_part->part_id, part->geom.start, + part->geom.length ); + + if (part->type & PED_PARTITION_EXTENDED) { + while (pnum <= last_num) { + part = ped_disk_get_partition (disk, pnum); + if (part && + !(part->type & PED_PARTITION_LOGICAL)) + break; + pnum++; + } + } else + pnum++; + } + + if ((ext_part != NULL || atr_disk->format == FMT_AHDI) + && pnum <= last_num) { + ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, + _("There were remaining partitions after filling " + "the main AHDI table.") ); + goto error; + } + + /* Leave XGM or ICD mode if uneeded */ + if (pnum > last_num + && (atr_disk->format == FMT_ICD || ext_part == NULL)) + atr_disk->format = FMT_AHDI; + + /* If AHDI mode, check that no ICD will be detected */ + /* and propose to fix */ + if (atr_disk->format == FMT_AHDI + && atr_part_valid (&table.icd_part[0], rs_hd_size) + && atr_part_known (&table.icd_part[0], atr_known_icd_pid)) { + int result = PED_EXCEPTION_UNHANDLED; + result = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_YES_NO_CANCEL, + _("The main AHDI table has been filled with all " + "partitions but the ICD table is not empty " + "so more partitions of unknown size and position " + "will be detected by ICD compatible software. Do " + "you want to invalidate the ICD table?") ); + if (result == PED_EXCEPTION_YES + || result == PED_EXCEPTION_UNHANDLED) + table.icd_part[0].flag &= ~PART_FLAG_USED; + else if (result == PED_EXCEPTION_CANCEL) + goto error; + } + + if (put_sign) + goto write_to_dev; + + /* Fill the ICD table */ + if (atr_disk->format == FMT_ICD) + for (i = 0; i < N_ICD; i++) { + if (pnum > last_num) + part = NULL; + else while (pnum <= last_num + && !(part = ped_disk_get_partition (disk, pnum))) + pnum++; + + if (!part) { + table.icd_part[i].flag &= ~PART_FLAG_USED; + continue; + } + + if (part->type & PED_PARTITION_EXTENDED + || part->type & PED_PARTITION_LOGICAL) { + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("ICD entries can't contain extended or " + "logical partitions.") ); + goto error; + } + + atr_part = ATARI_PART (part); + atr_fill_raw_entry (&table.icd_part[i], atr_part->flag, + atr_part->icd_id, part->geom.start, + part->geom.length ); + + pnum++; + } + + /* Write the chained list of logical partitions */ + if (atr_disk->format == FMT_XGM) { + if (!atr_write_logicals (disk)) + goto error; + } + +write_to_dev: + if (pnum <= last_num) { + ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, + _("There were remaining partitions after filling " + "the tables.") ); + goto error; + } + + /* Do we need to do that in case of failure too??? */ + atr_table_set_boot (&table, boot); + + /* Commit the root sector... */ + if (!ped_device_write (disk->dev, (void*) &table, 0, 1) + || !ped_device_sync (disk->dev)) + goto error; + + /* Try to init the HDX compatibility Bad Sectors List if needed. */ + if (atr_disk->HDX_comp && !atr_empty_init_bsl (disk)) + goto error; + + atr_disk->has_been_read = 1; + return ped_device_sync (disk->dev); + +error: + atr_disk->has_been_read = 0; + return 0; +} +#endif + +/* If extended partition in ICD mode, generate an error and returns 1 */ +/* else returns 0 */ +static int +atr_xgm_in_icd (const PedDisk* disk, PedPartitionType part_type) +{ + AtariDisk* atrdisk; + + PED_ASSERT (disk != NULL); + + if (part_type & PED_PARTITION_EXTENDED) { + atrdisk = ATARI_DISK (disk); + if (atrdisk->format == FMT_ICD) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("You can't use an extended XGM partition in " + "ICD mode (more than %d primary partitions, if " + "XGM is the first one it counts for two)."), + N_AHDI ); + return 1; + } + } + + return 0; +} + +static PedPartition* +atari_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + AtariPart* atrpart; + + if (atr_xgm_in_icd(disk, part_type)) + return 0; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + if (ped_partition_is_active (part)) { + part->disk_specific = atrpart = ped_malloc (sizeof (AtariPart)); + if (!atrpart) + goto error_free_part; + memset (atrpart, 0, sizeof (AtariPart)); + } else { + part->disk_specific = NULL; + } + return part; + +error_free_part: + _ped_partition_free (part); +error: + return NULL; +} + +static PedPartition* +atari_partition_duplicate (const PedPartition* part) +{ + PedPartition* new_part; + + new_part = ped_partition_new (part->disk, part->type, + part->fs_type, part->geom.start, + part->geom.end); + if (!new_part) + return NULL; + new_part->num = part->num; + if (ped_partition_is_active (part)) + memcpy (new_part->disk_specific, part->disk_specific, + sizeof (AtariPart)); + + return new_part; +} + +static void +atari_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL); + + if (ped_partition_is_active (part)) { + PED_ASSERT (part->disk_specific != NULL); + free (part->disk_specific); + } + _ped_partition_free (part); +} + +/* Note: fs_type is NULL for extended partitions */ +static int +atari_partition_set_system (PedPartition* part, + const PedFileSystemType* fs_type) +{ + AtariPart* atrpart; + AtariFS2PartId* fs2id; + PED_ASSERT (part != NULL); + atrpart = ATARI_PART (part); + PED_ASSERT (atrpart != NULL); + + part->fs_type = fs_type; + + if (atr_xgm_in_icd(part->disk, part->type)) + return 0; + + if (part->type & PED_PARTITION_EXTENDED) { + strcpy (atrpart->part_id, "XGM"); + strcpy (atrpart->icd_id, "XGM"); + return 1; + } + + if (!fs_type) { + strcpy (atrpart->part_id, "RAW"); + strcpy (atrpart->icd_id, "RAW"); + return 1; + } + + for (fs2id = atr_fs2pid; fs2id->fs; fs2id++) { + if (!*fs2id->fs /* default entry */ + || ((!strcmp (fs_type->name, fs2id->fs) + && part->geom.length < fs2id->max_sectors))) { + + strcpy (atrpart->part_id, fs2id->pid); + if (atr_pid_known (fs2id->pid, atr_known_icd_pid)) + strcpy (atrpart->icd_id, fs2id->pid); + else + strcpy (atrpart->icd_id, "RAW"); + + break; + } + } + PED_ASSERT (fs2id->fs != NULL); + + return 1; +} + +static int +atari_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + AtariPart* atr_part; + AtariPartID2BootFlag* bf; + + PED_ASSERT (part != NULL); + atr_part = ATARI_PART (part); + PED_ASSERT (atr_part != NULL); + + if (flag != PED_PARTITION_BOOT) + return 0; + + if (state == 0) { + atr_part->flag = 0; + } else { + for (bf = atr_pid2bf; *bf->pid; bf++) { + if (atr_pid_eq (bf->pid, atr_part->part_id)) + break; + } + atr_part->flag = bf->flag; + } + + return 1; +} + +static int _GL_ATTRIBUTE_PURE +atari_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + AtariPart* atr_part; + + PED_ASSERT (part != NULL); + atr_part = ATARI_PART (part); + PED_ASSERT (atr_part != NULL); + + if (flag != PED_PARTITION_BOOT) + return 0; + + return (atr_part->flag != 0); +} + +static int +atari_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + if (flag == PED_PARTITION_BOOT) + return 1; + + return 0; +} + +/* Adapted from disk_dos */ +static PedConstraint* +atr_log_constraint (const PedPartition* part) +{ + const PedGeometry* geom = &part->geom; + PedGeometry safe_space; + PedSector min_start; + PedSector max_end; + PedDisk* disk; + PedDevice* dev; + PedPartition* ext_part; + PedPartition* walk; + int first_log, not_first; + + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->dev != NULL); + ext_part = ped_disk_extended_partition (part->disk); + PED_ASSERT (ext_part != NULL); + + dev = (disk = part->disk) -> dev; + + first_log = atr_find_first_log (disk); + if (first_log == -1) + first_log = part->num; + + not_first = (part->num != first_log); + + walk = ext_part->part_list; + + min_start = ext_part->geom.start + 1 + not_first; + max_end = ext_part->geom.end; + + while (walk != NULL + && ( walk->geom.start - (walk->num != first_log) + < geom->start - not_first + || walk->geom.start - (walk->num != first_log) + < min_start ) ) { + if (walk != part && ped_partition_is_active (walk)) + min_start = walk->geom.end + 1 + not_first; + walk = walk->next; + } + + while (walk && (walk == part || !ped_partition_is_active (walk))) + walk = walk->next; + + if (walk) + max_end = walk->geom.start - 1 - (walk->num != first_log); + + if (min_start >= max_end) + return NULL; + + ped_geometry_init (&safe_space, dev, min_start, + max_end - min_start + 1); + return ped_constraint_new_from_max (&safe_space); +} + +/* Adapted from disk_dos */ +static PedGeometry* +art_min_extended_geom (const PedPartition* ext_part) +{ + PedDisk* disk = ext_part->disk; + PedPartition* walk; + PedGeometry* min_geom; + int first_log; + + first_log = atr_find_first_log (disk); + if (first_log == -1) + return NULL; + + walk = ped_disk_get_partition (disk, first_log); + PED_ASSERT (walk->type & PED_PARTITION_LOGICAL); + min_geom = ped_geometry_duplicate (&walk->geom); + if (!min_geom) + return NULL; + ped_geometry_set_start (min_geom, walk->geom.start - 1); + + for (walk = ext_part->part_list; walk; walk = walk->next) { + if (!ped_partition_is_active (walk) || walk->num == first_log) + continue; + if (walk->geom.start < min_geom->start) + ped_geometry_set_start (min_geom, walk->geom.start - 2); + if (walk->geom.end > min_geom->end) + ped_geometry_set_end (min_geom, walk->geom.end); + } + + return min_geom; +} + +/* Adapted from disk_dos */ +static PedConstraint* +atr_ext_constraint (const PedPartition* part) +{ + PedGeometry start_range; + PedGeometry end_range; + PedConstraint* constraint; + PedDevice* dev; + PedDisk* disk; + PedGeometry* min; + + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->dev != NULL); + + dev = (disk = part->disk) -> dev; + min = art_min_extended_geom (part); + + if (min) { + ped_geometry_init (&start_range, dev, 1, min->start); + ped_geometry_init (&end_range, dev, min->end, + dev->length - min->end); + ped_geometry_destroy (min); + } else { + ped_geometry_init (&start_range, dev, 1, dev->length - 1); + ped_geometry_init (&end_range, dev, 1, dev->length - 1); + } + + constraint = ped_constraint_new (ped_alignment_any, ped_alignment_any, + &start_range, &end_range, 1, dev->length); + return constraint; +} + +static PedConstraint* +atr_prim_constraint (const PedPartition* part) +{ + PedDevice* dev; + PedGeometry max; + + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->dev != NULL); + + dev = part->disk->dev; + + ped_geometry_init (&max, dev, 1, dev->length - 1); + return ped_constraint_new_from_max (&max); +} + +/* inspiration from disk_dos */ +static PedGeometry* +_best_solution (PedGeometry* a, PedGeometry* b) +{ + if (!a) + return b; + if (!b) + return a; + + if (a->length < b->length) + goto choose_b; + + ped_geometry_destroy (b); + return a; + +choose_b: + ped_geometry_destroy (a); + return b; +} + +/* copied from disk_dos */ +static PedGeometry* +_try_constraint (const PedPartition* part, const PedConstraint* external, + PedConstraint* internal) +{ + PedConstraint* intersection; + PedGeometry* solution; + + intersection = ped_constraint_intersect (external, internal); + ped_constraint_destroy (internal); + if (!intersection) + return NULL; + + solution = ped_constraint_solve_nearest (intersection, &part->geom); + ped_constraint_destroy (intersection); + return solution; +} + +/* + * internal is either the primary or extented constraint. + * If there's no BSL, the is the only internal constraint considered. + * If there's a BSL, try to fit the partition before or after (and + * choose the best fit, the one which results in the greatest size...) + */ +static int +atr_prim_align (PedPartition* part, const PedConstraint* constraint, + PedConstraint* internal) +{ + PedDevice* dev; + AtariDisk* atr_disk; + PedConstraint* cut; + PedGeometry* solution = NULL; + PedGeometry max; + PedSector bsl_end; + + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->dev != NULL); + dev = part->disk->dev; + atr_disk = ATARI_DISK (part->disk); + PED_ASSERT (atr_disk != NULL); + + /* No BSL */ + if (!atr_disk->bsl_start && !atr_disk->bsl_count) { + /* Note: _ped_partition_attempt_align will destroy internal */ + return _ped_partition_attempt_align(part, constraint, internal); + } + + /* BSL, try to fit before */ + if (atr_disk->bsl_start > 1) { + ped_geometry_init (&max, dev, 1, atr_disk->bsl_start - 1); + cut = ped_constraint_new_from_max (&max); + solution = _best_solution (solution, + _try_constraint (part, constraint, + ped_constraint_intersect (internal, cut))); + ped_constraint_destroy (cut); + } + + /* BSL, try to fit after, take the best solution */ + bsl_end = atr_disk->bsl_start + atr_disk->bsl_count; + if (bsl_end < dev->length) { + ped_geometry_init (&max, dev, bsl_end, dev->length - bsl_end); + cut = ped_constraint_new_from_max (&max); + solution = _best_solution (solution, + _try_constraint (part, constraint, + ped_constraint_intersect (internal, cut))); + ped_constraint_destroy (cut); + } + + ped_constraint_destroy (internal); + + if (solution) { + ped_geometry_set (&part->geom, solution->start, + solution->length); + ped_geometry_destroy (solution); + return 1; + } + + return 0; +} + +static int +atari_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL); + + switch (part->type) { + case PED_PARTITION_LOGICAL: + if (_ped_partition_attempt_align (part, constraint, + atr_log_constraint (part) ) ) + return 1; + break; + case PED_PARTITION_EXTENDED: + if (atr_prim_align (part, constraint, + atr_ext_constraint (part) ) ) + return 1; + break; + default: + if (atr_prim_align (part, constraint, + atr_prim_constraint (part) ) ) + return 1; + break; + } + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +/* increment numbers of any non logical partition found after the last */ +/* logical one, to make room for a new logical partition */ +static int +art_room_for_logic (PedDisk* disk) +{ + PedPartition* part; + int num, last_logic, last; + + /* too many partitions ? */ + last = ped_disk_get_last_partition_num (disk); + if (last >= MAXIMUM_PARTS) + return 0; + + /* find the last logical partition */ + last_logic = 0; + for (num = 1; num <= last; num++) { + part = ped_disk_get_partition (disk, num); + if (part && ped_partition_is_active (part) + && (part->type & PED_PARTITION_LOGICAL)) + last_logic = num; + } + + if (!last_logic) + return 1; + + /* increment */ + for (num = last; num > last_logic; num--) { + part = ped_disk_get_partition (disk, num); + if (part && ped_partition_is_active (part) + && !(part->type & ( PED_PARTITION_LOGICAL + | PED_PARTITION_EXTENDED)) + && part->num > 0 ) + part->num++; + } + + return 1; +} + +static int +atari_partition_enumerate (PedPartition* part) +{ + AtariDisk* atrdisk; + PedPartition* ext_part; + PedPartition* log; + int i, want_icd, want_xgm, num_max, xgm_begin, prim_count; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + atrdisk = ATARI_DISK (part->disk); + PED_ASSERT (atrdisk != NULL); + + /* WARNING: some similar/related code in atari_write */ + /* This is quite a <hack> : this function is probably the only way */ + /* to know something has been / is going to be modified in the table.*/ + /* So we detect the current operation mode (AHDI/XGM/ICD) and report */ + /* errors (in which case we refuse to operate...) */ + + prim_count = ped_disk_get_primary_partition_count (part->disk); + ext_part = ped_disk_extended_partition (part->disk); + + /* <hack in the hack> : we can't reorder (yet) , so if we begin with */ + /* XGM the first slot must be empty */ + xgm_begin = ((log = ped_disk_get_partition (part->disk, 1)) + && (log->type & PED_PARTITION_LOGICAL)) + || ((part->num == -1) + && (part->type & PED_PARTITION_LOGICAL) + && !ped_disk_get_partition (part->disk, 1)); + /* </hack in the hack> */ + + PED_ASSERT (atrdisk->format != FMT_ICD || ext_part == NULL); + PED_ASSERT (atrdisk->format != FMT_XGM + || prim_count + xgm_begin <= N_AHDI); + PED_ASSERT (atrdisk->format != FMT_AHDI + || (ext_part == NULL && prim_count + xgm_begin <= N_AHDI)); + + want_icd = ( ( prim_count + + xgm_begin + + ( (part->num == -1) + && !(part->type & PED_PARTITION_LOGICAL) ) ) + > N_AHDI ); + want_xgm = ( (part->type & PED_PARTITION_EXTENDED) + || ext_part != NULL ); + + if (!want_xgm && !want_icd) + atrdisk->format = FMT_AHDI; + else if (want_xgm && !want_icd) + atrdisk->format = FMT_XGM; + else if (!want_xgm && want_icd) + atrdisk->format = FMT_ICD; + else { + if (atr_xgm_in_icd (part->disk, PED_PARTITION_EXTENDED)) + return 0; + else { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("You can't use more than %d primary partitions " + "(ICD mode) if you use an extended XGM " + "partition. If XGM is the first partition " + "it counts for two."), + N_AHDI ); + return 0; + } + } + /* End of </hack> */ + + + /* Ext will be numbered 0 and will stay 0... */ + if (part->num == 0) + return 1; + + if (part->num == -1) { + + /* Linux don't show the ext part itself for Atari disk labels */ + /* so we use number 0 (could use a big number too, but that */ + /* would be less cute ;) */ + if (part->type & PED_PARTITION_EXTENDED) { + part->num = 0; + return 1; + } + + switch (atrdisk->format) { + case FMT_AHDI: + case FMT_ICD: + num_max = N_ICD + N_AHDI; + break; + case FMT_XGM: + num_max = MAXIMUM_PARTS; + break; + default: + num_max = 0; + PED_ASSERT (0); + } + + /* make room for logical partitions */ + if (part->type & PED_PARTITION_LOGICAL) { + if (!art_room_for_logic (part->disk)) + goto error_alloc_failed; + } + + /* find an unused number */ + for (i = 1; i <= num_max; i++) { + if (!ped_disk_get_partition (part->disk, i)) { + part->num = i; + return 1; + } + } + + } else { + /* find an unused number before or don't re-number */ + for (i = 1; i < part->num; i++) { + if (!ped_disk_get_partition (part->disk, i)) { + part->num = i; + } + } + return 1; + } + + /* failed to allocate a number */ +error_alloc_failed: +#ifndef DISCOVER_ONLY + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unable to allocate a partition number.")); +#endif + return 0; +} + +static int +atr_creat_add_metadata (PedDisk* disk, PedSector start, PedSector end, + PedPartitionType type ) +{ + PedPartition* new_part; + PedConstraint* const_exact; + int added; + + type |= PED_PARTITION_METADATA; + new_part = ped_partition_new (disk, type, NULL, start, end); + if (!new_part) + goto error; + + const_exact = ped_constraint_exact (&new_part->geom); + added = ped_disk_add_partition (disk, new_part, const_exact); + ped_constraint_destroy (const_exact); + if (!added) + goto error_destroy_part; + + return 1; + +error_destroy_part: + ped_partition_destroy (new_part); +error: + return 0; +} + +static int +atari_alloc_metadata (PedDisk* disk) +{ + PedPartition* ext; + PedPartition* log; + AtariDisk* atr_disk; + int i; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + atr_disk = ATARI_DISK (disk); + PED_ASSERT (atr_disk != NULL); + + /* allocate 1 sector for the disk label at the start */ + if (!atr_creat_add_metadata (disk, 0, 0, 0)) + return 0; + + /* allocate the sectors containing the BSL */ + if (atr_disk->bsl_start || atr_disk->bsl_count) { + if (!atr_creat_add_metadata (disk, atr_disk->bsl_start, + atr_disk->bsl_start + + atr_disk->bsl_count - 1, 0 ) ) + return 0; + } + + ext = ped_disk_extended_partition (disk); + if (ext) { + if (!atr_creat_add_metadata (disk, ext->geom.start, + ext->geom.start, + PED_PARTITION_LOGICAL ) ) + return 0; + + /* Find the first logical part */ + for (i = 1; i <= ped_disk_get_last_partition_num (disk); i++) + if ((log = ped_disk_get_partition (disk, i)) + && (log->type & PED_PARTITION_LOGICAL)) + break; + + for (log = ext->part_list; log; log = log->next) { + if ((log->type & ( PED_PARTITION_METADATA + | PED_PARTITION_FREESPACE)) + || log->num == i) + continue; + + if (!atr_creat_add_metadata (disk, log->geom.start-1, + log->geom.start-1, + PED_PARTITION_LOGICAL ) ) + return 0; + } + } + + return 1; +} + +static int _GL_ATTRIBUTE_PURE +atari_get_max_primary_partition_count (const PedDisk* disk) +{ + AtariDisk* atr_disk; + + PED_ASSERT (disk != NULL); + atr_disk = ATARI_DISK (disk); + PED_ASSERT (atr_disk != NULL); + + return atr_disk->format == FMT_XGM ? N_AHDI : N_AHDI + N_ICD; +} + +static bool +atari_get_max_supported_partition_count (const PedDisk* disk, int *max_n) +{ + AtariDisk* atr_disk; + + PED_ASSERT (disk != NULL); + atr_disk = ATARI_DISK (disk); + PED_ASSERT (atr_disk != NULL); + + *max_n = atr_disk->format == FMT_XGM ? N_AHDI : N_AHDI + N_ICD; + return true; +} + +#include "pt-common.h" +PT_define_limit_functions(atari) + +static PedDiskOps atari_disk_ops = { + clobber: NULL_IF_DISCOVER_ONLY (atari_clobber), + write: NULL_IF_DISCOVER_ONLY (atari_write), + + partition_set_name: NULL, + partition_get_name: NULL, + + PT_op_function_initializers (atari) +}; + +static PedDiskType atari_disk_type = { + next: NULL, + name: "atari", + ops: &atari_disk_ops, + features: PED_DISK_TYPE_EXTENDED +}; + +void +ped_disk_atari_init () +{ + PED_ASSERT (sizeof (AtariRawPartition) == 12); + PED_ASSERT (sizeof (AtariRawTable) == 512); + /* GNU Libc doesn't support NULL instead of the locale name */ + PED_ASSERT ((atr_c_locale = newlocale(LC_ALL_MASK, "C", NULL)) != NULL); + + ped_disk_type_register (&atari_disk_type); +} + +void +ped_disk_atari_done () +{ + ped_disk_type_unregister (&atari_disk_type); + freelocale(atr_c_locale); +} diff --git a/libparted/labels/bsd.c b/libparted/labels/bsd.c new file mode 100644 index 0000000..38bc64c --- /dev/null +++ b/libparted/labels/bsd.c @@ -0,0 +1,654 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + + libparted - a library for manipulating disk partitions + Copyright (C) 2000-2001, 2007-2014, 2019-2023 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/>. + + Contributor: Matt Wilson <msw@redhat.com> +*/ + +#include <config.h> + +#include <stdbool.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <stdbool.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "misc.h" +#include "pt-tools.h" + +/* struct's & #define's stolen from libfdisk, which probably came from + * Linux... + */ + +#define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */ +#define BSD_MAXPARTITIONS 8 +#define BSD_FS_UNUSED 0 /* disklabel unused partition entry ID */ + +#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 */ + +#define BSD_BBSIZE 8192 /* size of boot area, with label */ +#define BSD_SBSIZE 8192 /* max size of fs superblock */ + +typedef struct _BSDRawPartition BSDRawPartition; +typedef struct _BSDRawLabel BSDRawLabel; +typedef struct _BSDDiskData BSDDiskData; + +struct _BSDRawPartition { /* the partition table */ + uint32_t p_size; /* number of sectors in partition */ + uint32_t p_offset; /* starting sector */ + uint32_t p_fsize; /* file system basic fragment size */ + uint8_t p_fstype; /* file system type, see below */ + uint8_t p_frag; /* file system fragments per block */ + uint16_t p_cpg; /* file system cylinders per group */ +} __attribute__((packed)); + +struct _BSDRawLabel { + uint32_t d_magic; /* the magic number */ + int16_t d_type; /* drive type */ + int16_t d_subtype; /* controller/d_type specific */ + int8_t d_typename[16]; /* type name, e.g. "eagle" */ + int8_t d_packname[16]; /* pack identifier */ + 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 */ + uint16_t d_sparespertrack; /* # of spare sectors per track */ + uint16_t d_sparespercyl; /* # of spare sectors per cylinder */ + uint32_t d_acylinders; /* # of alt. cylinders per unit */ + 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 */ +#define NDDATA 5 + uint32_t d_drivedata[NDDATA]; /* drive-type specific information */ +#define NSPARE 5 + uint32_t d_spare[NSPARE]; /* reserved for future use */ + uint32_t d_magic2; /* the magic number (again) */ + uint16_t d_checksum; /* xor of data incl. partitions */ + + /* file system 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 */ +#define D_PARTITIONS_WORDS 59 + BSDRawPartition d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */ +} __attribute__((packed, aligned(2))); + +struct _BSDDiskData { + char boot_code[64]; + BSDRawLabel label; /* label is offset by 64 bytes */ + char unused[172]; /* May contain more partitions */ +} __attribute__((packed, aligned(2))); + +typedef struct { + uint8_t type; + int boot; + int raid; + int lvm; +} BSDPartitionData; + +static PedDiskType bsd_disk_type; + +/* XXX fixme: endian? */ +static unsigned short +xbsd_dkcksum (BSDRawLabel *lp) { + const u_short* word = (u_short*)(lp); + const u_short* end = word + D_PARTITIONS_WORDS + PED_LE16_TO_CPU(lp->d_npartitions); + u_short sum; + + lp->d_checksum = 0; + for(sum=0; word < end; word++) + sum ^= PED_LE16_TO_CPU(*word); + return sum; +} + +/* XXX fixme: endian? */ +static void +alpha_bootblock_checksum (void *boot) { + uint64_t* dp = (uint64_t *)boot; + uint64_t sum=0; + int i; + + for (i = 0; i < 63; i++) + sum += dp[i]; + dp[63] = sum; +} + +static int +bsd_probe (const PedDevice *dev) +{ + BSDRawLabel *label; + + PED_ASSERT (dev != NULL); + + if (dev->sector_size < 512) + return 0; + + void *s0; + if (!ptt_read_sector (dev, 0, &s0)) + return 0; + + label = &((BSDDiskData*) s0)->label; + + /* check magic */ + bool found = PED_LE32_TO_CPU (label->d_magic) == BSD_DISKMAGIC; + free (s0); + return found; +} + +static PedDisk* +bsd_alloc (const PedDevice* dev) +{ + PedDisk* disk; + BSDDiskData* bsd_specific; + BSDRawLabel *label; + + PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + + disk = _ped_disk_alloc ((PedDevice*)dev, &bsd_disk_type); + if (!disk) + goto error; + disk->disk_specific = bsd_specific = ped_calloc (sizeof (BSDDiskData)); + if (!bsd_specific) + goto error_free_disk; + + /* Initialize the disk label's default values */ + label = &bsd_specific->label; + label->d_magic = PED_CPU_TO_LE32 (BSD_DISKMAGIC); + label->d_type = PED_CPU_TO_LE16 (BSD_DTYPE_SCSI); + label->d_flags = 0; + label->d_secsize = PED_CPU_TO_LE16 (dev->sector_size); + label->d_nsectors = PED_CPU_TO_LE32 (dev->bios_geom.sectors); + label->d_ntracks = PED_CPU_TO_LE32 (dev->bios_geom.heads); + label->d_ncylinders = PED_CPU_TO_LE32 (dev->bios_geom.cylinders); + label->d_secpercyl = PED_CPU_TO_LE32 (dev->bios_geom.sectors + * dev->bios_geom.heads); + label->d_secperunit + = PED_CPU_TO_LE32 (dev->bios_geom.sectors + * dev->bios_geom.heads + * dev->bios_geom.cylinders); + + label->d_rpm = PED_CPU_TO_LE16 (3600); + label->d_interleave = PED_CPU_TO_LE16 (1); + label->d_trackskew = 0; + label->d_cylskew = 0; + label->d_headswitch = 0; + label->d_trkseek = 0; + + label->d_magic2 = PED_CPU_TO_LE32 (BSD_DISKMAGIC); + label->d_bbsize = PED_CPU_TO_LE32 (BSD_BBSIZE); + label->d_sbsize = PED_CPU_TO_LE32 (BSD_SBSIZE); + + label->d_npartitions = 0; + label->d_checksum = xbsd_dkcksum (label); + + return disk; + +error_free_disk: + free (disk); +error: + return NULL; +} + +static PedDisk* +bsd_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + BSDDiskData* new_bsd_data; + BSDDiskData* old_bsd_data = (BSDDiskData*) disk->disk_specific; + + new_disk = ped_disk_new_fresh (disk->dev, &bsd_disk_type); + if (!new_disk) + return NULL; + + new_bsd_data = (BSDDiskData*) new_disk->disk_specific; + memcpy (new_bsd_data, old_bsd_data, sizeof(BSDDiskData)); + return new_disk; +} + +static void +bsd_free (PedDisk* disk) +{ + free (disk->disk_specific); + _ped_disk_free (disk); +} + +static int +bsd_read (PedDisk* disk) +{ + BSDDiskData* bsd_specific = (BSDDiskData*) disk->disk_specific; + BSDRawLabel* label; + int i; + + ped_disk_delete_all (disk); + + void *s0; + if (!ptt_read_sector (disk->dev, 0, &s0)) + return 0; + + memcpy (bsd_specific, s0, sizeof (BSDDiskData)); + free (s0); + + label = &bsd_specific->label; + + for (i = 1; i <= BSD_MAXPARTITIONS; i++) { + PedPartition* part; + BSDPartitionData* bsd_part_data; + PedSector start; + PedSector end; + + if (!label->d_partitions[i - 1].p_size + || !label->d_partitions[i - 1].p_fstype) + continue; + start = PED_LE32_TO_CPU(label->d_partitions[i - 1].p_offset); + end = PED_LE32_TO_CPU(label->d_partitions[i - 1].p_offset) + + PED_LE32_TO_CPU(label->d_partitions[i - 1].p_size) - 1; + part = ped_partition_new (disk, PED_PARTITION_NORMAL, + NULL, start, end); + if (!part) + goto error; + bsd_part_data = part->disk_specific; + bsd_part_data->type = label->d_partitions[i - 1].p_fstype; + part->num = i; + part->fs_type = ped_file_system_probe (&part->geom); + + PedConstraint *constraint_exact + = ped_constraint_exact (&part->geom); + if (constraint_exact == NULL) + goto error; + bool ok = ped_disk_add_partition (disk, part, constraint_exact); + ped_constraint_destroy (constraint_exact); + if (!ok) + goto error; + } + + return 1; + +error: + return 0; +} + +static void +_probe_and_add_boot_code (const PedDisk* disk) +{ + BSDDiskData *old_data; + + void *s0; + if (!ptt_read_sector (disk->dev, 0, &s0)) + return; + old_data = (BSDDiskData*) s0; + if (old_data->boot_code [0] + && old_data->label.d_magic == PED_CPU_TO_LE32 (BSD_DISKMAGIC)) { + BSDDiskData *bsd_specific = (BSDDiskData*) disk->disk_specific; + memcpy (bsd_specific, old_data, sizeof (BSDDiskData)); + } + free (s0); +} + +#ifndef DISCOVER_ONLY +static int +bsd_write (const PedDisk* disk) +{ + BSDDiskData* bsd_specific; + BSDRawLabel* label; + BSDPartitionData* bsd_data; + PedPartition* part; + int i; + int max_part = 0; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + + bsd_specific = (BSDDiskData*) disk->disk_specific; + label = &bsd_specific->label; + + if (!bsd_specific->boot_code[0]) + _probe_and_add_boot_code (disk); + + memset (label->d_partitions, 0, + sizeof (BSDRawPartition) * BSD_MAXPARTITIONS); + + for (i = 1; i <= BSD_MAXPARTITIONS; i++) { + part = ped_disk_get_partition (disk, i); + if (!part) + continue; + bsd_data = part->disk_specific; + label->d_partitions[i - 1].p_fstype = bsd_data->type; + label->d_partitions[i - 1].p_offset + = PED_CPU_TO_LE32 (part->geom.start); + label->d_partitions[i - 1].p_size + = PED_CPU_TO_LE32 (part->geom.length); + max_part = i; + } + + label->d_npartitions = PED_CPU_TO_LE16 (max_part + 1); + label->d_checksum = xbsd_dkcksum (label); + + alpha_bootblock_checksum (bsd_specific); + + if (!ptt_write_sector (disk, bsd_specific, + sizeof (BSDDiskData))) + goto error; + return ped_device_sync (disk->dev); + +error: + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +bsd_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + BSDPartitionData* bsd_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (ped_partition_is_active (part)) { + part->disk_specific + = bsd_data = ped_malloc (sizeof (BSDPartitionData)); + if (!bsd_data) + goto error_free_part; + bsd_data->type = 0; + bsd_data->boot = 0; + bsd_data->raid = 0; + bsd_data->lvm = 0; + } else { + part->disk_specific = NULL; + } + return part; + +error_free_part: + free (part); +error: + return 0; +} + +static PedPartition* +bsd_partition_duplicate (const PedPartition* part) +{ + PedPartition* new_part; + BSDPartitionData* new_bsd_data; + BSDPartitionData* old_bsd_data; + + new_part = ped_partition_new (part->disk, part->type, + part->fs_type, part->geom.start, + part->geom.end); + if (!new_part) + return NULL; + new_part->num = part->num; + + old_bsd_data = (BSDPartitionData*) part->disk_specific; + new_bsd_data = (BSDPartitionData*) new_part->disk_specific; + new_bsd_data->type = old_bsd_data->type; + new_bsd_data->boot = old_bsd_data->boot; + new_bsd_data->raid = old_bsd_data->raid; + new_bsd_data->lvm = old_bsd_data->lvm; + return new_part; +} + +static void +bsd_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL); + + if (ped_partition_is_active (part)) + free (part->disk_specific); + _ped_partition_free (part); +} + +static int +bsd_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + BSDPartitionData* bsd_data = part->disk_specific; + + part->fs_type = fs_type; + + if (!fs_type) + bsd_data->type = 0x8; + else if (is_linux_swap (fs_type->name)) + bsd_data->type = 0x1; + else + bsd_data->type = 0x8; + + return 1; +} + +static int +bsd_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + BSDPartitionData* bsd_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + PED_ASSERT (part->disk != NULL); + + bsd_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_BOOT: + bsd_data->boot = state; + return 1; + case PED_PARTITION_RAID: + if (state) { + bsd_data->lvm = 0; + } + bsd_data->raid = state; + return 1; + case PED_PARTITION_LVM: + if (state) { + bsd_data->raid = 0; + } + bsd_data->lvm = state; + return 1; + default: + ; + } + return 0; +} + +static int _GL_ATTRIBUTE_PURE +bsd_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + BSDPartitionData* bsd_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + bsd_data = part->disk_specific; + switch (flag) { + case PED_PARTITION_BOOT: + return bsd_data->boot; + + case PED_PARTITION_RAID: + return bsd_data->raid; + + case PED_PARTITION_LVM: + return bsd_data->lvm; + + default: + ; + } + return 0; +} + +static int +bsd_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_BOOT: + case PED_PARTITION_RAID: + case PED_PARTITION_LVM: + return 1; + default: + ; + } + return 0; +} + + +static int +bsd_get_max_primary_partition_count (const PedDisk* disk) +{ + return BSD_MAXPARTITIONS; +} + +static bool +bsd_get_max_supported_partition_count(const PedDisk* disk, int *max_n) +{ + *max_n = BSD_MAXPARTITIONS; + return true; +} + +static PedConstraint* +_get_constraint (const PedDevice* dev) +{ + PedGeometry max; + + ped_geometry_init (&max, dev, 1, dev->length - 1); + return ped_constraint_new_from_max (&max); +} + +static int +bsd_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + if (_ped_partition_attempt_align (part, constraint, + _get_constraint (part->disk->dev))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +bsd_partition_enumerate (PedPartition* part) +{ + int i; + PedPartition* p; + + /* never change the partition numbers */ + if (part->num != -1) + return 1; + for (i = 1; i <= BSD_MAXPARTITIONS; i++) { + p = ped_disk_get_partition (part->disk, i); + if (!p) { + part->num = i; + return 1; + } + } + + /* failed to allocate a number */ +#ifndef DISCOVER_ONLY + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unable to allocate a bsd disklabel slot.")); +#endif + return 0; +} + +static int +bsd_alloc_metadata (PedDisk* disk) +{ + PedPartition* new_part; + PedConstraint* constraint_any = NULL; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + + constraint_any = ped_constraint_any (disk->dev); + + /* allocate 1 sector for the disk label at the start */ + new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, 0, 0); + if (!new_part) + goto error; + + if (!ped_disk_add_partition (disk, new_part, constraint_any)) { + ped_partition_destroy (new_part); + goto error; + } + + ped_constraint_destroy (constraint_any); + return 1; +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +#include "pt-common.h" +PT_define_limit_functions (bsd) + +static PedDiskOps bsd_disk_ops = { + clobber: NULL, + write: NULL_IF_DISCOVER_ONLY (bsd_write), + + partition_set_name: NULL, + partition_get_name: NULL, + + PT_op_function_initializers (bsd) +}; + +static PedDiskType bsd_disk_type = { + next: NULL, + name: "bsd", + ops: &bsd_disk_ops, + features: 0 +}; + +void +ped_disk_bsd_init () +{ + PED_ASSERT (sizeof (BSDRawPartition) == 16); + PED_ASSERT (sizeof (BSDRawLabel) == 276); + + ped_disk_type_register (&bsd_disk_type); +} + +void +ped_disk_bsd_done () +{ + ped_disk_type_unregister (&bsd_disk_type); +} diff --git a/libparted/labels/dasd.c b/libparted/labels/dasd.c new file mode 100644 index 0000000..1d99458 --- /dev/null +++ b/libparted/labels/dasd.c @@ -0,0 +1,1032 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + + libparted - a library for manipulating disk partitions + Copyright (C) 2000-2001, 2007-2014, 2019-2023 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/>. + + Contributor: Phil Knirsch <phil@redhat.de> + Harald Hoyer <harald@redhat.de> +*/ + +#include <config.h> + +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <time.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdbool.h> + +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include <parted/vtoc.h> +#include <parted/fdasd.h> +#include <arch/linux.h> + +#include <libintl.h> +#if ENABLE_NLS +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "misc.h" +#include "pt-tools.h" + +#define PARTITION_LINUX_SWAP 0x82 +#define PARTITION_LINUX 0x83 +#define PARTITION_LINUX_LVM 0x8e +#define PARTITION_LINUX_RAID 0xfd + +extern void ped_disk_dasd_init (); +extern void ped_disk_dasd_done (); + +#define DASD_NAME "dasd" + +typedef struct { + int type; + int system; +} DasdPartitionData; + +typedef struct { + unsigned int format_type; + unsigned int label_block; + volume_label_t vlabel; +} DasdDiskSpecific; + +static int dasd_probe (const PedDevice *dev); +static int dasd_read (PedDisk* disk); +static int dasd_write (const PedDisk* disk); + +static PedPartition* dasd_partition_new (const PedDisk* disk, + PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, + PedSector end); +static PedPartition* dasd_partition_duplicate (const PedPartition *part); +static void dasd_partition_destroy (PedPartition* part); +static int dasd_partition_set_flag (PedPartition* part, + PedPartitionFlag flag, + int state); +static int dasd_partition_get_flag (const PedPartition* part, + PedPartitionFlag flag); +static int dasd_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag); +static int dasd_partition_align (PedPartition* part, + const PedConstraint* constraint); +static int dasd_partition_enumerate (PedPartition* part); +static int dasd_get_max_primary_partition_count (const PedDisk* disk); +static bool dasd_get_max_supported_partition_count (const PedDisk* disk, int *max_n); +static PedAlignment *dasd_get_partition_alignment(const PedDisk *disk); + +static PedDisk* dasd_alloc (const PedDevice* dev); +static PedDisk* dasd_duplicate (const PedDisk* disk); +static void dasd_free (PedDisk* disk); +static int dasd_partition_set_system (PedPartition* part, + const PedFileSystemType* fs_type); +static int dasd_alloc_metadata (PedDisk* disk); + +#include "pt-common.h" +PT_define_limit_functions (dasd) + +static PedDiskOps dasd_disk_ops = { + clobber: NULL, + write: NULL_IF_DISCOVER_ONLY (dasd_write), + + partition_set_name: NULL, + partition_get_name: NULL, + partition_set_type_id: NULL, + partition_get_type_id: NULL, + partition_set_type_uuid: NULL, + partition_get_type_uuid: NULL, + + get_partition_alignment: dasd_get_partition_alignment, + + PT_op_function_initializers (dasd) +}; + +static PedDiskType dasd_disk_type = { + next: NULL, + name: "dasd", + ops: &dasd_disk_ops, + features: 0 +}; + +struct flag_id_mapping_t +{ + enum _PedPartitionFlag flag; + int type_id; +}; + +static const struct flag_id_mapping_t flag_id_mapping[] = +{ + { PED_PARTITION_LVM, PARTITION_LINUX_LVM }, + { PED_PARTITION_RAID, PARTITION_LINUX_RAID }, + { PED_PARTITION_SWAP, PARTITION_LINUX_SWAP }, +}; + +static const struct flag_id_mapping_t* _GL_ATTRIBUTE_CONST +dasd_find_flag_id_mapping (PedPartitionFlag flag) +{ + int n = sizeof(flag_id_mapping) / sizeof(flag_id_mapping[0]); + + for (int i = 0; i < n; ++i) + if (flag_id_mapping[i].flag == flag) + return &flag_id_mapping[i]; + + return NULL; +} + +static PedDisk* +dasd_alloc (const PedDevice* dev) +{ + PedDisk* disk; + LinuxSpecific* arch_specific; + DasdDiskSpecific *disk_specific; + char volser[7]; + + PED_ASSERT (dev != NULL); + + arch_specific = LINUX_SPECIFIC (dev); + disk = _ped_disk_alloc (dev, &dasd_disk_type); + if (!disk) + return NULL; + + disk->disk_specific = disk_specific = ped_malloc(sizeof(DasdDiskSpecific)); + if (!disk->disk_specific) { + free (disk); + return NULL; + } + + /* CDL format, newer */ + disk_specific->format_type = 2; + disk_specific->label_block = 2; + + /* Setup volume label (for fresh disks) */ + snprintf(volser, sizeof(volser), "0X%04X", arch_specific->devno); + vtoc_volume_label_init(&disk_specific->vlabel); + vtoc_volume_label_set_key(&disk_specific->vlabel, "VOL1"); + vtoc_volume_label_set_label(&disk_specific->vlabel, "VOL1"); + vtoc_volume_label_set_volser(&disk_specific->vlabel, volser); + vtoc_set_cchhb(&disk_specific->vlabel.vtoc, + VTOC_START_CC, VTOC_START_HH, 0x01); + + return disk; +} + +static PedDisk* +dasd_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + + new_disk = ped_disk_new_fresh(disk->dev, &dasd_disk_type); + + if (!new_disk) + return NULL; + + memcpy(new_disk->disk_specific, disk->disk_specific, + sizeof(DasdDiskSpecific)); + + return new_disk; +} + +static void +dasd_free (PedDisk* disk) +{ + PED_ASSERT(disk != NULL); + /* Don't free disk->disk_specific first, in case _ped_disk_free + or one of its eventual callees ever accesses it. */ + void *p = disk->disk_specific; + _ped_disk_free(disk); + free(p); +} + + +void +ped_disk_dasd_init () +{ + ped_disk_type_register(&dasd_disk_type); +} + +void +ped_disk_dasd_done () +{ + ped_disk_type_unregister(&dasd_disk_type); +} + +static int +dasd_probe (const PedDevice *dev) +{ + LinuxSpecific* arch_specific; + struct fdasd_anchor anchor; + + PED_ASSERT(dev != NULL); + + arch_specific = LINUX_SPECIFIC(dev); + + /* add partition test here */ + fdasd_initialize_anchor(&anchor); + + if (fdasd_get_geometry(dev, &anchor, arch_specific->fd) == 0) + goto error_cleanup; + + /* Labels are required on CDL formatted DASDs. */ + if (fdasd_check_volume(&anchor, arch_specific->fd) && + anchor.FBA_layout == 0) + goto error_cleanup; + + fdasd_cleanup(&anchor); + + return 1; + + error_cleanup: + fdasd_cleanup(&anchor); + ped_exception_throw(PED_EXCEPTION_ERROR,PED_EXCEPTION_IGNORE_CANCEL, + "Error while probing device %s.", dev->path); + + return 0; +} + +static int +dasd_read (PedDisk* disk) +{ + int i; + char str[20]; + PedDevice* dev; + PedPartition* part; + PedFileSystemType *fs; + PedSector start, end; + PedConstraint* constraint_exact; + partition_info_t *p; + LinuxSpecific* arch_specific; + DasdDiskSpecific* disk_specific; + struct fdasd_anchor anchor; + + PDEBUG; + + PED_ASSERT (disk != NULL); + PDEBUG; + PED_ASSERT (disk->dev != NULL); + PDEBUG; + + dev = disk->dev; + + arch_specific = LINUX_SPECIFIC(dev); + disk_specific = disk->disk_specific; + + PDEBUG; + + fdasd_initialize_anchor(&anchor); + + if (fdasd_get_geometry(disk->dev, &anchor, arch_specific->fd) == 0) + goto error_close_dev; + + disk_specific->label_block = anchor.label_block; + + if ((anchor.geo.cylinders * anchor.geo.heads) > BIG_DISK_SIZE) + anchor.big_disk++; + + /* check dasd for labels and vtoc */ + if (fdasd_check_volume(&anchor, arch_specific->fd)) { + DasdPartitionData* dasd_data; + + /* Kernel partitioning code will report 'implicit' partitions + * for non-CDL format DASDs even when there is no + * label/VTOC. */ + if (anchor.FBA_layout == 0) + goto error_close_dev; + + disk_specific->format_type = 1; + + /* Register implicit partition */ + ped_disk_delete_all (disk); + + start = (PedSector) arch_specific->real_sector_size / + (PedSector) disk->dev->sector_size * + (PedSector) (anchor.label_block + 1); + end = disk->dev->length - 1; + part = ped_partition_new (disk, PED_PARTITION_NORMAL, NULL, + start, end); + if (!part) + goto error_close_dev; + + part->num = 1; + part->fs_type = ped_file_system_probe (&part->geom); + dasd_data = part->disk_specific; + dasd_data->type = 0; + + if (!ped_disk_add_partition (disk, part, NULL)) + goto error_close_dev; + + fdasd_cleanup(&anchor); + + return 1; + } + + /* Save volume label (read by fdasd_check_volume) for writing */ + memcpy(&disk_specific->vlabel, anchor.vlabel, sizeof(volume_label_t)); + + ped_disk_delete_all (disk); + + bool is_ldl = strncmp(anchor.vlabel->volkey, + vtoc_ebcdic_enc("LNX1", str, 4), 4) == 0; + bool is_cms = strncmp(anchor.vlabel->volkey, + vtoc_ebcdic_enc("CMS1", str, 4), 4) == 0; + if (is_ldl || is_cms) { + DasdPartitionData* dasd_data; + + union vollabel { + volume_label_t ldl; + cms_volume_label_t cms; + }; + union vollabel *cms_ptr1 = (union vollabel *) anchor.vlabel; + cms_volume_label_t *cms_ptr = &cms_ptr1->cms; + volume_label_t *ldl_ptr = &cms_ptr1->ldl; + int partition_start_block; + + disk_specific->format_type = 1; + + if (is_cms && cms_ptr->usable_count >= cms_ptr->block_count) + partition_start_block = 2; /* FBA DASD */ + else + partition_start_block = 3; /* CKD DASD */ + + if (is_ldl) + start = (long long) arch_specific->real_sector_size + / (long long) disk->dev->sector_size + * (long long) partition_start_block; + else if (cms_ptr->disk_offset == 0) + start = (long long) cms_ptr->block_size + / (long long) disk->dev->sector_size + * (long long) partition_start_block; + else + start = (long long) cms_ptr->block_size + / (long long) disk->dev->sector_size + * (long long) cms_ptr->disk_offset; + + if (is_ldl) + if (ldl_ptr->ldl_version >= 0xf2) + end = (long long) arch_specific->real_sector_size + / (long long) disk->dev->sector_size + * (long long) ldl_ptr->formatted_blocks - 1; + else + end = disk->dev->length - 1; + else + if (cms_ptr->disk_offset == 0) + end = (long long) cms_ptr->block_size + / (long long) disk->dev->sector_size + * (long long) cms_ptr->block_count - 1; + else + /* + Frankly, I do not understand why the last block + of the CMS reserved file is not included in the + partition; but this is the algorithm used by the + Linux kernel. See fs/partitions/ibm.c in the + Linux kernel source code. + */ + end = (long long) cms_ptr->block_size + / (long long) disk->dev->sector_size + * (long long) (cms_ptr->block_count - 1) - 1; + + part = ped_partition_new (disk, PED_PARTITION_NORMAL, NULL, start, end); + if (!part) + goto error_close_dev; + + part->num = 1; + part->fs_type = ped_file_system_probe (&part->geom); + dasd_data = part->disk_specific; + dasd_data->type = 0; + + if (!ped_disk_add_partition (disk, part, NULL)) + goto error_close_dev; + + fdasd_cleanup(&anchor); + + return 1; + } + + /* CDL format, newer */ + disk_specific->format_type = 2; + + p = anchor.first; + PDEBUG; + + for (i = 1 ; i <= USABLE_PARTITIONS; i++) { + char *ch = p->f1->DS1DSNAM; + DasdPartitionData* dasd_data; + + + if (p->used != 0x01) + continue; + + PDEBUG; + + start = (long long)(long long) p->start_trk + * (long long) disk->dev->hw_geom.sectors + * (long long) arch_specific->real_sector_size + / (long long) disk->dev->sector_size; + end = (long long)((long long) p->end_trk + 1) + * (long long) disk->dev->hw_geom.sectors + * (long long) arch_specific->real_sector_size + / (long long) disk->dev->sector_size - 1; + part = ped_partition_new(disk, PED_PARTITION_NORMAL, NULL, + start, end); + PDEBUG; + + if (!part) + goto error_close_dev; + + PDEBUG; + + part->num = i; + part->fs_type = ped_file_system_probe(&part->geom); + + vtoc_ebcdic_dec(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + ch = strstr(p->f1->DS1DSNAM, "PART"); + + if (ch != NULL) { + strncpy(str, ch+9, 6); + str[6] = '\0'; + } + + dasd_data = part->disk_specific; + + if (strncmp(PART_TYPE_RAID, str, 6) == 0) + dasd_data->system = PARTITION_LINUX_RAID; + else if (strncmp(PART_TYPE_LVM, str, 6) == 0) + dasd_data->system = PARTITION_LINUX_LVM; + else if (strncmp(PART_TYPE_SWAP, str, 6) == 0) + dasd_data->system = PARTITION_LINUX_SWAP; + + vtoc_ebcdic_enc(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + + dasd_data->type = 0; + + constraint_exact = ped_constraint_exact (&part->geom); + if (!constraint_exact) + goto error_close_dev; + if (!ped_disk_add_partition(disk, part, constraint_exact)) { + ped_constraint_destroy(constraint_exact); + goto error_close_dev; + } + ped_constraint_destroy(constraint_exact); + + if (p->fspace_trk > 0) { + start = (long long)((long long) p->end_trk + 1) + * (long long) disk->dev->hw_geom.sectors + * (long long) arch_specific->real_sector_size + / (long long) disk->dev->sector_size; + end = (long long)((long long) p->end_trk + 1 + p->fspace_trk) + * (long long) disk->dev->hw_geom.sectors + * (long long) arch_specific->real_sector_size + / (long long) disk->dev->sector_size - 1; + part = ped_partition_new (disk, PED_PARTITION_NORMAL, + NULL, start, end); + + if (!part) + goto error_close_dev; + + part->type = PED_PARTITION_FREESPACE; + constraint_exact = ped_constraint_exact(&part->geom); + + if (!constraint_exact) + goto error_close_dev; + if (!ped_disk_add_partition(disk, part, constraint_exact)) { + ped_constraint_destroy(constraint_exact); + goto error_close_dev; + } + + ped_constraint_destroy (constraint_exact); + } + + p = p->next; + } + + PDEBUG; + fdasd_cleanup(&anchor); + return 1; + +error_close_dev: + PDEBUG; + fdasd_cleanup(&anchor); + return 0; +} + +static int +dasd_update_type (const PedDisk* disk, struct fdasd_anchor *anchor, + partition_info_t *part_info[USABLE_PARTITIONS]) +{ + PedPartition* part; + LinuxSpecific* arch_specific; + DasdDiskSpecific* disk_specific; + + arch_specific = LINUX_SPECIFIC(disk->dev); + disk_specific = disk->disk_specific; + + PDEBUG; + + unsigned int i; + for (i = 1; i <= USABLE_PARTITIONS; i++) { + partition_info_t *p; + char *ch = NULL; + DasdPartitionData* dasd_data; + + PDEBUG; + + part = ped_disk_get_partition(disk, i); + if (!part) + continue; + + PDEBUG; + + dasd_data = part->disk_specific; + p = part_info[i - 1]; + + if (!p ) { + PDEBUG; + continue; + } + + vtoc_ebcdic_dec(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + ch = strstr(p->f1->DS1DSNAM, "PART"); + + PDEBUG; + if (ch == NULL) { + vtoc_ebcdic_enc(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + PDEBUG; + continue; + } + + ch += 9; + + switch (dasd_data->system) { + case PARTITION_LINUX_LVM: + PDEBUG; + strncpy(ch, PART_TYPE_LVM, 6); + break; + case PARTITION_LINUX_RAID: + PDEBUG; + strncpy(ch, PART_TYPE_RAID, 6); + break; + case PARTITION_LINUX: + PDEBUG; + strncpy(ch, PART_TYPE_NATIVE, 6); + break; + case PARTITION_LINUX_SWAP: + PDEBUG; + strncpy(ch, PART_TYPE_SWAP, 6); + break; + default: + PDEBUG; + strncpy(ch, PART_TYPE_NATIVE, 6); + break; + } + + anchor->vtoc_changed++; + vtoc_ebcdic_enc(p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + } + + return 1; +} + +static int +dasd_write (const PedDisk* disk) +{ + DasdPartitionData* dasd_data; + PedPartition* part; + int i; + partition_info_t *p; + LinuxSpecific* arch_specific; + DasdDiskSpecific* disk_specific; + struct fdasd_anchor anchor; + partition_info_t *part_info[USABLE_PARTITIONS]; + + PED_ASSERT(disk != NULL); + PED_ASSERT(disk->dev != NULL); + + arch_specific = LINUX_SPECIFIC (disk->dev); + disk_specific = disk->disk_specific; + + PDEBUG; + + /* If not formated in CDL, don't write anything. */ + if (disk_specific->format_type == 1) { + ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The partition table of DASD-LDL device cannot be changed.\n")); + return 1; + } + + /* initialize the anchor */ + fdasd_initialize_anchor(&anchor); + if (fdasd_get_geometry(disk->dev, &anchor, arch_specific->fd) == 0) + goto error; + + fdasd_check_volume(&anchor, arch_specific->fd); + memcpy(anchor.vlabel, &disk_specific->vlabel, sizeof(volume_label_t)); + anchor.vlabel_changed++; + + if ((anchor.geo.cylinders * anchor.geo.heads) > BIG_DISK_SIZE) + anchor.big_disk++; + + fdasd_recreate_vtoc(&anchor); + + for (i = 1; i <= USABLE_PARTITIONS; i++) { + unsigned int start, stop; + + PDEBUG; + part = ped_disk_get_partition(disk, i); + if (!part) + continue; + + PDEBUG; + + start = part->geom.start * disk->dev->sector_size + / arch_specific->real_sector_size / disk->dev->hw_geom.sectors; + stop = (part->geom.end + 1) + * disk->dev->sector_size / arch_specific->real_sector_size + / disk->dev->hw_geom.sectors - 1; + + PDEBUG; + dasd_data = part->disk_specific; + + p = fdasd_add_partition(&anchor, start, stop); + if (!p) { + PDEBUG; + goto error; + } + part_info[i - 1] = p; + p->type = dasd_data->system; + } + + PDEBUG; + + if (!fdasd_prepare_labels(&anchor, arch_specific->fd)) + goto error; + + dasd_update_type(disk, &anchor, part_info); + PDEBUG; + + if (!fdasd_write_labels(&anchor, arch_specific->fd)) + goto error; + + fdasd_cleanup(&anchor); + return 1; + +error: + PDEBUG; + fdasd_cleanup(&anchor); + return 0; +} + +static PedPartition* +dasd_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + + part = _ped_partition_alloc(disk, part_type, fs_type, start, end); + if (!part) + goto error; + + part->disk_specific = ped_calloc (sizeof (DasdPartitionData)); + return part; + +error: + return 0; +} + +static PedPartition* +dasd_partition_duplicate (const PedPartition *part) +{ + PedPartition *new_part; + + new_part = ped_partition_new (part->disk, part->type, part->fs_type, + part->geom.start, part->geom.end); + if (!new_part) + return NULL; + new_part->num = part->num; + + memcpy(new_part->disk_specific, part->disk_specific, + sizeof(DasdPartitionData)); + + return new_part; +} + +static void +dasd_partition_destroy (PedPartition* part) +{ + PED_ASSERT(part != NULL); + + if (ped_partition_is_active(part)) + free(part->disk_specific); + free(part); +} + +static int +dasd_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + DasdPartitionData* dasd_data; + + PED_ASSERT(part != NULL); + PED_ASSERT(part->disk_specific != NULL); + dasd_data = part->disk_specific; + + const struct flag_id_mapping_t* p = dasd_find_flag_id_mapping (flag); + if (p) + { + if (state) + dasd_data->system = p->type_id; + else if (dasd_data->system == p->type_id) + return dasd_partition_set_system (part, part->fs_type); + return 1; + } + + return 0; +} + +static int +dasd_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + DasdPartitionData* dasd_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + dasd_data = part->disk_specific; + + const struct flag_id_mapping_t* p = dasd_find_flag_id_mapping (flag); + if (p) + return dasd_data->system == p->type_id; + + return 0; +} + +/* + * The DASD-LDL does not support flags now. + * So just return 0. +*/ +static int +dasd_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + DasdDiskSpecific* disk_specific; + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->disk_specific != NULL); + + disk_specific = part->disk->disk_specific; + + if (disk_specific->format_type == 1) + return 0; + + if (dasd_find_flag_id_mapping (flag)) + return 1; + + return 0; +} + + +static int +dasd_get_max_primary_partition_count (const PedDisk* disk) +{ + DasdDiskSpecific* disk_specific; + + disk_specific = disk->disk_specific; + /* If formated in LDL, maximum partition number is 1 */ + if (disk_specific->format_type == 1) + return 1; + + return USABLE_PARTITIONS; +} + +static bool +dasd_get_max_supported_partition_count (const PedDisk* disk, int *max_n) +{ + *max_n = dasd_get_max_primary_partition_count(disk); + return true; +} + +static PedAlignment* +dasd_get_partition_alignment(const PedDisk *disk) +{ + LinuxSpecific *arch_specific = LINUX_SPECIFIC(disk->dev); + PedSector sector_size = + arch_specific->real_sector_size / disk->dev->sector_size; + + return ped_alignment_new(0, disk->dev->hw_geom.sectors * sector_size); +} + +static PedConstraint* +_primary_constraint (PedDisk* disk) +{ + PedAlignment start_align; + PedAlignment end_align; + PedGeometry max_geom; + PedSector sector_size; + LinuxSpecific* arch_specific; + DasdDiskSpecific* disk_specific; + PedSector start; + + PDEBUG; + + arch_specific = LINUX_SPECIFIC (disk->dev); + disk_specific = disk->disk_specific; + sector_size = arch_specific->real_sector_size / disk->dev->sector_size; + + if (!ped_alignment_init (&start_align, 0, + disk->dev->hw_geom.sectors * sector_size)) + return NULL; + if (!ped_alignment_init (&end_align, -1, + disk->dev->hw_geom.sectors * sector_size)) + return NULL; + + start = (FIRST_USABLE_TRK * (long long) disk->dev->hw_geom.sectors + * (long long) arch_specific->real_sector_size + / (long long) disk->dev->sector_size); + + if (!ped_geometry_init (&max_geom, disk->dev, start, disk->dev->length)) + return NULL; + + return ped_constraint_new(&start_align, &end_align, &max_geom, + &max_geom, 1, disk->dev->length); +} + +static int +dasd_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + DasdDiskSpecific* disk_specific; + + PED_ASSERT (part != NULL); + + disk_specific = part->disk->disk_specific; + /* If formated in LDL, ignore metadata partition */ + if (disk_specific->format_type == 1) + return 1; + + if (_ped_partition_attempt_align(part, constraint, + _primary_constraint(part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + + return 0; +} + +static int +dasd_partition_enumerate (PedPartition* part) +{ + int i; + PedPartition* p; + + /* never change the partition numbers */ + if (part->num != -1) + return 1; + + for (i = 1; i <= USABLE_PARTITIONS; i++) { + p = ped_disk_get_partition (part->disk, i); + if (!p) { + part->num = i; + return 1; + } + } + + /* failed to allocate a number */ + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unable to allocate a dasd disklabel slot")); + return 0; +} + +static int +dasd_partition_set_system (PedPartition* part, + const PedFileSystemType* fs_type) +{ + DasdPartitionData* dasd_data = part->disk_specific; + PedSector cyl_size; + + cyl_size=part->disk->dev->hw_geom.sectors * part->disk->dev->hw_geom.heads; + PDEBUG; + + part->fs_type = fs_type; + + if (!fs_type) { + dasd_data->system = PARTITION_LINUX; + PDEBUG; + } else if (is_linux_swap (fs_type->name)) { + dasd_data->system = PARTITION_LINUX_SWAP; + PDEBUG; + } else { + dasd_data->system = PARTITION_LINUX; + PDEBUG; + } + + return 1; +} + +static int +dasd_alloc_metadata (PedDisk* disk) +{ + PedPartition* new_part; + PedConstraint* constraint_any = NULL; + PedSector vtoc_end; + LinuxSpecific* arch_specific; + DasdDiskSpecific* disk_specific; + PedPartition* part = NULL; /* initialize solely to placate gcc */ + PedPartition* new_part2; + PedSector trailing_meta_start, trailing_meta_end; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + + arch_specific = LINUX_SPECIFIC (disk->dev); + disk_specific = disk->disk_specific; + + constraint_any = ped_constraint_any (disk->dev); + + /* For LDL or CMS, the leading metadata ends at the sector before + the start of the first partition */ + if (disk_specific->format_type == 1) { + part = ped_disk_get_partition(disk, 1); + if (part) + vtoc_end = part->geom.start - 1; + else + vtoc_end = (PedSector) arch_specific->real_sector_size / + (PedSector) disk->dev->sector_size * + (PedSector) disk_specific->label_block; + } + else { + if (disk->dev->type == PED_DEVICE_FILE) + arch_specific->real_sector_size = disk->dev->sector_size; + /* Mark the start of the disk as metadata. */ + vtoc_end = (FIRST_USABLE_TRK * (long long) disk->dev->hw_geom.sectors + * (long long) arch_specific->real_sector_size + / (long long) disk->dev->sector_size) - 1; + } + + new_part = ped_partition_new (disk,PED_PARTITION_METADATA,NULL,0,vtoc_end); + if (!new_part) + goto error; + + if (!ped_disk_add_partition (disk, new_part, constraint_any)) { + ped_partition_destroy (new_part); + goto error; + } + + if (disk_specific->format_type == 1 && part) { + /* + For LDL or CMS there may be trailing metadata as well. + For example: the last block of a CMS reserved file, + the "recomp" area of a CMS minidisk that has been + formatted and then formatted again with the RECOMP + option specifying fewer than the maximum number of + cylinders, a disk that was formatted at one size, + backed up, then restored to a larger size disk, etc. + */ + trailing_meta_start = part->geom.end + 1; + trailing_meta_end = (long long) disk->dev->length - 1; + if (trailing_meta_end >= trailing_meta_start) { + new_part2 = ped_partition_new (disk,PED_PARTITION_METADATA, + NULL, trailing_meta_start, trailing_meta_end); + if (!new_part2) { + ped_partition_destroy (new_part); + goto error; + } + if (!ped_disk_add_partition (disk, new_part2, + constraint_any)) { + ped_partition_destroy (new_part2); + ped_partition_destroy (new_part); + goto error; + } + } + } + + ped_constraint_destroy (constraint_any); + return 1; + +error: + ped_constraint_destroy (constraint_any); + return 0; +} diff --git a/libparted/labels/dos.c b/libparted/labels/dos.c new file mode 100644 index 0000000..e6a0105 --- /dev/null +++ b/libparted/labels/dos.c @@ -0,0 +1,2612 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2001, 2004-2005, 2007-2014, 2019-2023 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/>. +*/ + +#include <config.h> + +#include <sys/time.h> +#include <stdbool.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "misc.h" +#include "pt-tools.h" + +/* this MBR boot code is loaded into 0000:7c00 by the BIOS. See mbr.s for + * the source, and how to build it + */ + +static const char MBR_BOOT_CODE[] = { + 0xfa, 0xb8, 0x00, 0x10, 0x8e, 0xd0, 0xbc, 0x00, + 0xb0, 0xb8, 0x00, 0x00, 0x8e, 0xd8, 0x8e, 0xc0, + 0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9, + 0x00, 0x02, 0xf3, 0xa4, 0xea, 0x21, 0x06, 0x00, + 0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b, + 0x83, 0xc6, 0x10, 0x81, 0xfe, 0xfe, 0x07, 0x75, + 0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb, + 0x00, 0x7c, 0xb2, 0x80, 0x8a, 0x74, 0x01, 0x8b, + 0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00, + 0x00, 0xeb, 0xfe +}; + +#define MSDOS_MAGIC 0xAA55 +#define PARTITION_MAGIC_MAGIC 0xf6f6 + +/* The maximum number of DOS primary partitions. */ +#define DOS_N_PRI_PARTITIONS 4 + +#define PARTITION_EMPTY 0x00 +#define PARTITION_FAT12 0x01 +#define PARTITION_FAT16_SM 0x04 +#define PARTITION_DOS_EXT 0x05 +#define PARTITION_FAT16 0x06 +#define PARTITION_NTFS 0x07 +#define PARTITION_HPFS 0x07 +#define PARTITION_UDF 0x07 +#define PARTITION_FAT32 0x0b +#define PARTITION_FAT32_LBA 0x0c +#define PARTITION_FAT16_LBA 0x0e +#define PARTITION_EXT_LBA 0x0f + +#define PART_FLAG_HIDDEN 0x10 /* Valid for FAT/NTFS only */ +#define PARTITION_FAT12_H (PARTITION_FAT12 | PART_FLAG_HIDDEN) +#define PARTITION_FAT16_SM_H (PARTITION_FAT16_SM | PART_FLAG_HIDDEN) +#define PARTITION_DOS_EXT_H (PARTITION_DOS_EXT | PART_FLAG_HIDDEN) +#define PARTITION_FAT16_H (PARTITION_FAT16 | PART_FLAG_HIDDEN) +#define PARTITION_NTFS_H (PARTITION_NTFS | PART_FLAG_HIDDEN) +#define PARTITION_FAT32_H (PARTITION_FAT32 | PART_FLAG_HIDDEN) +#define PARTITION_FAT32_LBA_H (PARTITION_FAT32_LBA | PART_FLAG_HIDDEN) +#define PARTITION_FAT16_LBA_H (PARTITION_FAT16_LBA | PART_FLAG_HIDDEN) + +#define PARTITION_COMPAQ_DIAG 0x12 +#define PARTITION_MSFT_RECOVERY 0x27 +#define PARTITION_LDM 0x42 +#define PARTITION_LINUX_SWAP 0x82 +#define PARTITION_LINUX 0x83 +#define PARTITION_IRST 0x84 +#define PARTITION_LINUX_EXT 0x85 +#define PARTITION_LINUX_LVM 0x8e +#define PARTITION_HFS 0xaf +#define PARTITION_SUN_UFS 0xbf +#define PARTITION_DELL_DIAG 0xde +#define PARTITION_BLS_BOOT 0xea +#define PARTITION_GPT 0xee +#define PARTITION_ESP 0xef +#define PARTITION_PALO 0xf0 +#define PARTITION_PREP 0x41 +#define PARTITION_LINUX_RAID 0xfd +#define PARTITION_LINUX_LVM_OLD 0xfe + +struct flag_id_mapping_t +{ + enum _PedPartitionFlag flag; + unsigned char type_id; + unsigned char alt_type_id; +}; + +static const struct flag_id_mapping_t flag_id_mapping[] = +{ + { PED_PARTITION_BLS_BOOT, PARTITION_BLS_BOOT }, + { PED_PARTITION_DIAG, PARTITION_COMPAQ_DIAG, PARTITION_DELL_DIAG }, + { PED_PARTITION_ESP, PARTITION_ESP }, + { PED_PARTITION_IRST, PARTITION_IRST }, + { PED_PARTITION_LVM, PARTITION_LINUX_LVM, PARTITION_LINUX_LVM_OLD }, + { PED_PARTITION_MSFT_RESERVED, PARTITION_MSFT_RECOVERY }, + { PED_PARTITION_PALO, PARTITION_PALO }, + { PED_PARTITION_PREP, PARTITION_PREP }, + { PED_PARTITION_RAID, PARTITION_LINUX_RAID }, + { PED_PARTITION_SWAP, PARTITION_LINUX_SWAP }, +}; + +static const unsigned char skip_set_system_types[] = +{ + PARTITION_EXT_LBA, + PARTITION_DOS_EXT, + PARTITION_COMPAQ_DIAG, + PARTITION_MSFT_RECOVERY, + PARTITION_LINUX_LVM, + PARTITION_LINUX_SWAP, + PARTITION_LINUX_RAID, + PARTITION_PALO, + PARTITION_PREP, + PARTITION_IRST, + PARTITION_ESP, + PARTITION_BLS_BOOT +}; + +static const struct flag_id_mapping_t* _GL_ATTRIBUTE_CONST +dos_find_flag_id_mapping (PedPartitionFlag flag) +{ + int n = sizeof(flag_id_mapping) / sizeof(flag_id_mapping[0]); + + for (int i = 0; i < n; ++i) + if (flag_id_mapping[i].flag == flag) + return &flag_id_mapping[i]; + + return NULL; +} + +/** + * Check whether the type_id supports the hidden flag. Returns true for both hidden and + * non-hidden id. + */ +static bool +dos_type_id_supports_hidden(unsigned char type_id) +{ + switch (type_id) + { + case PARTITION_DOS_EXT: + case PARTITION_DOS_EXT_H: + case PARTITION_FAT12: + case PARTITION_FAT12_H: + case PARTITION_FAT16: + case PARTITION_FAT16_H: + case PARTITION_FAT16_LBA: + case PARTITION_FAT16_LBA_H: + case PARTITION_FAT16_SM: + case PARTITION_FAT16_SM_H: + case PARTITION_FAT32: + case PARTITION_FAT32_H: + case PARTITION_FAT32_LBA: + case PARTITION_FAT32_LBA_H: + case PARTITION_NTFS: + case PARTITION_NTFS_H: + return true; + + default: + return false; + } +} + +/** + * Check whether the type_id has the hidden flag set. + */ +static bool +dos_type_id_is_hidden(unsigned char type_id) +{ + switch (type_id) + { + case PARTITION_DOS_EXT_H: + case PARTITION_FAT12_H: + case PARTITION_FAT16_H: + case PARTITION_FAT16_LBA_H: + case PARTITION_FAT16_SM_H: + case PARTITION_FAT32_H: + case PARTITION_FAT32_LBA_H: + case PARTITION_NTFS_H: + return true; + + default: + return false; + } +} + +/** + * Sets the hidden flag on type_id. + */ +static bool +dos_type_id_set_hidden(unsigned char* type_id, bool state) +{ + PED_ASSERT (type_id); + + if (!dos_type_id_supports_hidden(*type_id)) + return false; + + if (state) + *type_id |= PART_FLAG_HIDDEN; + else + *type_id &= ~PART_FLAG_HIDDEN; + + return 1; +} + +/** + * Check whether the type_id supports the lba flag. Returns true for both lba and non-lba + * id. + */ +static bool +dos_type_id_supports_lba(unsigned char type_id) +{ + switch (type_id) + { + case PARTITION_FAT16: + case PARTITION_FAT16_H: + case PARTITION_FAT16_LBA: + case PARTITION_FAT16_LBA_H: + case PARTITION_FAT32: + case PARTITION_FAT32_H: + case PARTITION_FAT32_LBA: + case PARTITION_FAT32_LBA_H: + case PARTITION_DOS_EXT: + case PARTITION_EXT_LBA: + return true; + + default: + return false; + } +} + +/** + * Check whether the type_id has the lba flag set. + */ +static bool +dos_type_id_is_lba(unsigned char type_id) +{ + switch (type_id) + { + case PARTITION_FAT16_LBA: + case PARTITION_FAT16_LBA_H: + case PARTITION_FAT32_LBA: + case PARTITION_FAT32_LBA_H: + case PARTITION_EXT_LBA: + return true; + + default: + return false; + } +} + +/** + * Sets the lba flag on type_id. + */ +static bool +dos_type_id_set_lba(unsigned char* type_id, bool state) +{ + PED_ASSERT (type_id); + + if (!dos_type_id_supports_lba(*type_id)) + return false; + + if (state) + { + switch (*type_id) + { + case PARTITION_FAT16: + *type_id = PARTITION_FAT16_LBA; + break; + + case PARTITION_FAT32: + *type_id = PARTITION_FAT32_LBA; + break; + + case PARTITION_DOS_EXT: + *type_id = PARTITION_EXT_LBA; + break; + } + } + else + { + switch (*type_id) + { + case PARTITION_FAT16_LBA: + *type_id = PARTITION_FAT16; + break; + + case PARTITION_FAT32_LBA: + *type_id = PARTITION_FAT32; + break; + + case PARTITION_EXT_LBA: + *type_id = PARTITION_DOS_EXT; + break; + } + } + + return true; +} + + +/* This constant contains the maximum cylinder number that can be represented + * in (C,H,S) notation. Higher cylinder numbers are reserved for + * "too big" indicators (in which case only LBA addressing can be used). + * Some partition tables in the wild indicate this number is 1021. + * (i.e. 1022 is sometimes used to indicate "use LBA"). + */ +#define MAX_CHS_CYLINDER 1021 +#define MAX_TOTAL_PART 64 + +typedef struct _DosRawPartition DosRawPartition; +typedef struct _DosRawTable DosRawTable; + +/* note: lots of bit-bashing here, thus, you shouldn't look inside it. + * Use chs_to_sector() and sector_to_chs() instead. + */ +typedef struct { + uint8_t head; + uint8_t sector; + uint8_t cylinder; +} __attribute__((packed)) RawCHS; + +/* ripped from Linux source */ +struct _DosRawPartition { + uint8_t boot_ind; /* 00: 0x80 - active */ + RawCHS chs_start; /* 01: */ + uint8_t type; /* 04: partition type */ + RawCHS chs_end; /* 05: */ + uint32_t start; /* 08: starting sector counting from 0 */ + uint32_t length; /* 0c: nr of sectors in partition */ +} __attribute__((packed)); + +struct _DosRawTable { + char boot_code [440]; + uint32_t mbr_signature; /* really a unique ID */ + uint16_t Unknown; + DosRawPartition partitions [DOS_N_PRI_PARTITIONS]; + uint16_t magic; +} __attribute__((packed)); + +/* OrigState is information we want to preserve about the partition for + * dealing with CHS issues + */ +typedef struct { + PedGeometry geom; + DosRawPartition raw_part; + PedSector lba_offset; /* needed for computing start/end for + * logical partitions */ +} OrigState; + +typedef struct { + int cylinder_alignment; +} DosDiskData; + +typedef struct { + unsigned char system; + int boot; + OrigState* orig; /* used for CHS stuff */ +} DosPartitionData; + +static PedDiskType msdos_disk_type; + +#if 0 +From http://www.win.tue.nl/~aeb/linux/fs/fat/fat-1.html + +The 2-byte numbers are stored little endian (low order byte first). + +Here the FAT12 version, that is also the common part of the FAT12, FAT16 and FAT32 boot sectors. See further below. + +Bytes Content +0-2 Jump to bootstrap (E.g. eb 3c 90; on i86: JMP 003E NOP. + One finds either eb xx 90, or e9 xx xx. + The position of the bootstrap varies.) +3-10 OEM name/version (E.g. "IBM 3.3", "IBM 20.0", "MSDOS5.0", "MSWIN4.0". + Various format utilities leave their own name, like "CH-FOR18". + Sometimes just garbage. Microsoft recommends "MSWIN4.1".) + /* BIOS Parameter Block starts here */ +11-12 Number of bytes per sector (512) + Must be one of 512, 1024, 2048, 4096. +13 Number of sectors per cluster (1) + Must be one of 1, 2, 4, 8, 16, 32, 64, 128. + A cluster should have at most 32768 bytes. In rare cases 65536 is OK. +14-15 Number of reserved sectors (1) + FAT12 and FAT16 use 1. FAT32 uses 32. +16 Number of FAT copies (2) +17-18 Number of root directory entries (224) + 0 for FAT32. 512 is recommended for FAT16. +19-20 Total number of sectors in the filesystem (2880) + (in case the partition is not FAT32 and smaller than 32 MB) +21 Media descriptor type (f0: 1.4 MB floppy, f8: hard disk; see below) +22-23 Number of sectors per FAT (9) + 0 for FAT32. +24-25 Number of sectors per track (12) +26-27 Number of heads (2, for a double-sided diskette) +28-29 Number of hidden sectors (0) + Hidden sectors are sectors preceding the partition. + /* BIOS Parameter Block ends here */ +30-509 Bootstrap +510-511 Signature 55 aa +#endif + +/* There is a significant risk of misclassifying (as msdos) + a disk that is composed solely of a single FAT partition. + Return false if sector S could not be a valid FAT boot sector. + Otherwise, return true. */ +static bool +maybe_FAT (unsigned char const *s) +{ + if (! (s[0] == 0xeb || s[0] == 0xe9)) + return false; + + uint16_t sector_size = (s[12] << 8) | s[11]; + switch (sector_size) + { + case 512: + case 1024: + case 2048: + case 4096: + break; + default: + return false; + } + + if (! (s[21] == 0xf0 || s[21] == 0xf8)) + return false; + + return true; +} + +PedGeometry* +fat_probe_fat16 (PedGeometry* geom); + +PedGeometry* +fat_probe_fat32 (PedGeometry* geom); + +PedGeometry* +ntfs_probe (PedGeometry* geom); + +static int +msdos_probe (const PedDevice *dev) +{ + PedDiskType* disk_type; + DosRawTable* part_table; + int i; + PedGeometry *geom = NULL; + PedGeometry *fsgeom = NULL; + + PED_ASSERT (dev != NULL); + + if (dev->sector_size < sizeof *part_table) + return 0; + + void *label; + if (!ptt_read_sector (dev, 0, &label)) + return 0; + + part_table = (DosRawTable *) label; + + /* check magic */ + if (PED_LE16_TO_CPU (part_table->magic) != MSDOS_MAGIC) + goto probe_fail; + + geom = ped_geometry_new (dev, 0, dev->length); + PED_ASSERT (geom); + fsgeom = fat_probe_fat16 (geom); + if (fsgeom) + goto probe_fail; /* fat fs looks like dos mbr */ + fsgeom = fat_probe_fat32 (geom); + if (fsgeom) + goto probe_fail; /* fat fs looks like dos mbr */ + fsgeom = ntfs_probe (geom); + if (fsgeom) + goto probe_fail; /* ntfs fs looks like dos mbr */ + ped_geometry_destroy (geom); + geom = NULL; + + /* If this is a FAT fs, fail here. Checking for the FAT signature + * has some false positives; instead, do what the Linux kernel does + * and ensure that each partition has a boot indicator that is + * either 0 or 0x80. + */ + unsigned int n_active = 0; + for (i = 0; i < DOS_N_PRI_PARTITIONS; i++) { + if (part_table->partitions[i].boot_ind == 0x80) + ++n_active; + if (part_table->partitions[i].boot_ind != 0 + && part_table->partitions[i].boot_ind != 0x80) + goto probe_fail; + } + + /* If there are no active partitions and this is probably + a FAT file system, do not classify it as msdos. */ + if (n_active == 0 && maybe_FAT (label)) + goto probe_fail; + + /* If this is a GPT disk, fail here */ + for (i = 0; i < DOS_N_PRI_PARTITIONS; i++) { + if (part_table->partitions[i].type == PARTITION_GPT) + goto probe_fail; + } + + /* If this is an AIX Physical Volume, fail here. IBMA in EBCDIC */ + if (part_table->boot_code[0] == (char) 0xc9 && + part_table->boot_code[1] == (char) 0xc2 && + part_table->boot_code[2] == (char) 0xd4 && + part_table->boot_code[3] == (char) 0xc1) + goto probe_fail; + +#ifdef ENABLE_PC98 + /* HACK: it's impossible to tell PC98 and msdos disk labels apart. + * Someone made the signatures the same (very clever). Since + * PC98 has some idiosyncracies with it's boot-loader, it's detection + * is more reliable */ + disk_type = ped_disk_type_get ("pc98"); + if (disk_type && disk_type->ops->probe (dev)) + goto probe_fail; +#endif /* ENABLE_PC98 */ + + free (label); + return 1; + + probe_fail: + if (geom) + ped_geometry_destroy (geom); + if (fsgeom) + ped_geometry_destroy (fsgeom); + free (label); + return 0; +} + +static PedDisk* +msdos_alloc (const PedDevice* dev) +{ + PedDisk* disk; + PED_ASSERT (dev != NULL); + + disk = _ped_disk_alloc ((PedDevice*)dev, &msdos_disk_type); + if (disk) { + DosDiskData *disk_specific = ped_malloc(sizeof *disk_specific); + if (!disk_specific) { + free (disk); + return NULL; + } + disk_specific->cylinder_alignment = 1; + disk->disk_specific = disk_specific; + } + + return disk; +} + +static PedDisk* +msdos_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + + new_disk = ped_disk_new_fresh (disk->dev, &msdos_disk_type); + if (!new_disk) + return NULL; + + memcpy(new_disk->disk_specific, disk->disk_specific, + sizeof(DosDiskData)); + + return new_disk; +} + +static void +msdos_free (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + + DosDiskData *disk_specific = disk->disk_specific; + _ped_disk_free (disk); + free(disk_specific); +} + +static int +msdos_disk_set_flag (PedDisk *disk, PedDiskFlag flag, int state) +{ + DosDiskData *disk_specific = disk->disk_specific; + switch (flag) { + case PED_DISK_CYLINDER_ALIGNMENT: + disk_specific->cylinder_alignment = !!state; + return 1; + default: + return 0; + } +} + +static int +msdos_disk_get_flag (const PedDisk *disk, PedDiskFlag flag) +{ + DosDiskData *disk_specific = disk->disk_specific; + switch (flag) { + case PED_DISK_CYLINDER_ALIGNMENT: + return disk_specific->cylinder_alignment; + default: + return 0; + } +} + +static int +msdos_disk_is_flag_available (const PedDisk *disk, PedDiskFlag flag) +{ + switch (flag) { + case PED_DISK_CYLINDER_ALIGNMENT: + return 1; + default: + return 0; + } +} + +static int +chs_get_cylinder (const RawCHS* chs) +{ + return chs->cylinder + ((chs->sector >> 6) << 8); +} + +static int +chs_get_head (const RawCHS* chs) +{ + return chs->head; +} + +/* counts from 0 */ +static int +chs_get_sector (const RawCHS* chs) +{ + return (chs->sector & 0x3f) - 1; +} + +static PedSector _GL_ATTRIBUTE_PURE +chs_to_sector (const PedDevice* dev, const PedCHSGeometry *bios_geom, + const RawCHS* chs) +{ + PedSector c; /* not measured in sectors, but need */ + PedSector h; /* lots of bits */ + PedSector s; + + PED_ASSERT (bios_geom != NULL); + PED_ASSERT (chs != NULL); + + c = chs_get_cylinder (chs); + h = chs_get_head (chs); + s = chs_get_sector (chs); + + if (c > MAX_CHS_CYLINDER) /* MAGIC: C/H/S is irrelevant */ + return 0; + if (s < 0) + return 0; + return (c * bios_geom->heads + h) * bios_geom->sectors + s; +} + +static void +sector_to_chs (const PedDevice* dev, const PedCHSGeometry* bios_geom, + PedSector sector, RawCHS* chs) +{ + PedSector real_c, real_h, real_s; + + PED_ASSERT (dev != NULL); + PED_ASSERT (chs != NULL); + + if (!bios_geom) + bios_geom = &dev->bios_geom; + + real_c = sector / (bios_geom->heads * bios_geom->sectors); + real_h = (sector / bios_geom->sectors) % bios_geom->heads; + real_s = sector % bios_geom->sectors; + + if (real_c > MAX_CHS_CYLINDER) { + real_c = 1023; + real_h = bios_geom->heads - 1; + real_s = bios_geom->sectors - 1; + } + + chs->cylinder = real_c % 0x100; + chs->head = real_h; + chs->sector = real_s + 1 + (real_c >> 8 << 6); +} + +static PedSector _GL_ATTRIBUTE_PURE +legacy_start (const PedDisk* disk, const PedCHSGeometry* bios_geom, + const DosRawPartition* raw_part) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (raw_part != NULL); + + return chs_to_sector (disk->dev, bios_geom, &raw_part->chs_start); +} + +static PedSector _GL_ATTRIBUTE_PURE +legacy_end (const PedDisk* disk, const PedCHSGeometry* bios_geom, + const DosRawPartition* raw_part) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (raw_part != NULL); + + return chs_to_sector (disk->dev, bios_geom, &raw_part->chs_end); +} + +static PedSector _GL_ATTRIBUTE_PURE +linear_start (const PedDisk* disk, const DosRawPartition* raw_part, + PedSector offset) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (raw_part != NULL); + + return offset + PED_LE32_TO_CPU (raw_part->start); +} + +static PedSector _GL_ATTRIBUTE_PURE +linear_end (const PedDisk* disk, const DosRawPartition* raw_part, + PedSector offset) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (raw_part != NULL); + + return (linear_start (disk, raw_part, offset) + + (PED_LE32_TO_CPU (raw_part->length) - 1)); +} + +#ifndef DISCOVER_ONLY +static int _GL_ATTRIBUTE_PURE +partition_check_bios_geometry (PedPartition* part, PedCHSGeometry* bios_geom) +{ + PedSector leg_start, leg_end; + DosPartitionData* dos_data; + PedDisk* disk; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk_specific != NULL); + dos_data = part->disk_specific; + + if (!dos_data->orig) + return 1; + + disk = part->disk; + leg_start = legacy_start (disk, bios_geom, &dos_data->orig->raw_part); + leg_end = legacy_end (disk, bios_geom, &dos_data->orig->raw_part); + + if (leg_start && leg_start != dos_data->orig->geom.start) + return 0; + if (leg_end && leg_end != dos_data->orig->geom.end) + return 0; + return 1; +} + +static int _GL_ATTRIBUTE_PURE +disk_check_bios_geometry (const PedDisk* disk, PedCHSGeometry* bios_geom) +{ + PedPartition* part = NULL; + + PED_ASSERT (disk != NULL); + + while ((part = ped_disk_next_partition (disk, part))) { + if (ped_partition_is_active (part)) { + if (!partition_check_bios_geometry (part, bios_geom)) + return 0; + } + } + + return 1; +} + +static int +probe_filesystem_for_geom (const PedPartition* part, PedCHSGeometry* bios_geom) +{ + const char* ms_types[] = {"ntfs", "fat16", "fat32", NULL}; + int i; + int found; + unsigned char* buf; + int sectors; + int heads; + int res = 0; + + PED_ASSERT (bios_geom != NULL); + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->dev != NULL); + PED_ASSERT (part->disk->dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + + buf = ped_malloc (part->disk->dev->sector_size); + + if (!buf) + return 0; + + if (!part->fs_type) + goto end; + + found = 0; + for (i = 0; ms_types[i]; i++) { + if (!strcmp(ms_types[i], part->fs_type->name)) + found = 1; + } + if (!found) + goto end; + + if (!ped_geometry_read(&part->geom, buf, 0, 1)) + goto end; + + /* shared by the start of all Microsoft file systems */ + sectors = buf[0x18] + (buf[0x19] << 8); + heads = buf[0x1a] + (buf[0x1b] << 8); + + if (sectors < 1 || sectors > 63) + goto end; + if (heads > 255 || heads < 1) + goto end; + + bios_geom->sectors = sectors; + bios_geom->heads = heads; + bios_geom->cylinders = part->disk->dev->length / (sectors * heads); + res = 1; +end: + free(buf); + return res; +} + +/* This function attempts to infer the BIOS CHS geometry of the hard disk + * from the CHS + LBA information contained in the partition table from + * a single partition's entry. + * + * This involves some maths. Let (c,h,s,a) be the starting cylinder, + * starting head, starting sector and LBA start address of the partition. + * Likewise, (C,H,S,A) the end addresses. Using both of these pieces + * of information, we want to deduce cyl_sectors and head_sectors which + * are the sizes of a single cylinder and a single head, respectively. + * + * The relationships are: + * c*cyl_sectors + h * head_sectors + s = a + * C*cyl_sectors + H * head_sectors + S = A + * + * We can rewrite this in matrix form: + * + * [ c h ] [ cyl_sectors ] = [ s - a ] = [ a_ ] + * [ C H ] [ head_sectors ] [ S - A ] [ A_ ]. + * + * (s - a is abbreviated to a_to simplify the notation.) + * + * This can be abbreviated into augmented matrix form: + * + * [ c h | a_ ] + * [ C H | A_ ]. + * + * Solving these equations requires following the row reduction algorithm. We + * need to be careful about a few things though: + * - the equations might be linearly dependent, in which case there + * are many solutions. + * - the equations might be inconsistent, in which case there + * are no solutions. (Inconsistent partition table entry!) + * - there might be zeros, so we need to be careful about applying + * the algorithm. We know, however, that C > 0. + */ +static int +probe_partition_for_geom (const PedPartition* part, PedCHSGeometry* bios_geom) +{ + DosPartitionData* dos_data; + RawCHS* start_chs; + RawCHS* end_chs; + PedSector c, h, s, a, a_; /* start */ + PedSector C, H, S, A, A_; /* end */ + PedSector dont_overflow, denum; + PedSector cyl_size, head_size; + PedSector cylinders, heads, sectors; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + PED_ASSERT (bios_geom != NULL); + + dos_data = part->disk_specific; + + if (!dos_data->orig) + return 0; + + start_chs = &dos_data->orig->raw_part.chs_start; + c = chs_get_cylinder (start_chs); + h = chs_get_head (start_chs); + s = chs_get_sector (start_chs); + a = dos_data->orig->geom.start; + a_ = a - s; + + end_chs = &dos_data->orig->raw_part.chs_end; + C = chs_get_cylinder (end_chs); + H = chs_get_head (end_chs); + S = chs_get_sector (end_chs); + A = dos_data->orig->geom.end; + A_ = A - S; + + if (h < 0 || H < 0 || h > 254 || H > 254) + return 0; + if (c > C) + return 0; + + /* If no geometry is feasible, then don't even bother. + * Useful for eliminating assertions for broken partition + * tables generated by Norton Ghost et al. + */ + if (A > (C+1) * 255 * 63) + return 0; + + /* Not enough information. In theory, we can do better. Should we? */ + if (C > MAX_CHS_CYLINDER) + return 0; + if (C == 0) + return 0; + + /* Calculate the maximum number that can be multiplied by + * any head count without overflowing a PedSector + * 2^8 = 256, 8 bits + 1(sign bit) = 9 + */ + dont_overflow = 1; + dont_overflow <<= (8*sizeof(dont_overflow)) - 9; + dont_overflow--; + + if (a_ > dont_overflow || A_ > dont_overflow) + return 0; + + /* The matrix is solved by : + * + * [ c h | a_] R1 + * [ C H | A_] R2 + * + * (cH - Ch) cyl_size = a_H - A_h H R1 - h R2 + * => (if cH - Ch != 0) cyl_size = (a_H - A_h) / (cH - Ch) + * + * (Hc - hC) head_size = A_c - a_C c R2 - C R1 + * => (if cH - Ch != 0) head_size = (A_c - a_C) / (cH - Ch) + * + * But this calculation of head_size would need + * not overflowing A_c or a_C + * So substitution is use instead, to minimize dimension + * of temporary results : + * + * If h != 0 : head_size = ( a_ - c cyl_size ) / h + * If H != 0 : head_size = ( A_ - C cyl_size ) / H + * + */ + denum = c * H - C * h; + if (denum == 0) + return 0; + + cyl_size = (a_*H - A_*h) / denum; + /* Check for non integer result */ + if (cyl_size * denum != a_*H - A_*h) + return 0; + + if (!(cyl_size > 0)) + return 0; + if (!(cyl_size <= 255 * 63)) + return 0; + + if (h > 0) + head_size = ( a_ - c * cyl_size ) / h; + else if (H > 0) + head_size = ( A_ - C * cyl_size ) / H; + else { + /* should not happen because denum != 0 */ + PED_ASSERT (0); + } + + if (!(head_size > 0)) + return 0; + if (!(head_size <= 63)) + return 0; + + cylinders = part->disk->dev->length / cyl_size; + heads = cyl_size / head_size; + sectors = head_size; + + if (!(heads > 0)) + return 0; + if (!(heads < 256)) + return 0; + + if (!(sectors > 0)) + return 0; + if (!(sectors <= 63)) + return 0; + + /* Some broken OEM partitioning program(s) seem to have an out-by-one + * error on the end of partitions. We should offer to fix the + * partition table... + */ + if (((C + 1) * heads + H) * sectors + S == A) + C++; + + if (!((c * heads + h) * sectors + s == a)) + return 0; + if (!((C * heads + H) * sectors + S == A)) + return 0; + + bios_geom->cylinders = cylinders; + bios_geom->heads = heads; + bios_geom->sectors = sectors; + + return 1; +} + +static void +partition_probe_bios_geometry (const PedPartition* part, + PedCHSGeometry* bios_geom) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (bios_geom != NULL); + + if (ped_partition_is_active (part)) { + if (probe_partition_for_geom (part, bios_geom)) + return; + if (part->type & PED_PARTITION_EXTENDED) { + if (probe_filesystem_for_geom (part, bios_geom)) + return; + } + } + if (part->type & PED_PARTITION_LOGICAL) { + PedPartition* ext_part; + ext_part = ped_disk_extended_partition (part->disk); + PED_ASSERT (ext_part != NULL); + partition_probe_bios_geometry (ext_part, bios_geom); + } else { + *bios_geom = part->disk->dev->bios_geom; + } +} + +static void +disk_probe_bios_geometry (const PedDisk* disk, PedCHSGeometry* bios_geom) +{ + PedPartition* part; + + /* first look at the boot partition */ + part = NULL; + while ((part = ped_disk_next_partition (disk, part))) { + if (!ped_partition_is_active (part)) + continue; + if (ped_partition_get_flag (part, PED_PARTITION_BOOT)) { + if (probe_filesystem_for_geom (part, bios_geom)) + return; + if (probe_partition_for_geom (part, bios_geom)) + return; + } + } + + /* that didn't work... try all partition table entries */ + part = NULL; + while ((part = ped_disk_next_partition (disk, part))) { + if (ped_partition_is_active (part)) { + if (probe_partition_for_geom (part, bios_geom)) + return; + } + } + + /* that didn't work... look at all file systems */ + part = NULL; + while ((part = ped_disk_next_partition (disk, part))) { + if (ped_partition_is_active (part)) { + if (probe_filesystem_for_geom (part, bios_geom)) + return; + } + } +} +#endif /* !DISCOVER_ONLY */ + +static int _GL_ATTRIBUTE_PURE +raw_part_is_extended (const DosRawPartition* raw_part) +{ + PED_ASSERT (raw_part != NULL); + + switch (raw_part->type) { + case PARTITION_DOS_EXT: + case PARTITION_EXT_LBA: + case PARTITION_LINUX_EXT: + return 1; + + default: + return 0; + } + + return 0; +} + +static PedPartition* +raw_part_parse (const PedDisk* disk, const DosRawPartition* raw_part, + PedSector lba_offset, PedPartitionType type) +{ + PedPartition* part; + DosPartitionData* dos_data; + + PED_ASSERT (disk != NULL); + PED_ASSERT (raw_part != NULL); + + part = ped_partition_new ( + disk, type, NULL, + linear_start (disk, raw_part, lba_offset), + linear_end (disk, raw_part, lba_offset)); + if (!part) + return NULL; + dos_data = part->disk_specific; + dos_data->system = raw_part->type; + dos_data->boot = raw_part->boot_ind != 0; + dos_data->orig = ped_malloc (sizeof (OrigState)); + if (!dos_data->orig) { + ped_partition_destroy (part); + return NULL; + } + dos_data->orig->geom = part->geom; + dos_data->orig->raw_part = *raw_part; + dos_data->orig->lba_offset = lba_offset; + return part; +} + +static int +read_table (PedDisk* disk, PedSector sector, int is_extended_table) +{ + int i; + DosRawTable* table; + DosRawPartition* raw_part; + PedPartition* part; + PedPartitionType type; + PedSector lba_offset; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + + void *label = NULL; + if (!ptt_read_sector (disk->dev, sector, &label)) + goto error; + + table = (DosRawTable *) label; + + /* weird: empty extended partitions are filled with 0xf6 by PM */ + if (is_extended_table + && PED_LE16_TO_CPU (table->magic) == PARTITION_MAGIC_MAGIC) + goto read_ok; + +#ifndef DISCOVER_ONLY + if (PED_LE16_TO_CPU (table->magic) != MSDOS_MAGIC) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL, + _("Invalid partition table on %s " + "-- wrong signature %x."), + disk->dev->path, + PED_LE16_TO_CPU (table->magic)) + != PED_EXCEPTION_IGNORE) + goto error; + goto read_ok; + } +#endif + + /* parse the partitions from this table */ + for (i = 0; i < DOS_N_PRI_PARTITIONS; i++) { + raw_part = &table->partitions [i]; + if (raw_part->type == PARTITION_EMPTY || !raw_part->length) + continue; + + /* process nested extended partitions after normal logical + * partitions, to make sure we get the order right. + */ + if (is_extended_table && raw_part_is_extended (raw_part)) + continue; + + lba_offset = is_extended_table ? sector : 0; + + if (linear_start (disk, raw_part, lba_offset) == sector) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Invalid partition table - recursive " + "partition on %s."), + disk->dev->path) + != PED_EXCEPTION_IGNORE) + goto error; + continue; /* avoid infinite recursion */ + } + + if (is_extended_table) + type = PED_PARTITION_LOGICAL; + else if (raw_part_is_extended (raw_part)) + type = PED_PARTITION_EXTENDED; + else + type = PED_PARTITION_NORMAL; + + part = raw_part_parse (disk, raw_part, lba_offset, type); + if (!part) + goto error; + if (!is_extended_table) + part->num = i + 1; + if (type != PED_PARTITION_EXTENDED) + part->fs_type = ped_file_system_probe (&part->geom); + + PedConstraint *constraint_exact + = ped_constraint_exact (&part->geom); + bool ok = ped_disk_add_partition (disk, part, constraint_exact); + ped_constraint_destroy (constraint_exact); + if (!ok) + goto error; + + /* non-nested extended partition */ + if (part->type == PED_PARTITION_EXTENDED) { + if (!read_table (disk, part->geom.start, 1)) + goto error; + } + } + + if (is_extended_table) { + /* process the nested extended partitions */ + for (i = 0; i < DOS_N_PRI_PARTITIONS; i++) { + PedSector part_start; + + raw_part = &table->partitions [i]; + if (!raw_part_is_extended (raw_part)) + continue; + + lba_offset = ped_disk_extended_partition + (disk)->geom.start; + part_start = linear_start (disk, raw_part, lba_offset); + if (part_start == sector) { + /* recursive table - already threw an + * exception above. + */ + continue; + } + if (!read_table (disk, part_start, 1)) + goto error; + } + } + +read_ok: + free (label); + return 1; + +error: + free (label); + ped_disk_delete_all (disk); + return 0; +} + +static int +msdos_read (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + + ped_disk_delete_all (disk); + if (!read_table (disk, 0, 0)) + return 0; + +#ifndef DISCOVER_ONLY + /* try to figure out the correct BIOS CHS values */ + if (!disk_check_bios_geometry (disk, &disk->dev->bios_geom)) { + PedCHSGeometry bios_geom = disk->dev->bios_geom; + disk_probe_bios_geometry (disk, &bios_geom); + + /* if the geometry was wrong, then we should reread, to + * make sure the metadata is allocated in the right places. + */ + if (disk->dev->bios_geom.cylinders != bios_geom.cylinders + || disk->dev->bios_geom.heads != bios_geom.heads + || disk->dev->bios_geom.sectors != bios_geom.sectors) { + disk->dev->bios_geom = bios_geom; + return msdos_read (disk); + } + } +#endif + + return 1; +} + +#ifndef DISCOVER_ONLY +static int +fill_raw_part (DosRawPartition* raw_part, + const PedPartition* part, PedSector offset) +{ + DosPartitionData* dos_data; + PedCHSGeometry bios_geom; + + PED_ASSERT (raw_part != NULL); + PED_ASSERT (part != NULL); + + partition_probe_bios_geometry (part, &bios_geom); + + dos_data = part->disk_specific; + + raw_part->boot_ind = 0x80 * dos_data->boot; + raw_part->type = dos_data->system; + raw_part->start = PED_CPU_TO_LE32 (part->geom.start - offset); + raw_part->length = PED_CPU_TO_LE32 (part->geom.length); + + sector_to_chs (part->disk->dev, &bios_geom, part->geom.start, + &raw_part->chs_start); + sector_to_chs (part->disk->dev, &bios_geom, part->geom.end, + &raw_part->chs_end); + + if (dos_data->orig) { + DosRawPartition* orig_raw_part = &dos_data->orig->raw_part; + if (dos_data->orig->geom.start == part->geom.start) + raw_part->chs_start = orig_raw_part->chs_start; + if (dos_data->orig->geom.end == part->geom.end) + raw_part->chs_end = orig_raw_part->chs_end; + } + + return 1; +} + +static int +fill_ext_raw_part_geom (DosRawPartition* raw_part, + const PedCHSGeometry* bios_geom, + const PedGeometry* geom, PedSector offset) +{ + PED_ASSERT (raw_part != NULL); + PED_ASSERT (geom != NULL); + PED_ASSERT (geom->dev != NULL); + + raw_part->boot_ind = 0; + raw_part->type = PARTITION_DOS_EXT; + raw_part->start = PED_CPU_TO_LE32 (geom->start - offset); + raw_part->length = PED_CPU_TO_LE32 (geom->length); + + sector_to_chs (geom->dev, bios_geom, geom->start, &raw_part->chs_start); + sector_to_chs (geom->dev, bios_geom, geom->start + geom->length - 1, + &raw_part->chs_end); + + return 1; +} + +static int +write_ext_table (const PedDisk* disk, + PedSector sector, const PedPartition* logical) +{ + PedPartition* part; + PedSector lba_offset; + + PED_ASSERT (disk != NULL); + PED_ASSERT (ped_disk_extended_partition (disk) != NULL); + PED_ASSERT (logical != NULL); + + lba_offset = ped_disk_extended_partition (disk)->geom.start; + + void* s; + if (!ptt_read_sector (disk->dev, sector, &s)) + return 0; + + DosRawTable *table = s; + memset(&(table->partitions), 0, sizeof (table->partitions)); + table->magic = PED_CPU_TO_LE16 (MSDOS_MAGIC); + + int ok = 0; + if (!fill_raw_part (&table->partitions[0], logical, sector)) + goto cleanup; + + part = ped_disk_get_partition (disk, logical->num + 1); + if (part) { + PedGeometry* geom; + PedCHSGeometry bios_geom; + + geom = ped_geometry_new (disk->dev, part->prev->geom.start, + part->geom.end - part->prev->geom.start + 1); + if (!geom) + goto cleanup; + partition_probe_bios_geometry (part, &bios_geom); + fill_ext_raw_part_geom (&table->partitions[1], &bios_geom, + geom, lba_offset); + ped_geometry_destroy (geom); + + if (!write_ext_table (disk, part->prev->geom.start, part)) + goto cleanup; + } + + ok = ped_device_write (disk->dev, table, sector, 1); + cleanup: + free (s); + return ok; +} + +static int +write_empty_table (const PedDisk* disk, PedSector sector) +{ + DosRawTable table; + void* table_sector; + + PED_ASSERT (disk != NULL); + + if (ptt_read_sector (disk->dev, sector, &table_sector)) { + memcpy (&table, table_sector, sizeof (table)); + free(table_sector); + } + memset (&(table.partitions), 0, sizeof (table.partitions)); + table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC); + + return ped_device_write (disk->dev, (void*) &table, sector, 1); +} + +/* Find the first logical partition, and write the partition table for it. + */ +static int +write_extended_partitions (const PedDisk* disk) +{ + PedPartition* ext_part; + PedPartition* part; + PedCHSGeometry bios_geom; + + PED_ASSERT (disk != NULL); + + ext_part = ped_disk_extended_partition (disk); + partition_probe_bios_geometry (ext_part, &bios_geom); + part = ped_disk_get_partition (disk, 5); + if (part) + return write_ext_table (disk, ext_part->geom.start, part); + else + return write_empty_table (disk, ext_part->geom.start); +} + +static int +msdos_write (const PedDisk* disk) +{ + PedPartition* part; + int i; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + + void *s0; + if (!ptt_read_sector (disk->dev, 0, &s0)) + return 0; + DosRawTable *table = (DosRawTable *) s0; + + if (!table->boot_code[0]) { + memset (table, 0, 512); + memcpy (table->boot_code, MBR_BOOT_CODE, sizeof (MBR_BOOT_CODE)); + } + + /* If there is no unique identifier, generate a random one */ + if (!table->mbr_signature) + table->mbr_signature = generate_random_uint32 (); + + memset (table->partitions, 0, sizeof (table->partitions)); + table->magic = PED_CPU_TO_LE16 (MSDOS_MAGIC); + + for (i=1; i<=DOS_N_PRI_PARTITIONS; i++) { + part = ped_disk_get_partition (disk, i); + if (!part) + continue; + + if (!fill_raw_part (&table->partitions [i - 1], part, 0)) + goto write_fail; + + if (part->type == PED_PARTITION_EXTENDED) { + if (!write_extended_partitions (disk)) + goto write_fail; + } + } + + int write_ok = ped_device_write (disk->dev, (void*) table, 0, 1); + free (s0); + if (!write_ok) + return 0; + return ped_device_sync (disk->dev); + + write_fail: + free (s0); + return 0; + +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +msdos_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + DosPartitionData* dos_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (ped_partition_is_active (part)) { + part->disk_specific + = dos_data = ped_calloc (sizeof (DosPartitionData)); + if (!dos_data) + goto error_free_part; + dos_data->system = PARTITION_LINUX; + } else { + part->disk_specific = NULL; + } + return part; + +error_free_part: + free (part); +error: + return 0; +} + +static PedPartition* +msdos_partition_duplicate (const PedPartition* part) +{ + PedPartition* new_part; + DosPartitionData* new_dos_data; + DosPartitionData* old_dos_data; + + new_part = ped_partition_new (part->disk, part->type, part->fs_type, + part->geom.start, part->geom.end); + if (!new_part) + return NULL; + new_part->num = part->num; + + old_dos_data = (DosPartitionData*) part->disk_specific; + new_dos_data = (DosPartitionData*) new_part->disk_specific; + new_dos_data->system = old_dos_data->system; + new_dos_data->boot = old_dos_data->boot; + + if (old_dos_data->orig) { + new_dos_data->orig = ped_malloc (sizeof (OrigState)); + if (!new_dos_data->orig) { + ped_partition_destroy (new_part); + return NULL; + } + new_dos_data->orig->geom = old_dos_data->orig->geom; + new_dos_data->orig->raw_part = old_dos_data->orig->raw_part; + new_dos_data->orig->lba_offset = old_dos_data->orig->lba_offset; + } + return new_part; +} + +static void +msdos_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL); + + if (ped_partition_is_active (part)) { + DosPartitionData* dos_data; + dos_data = (DosPartitionData*) part->disk_specific; + free (dos_data->orig); + free (part->disk_specific); + } + free (part); +} + +/* is_skip_type checks the type against the list of types that should not be + * overridden by set_system. It returns a 1 if it is in the list. +*/ +static bool +is_skip_type(unsigned char type_id) { + int n = sizeof(skip_set_system_types) / sizeof(skip_set_system_types[0]); + for (int i = 0; i < n; ++i) { + if (type_id == skip_set_system_types[i]) { + return true; + } + } + + return false; +} + +static int +msdos_partition_set_system (PedPartition* part, + const PedFileSystemType* fs_type) +{ + DosPartitionData* dos_data = part->disk_specific; + + part->fs_type = fs_type; + + // Is this a type that should skip fs_type checking? + if (is_skip_type(dos_data->system)) { + return 1; + } + + if (part->type & PED_PARTITION_EXTENDED) { + dos_data->system = PARTITION_EXT_LBA; + return 1; + } + + if (!fs_type) + dos_data->system = PARTITION_LINUX; + else if (!strcmp (fs_type->name, "fat16")) + dos_data->system = PARTITION_FAT16; + else if (!strcmp (fs_type->name, "fat32")) + dos_data->system = PARTITION_FAT32; + else if (!strcmp (fs_type->name, "ntfs") + || !strcmp (fs_type->name, "hpfs")) + dos_data->system = PARTITION_NTFS; + else if (!strcmp (fs_type->name, "hfs") + || !strcmp (fs_type->name, "hfs+")) + dos_data->system = PARTITION_HFS; + else if (!strcmp (fs_type->name, "udf")) + dos_data->system = PARTITION_UDF; + else if (!strcmp (fs_type->name, "sun-ufs")) + dos_data->system = PARTITION_SUN_UFS; + else if (is_linux_swap (fs_type->name)) + dos_data->system = PARTITION_LINUX_SWAP; + else + dos_data->system = PARTITION_LINUX; + + return 1; +} + +static int +msdos_partition_set_flag (PedPartition* part, + PedPartitionFlag flag, int state) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + PED_ASSERT (part->disk != NULL); + + DosPartitionData* dos_data = part->disk_specific; + + const struct flag_id_mapping_t* p = dos_find_flag_id_mapping (flag); + if (p) + { + if (part->type & PED_PARTITION_EXTENDED) + return 0; + + if (state) { + dos_data->system = p->type_id; + } else if (dos_data->system == p->type_id || dos_data->system == p->alt_type_id) { + // Clear the type so that fs_type will be used to return it to the default + dos_data->system = PARTITION_LINUX; + return ped_partition_set_system (part, part->fs_type); + } + return 1; + } + + switch (flag) { + case PED_PARTITION_HIDDEN: + { + return dos_type_id_set_hidden(&dos_data->system, state); + } + + case PED_PARTITION_LBA: + { + return dos_type_id_set_lba(&dos_data->system, state); + } + + case PED_PARTITION_BOOT: + { + dos_data->boot = state; + + if (state) + { + PedDisk* disk = part->disk; + PedPartition* walk = ped_disk_next_partition (disk, NULL); + for (; walk; walk = ped_disk_next_partition (disk, walk)) { + if (walk == part || !ped_partition_is_active (walk)) + continue; + msdos_partition_set_flag (walk, PED_PARTITION_BOOT, 0); + } + } + + return 1; + } + + default: + return 0; + } +} + +static int _GL_ATTRIBUTE_PURE +msdos_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + DosPartitionData* dos_data = part->disk_specific; + + const struct flag_id_mapping_t* p = dos_find_flag_id_mapping (flag); + if (p) + return dos_data->system == p->type_id || dos_data->system == p->alt_type_id; + + switch (flag) { + case PED_PARTITION_HIDDEN: + return dos_type_id_is_hidden(dos_data->system); + + case PED_PARTITION_LBA: + return dos_type_id_is_lba(dos_data->system); + + case PED_PARTITION_BOOT: + return dos_data->boot; + + default: + return 0; + } +} + +static int +msdos_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + if (dos_find_flag_id_mapping (flag)) + return part->type != PED_PARTITION_EXTENDED; + + DosPartitionData* dos_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_HIDDEN: + return dos_type_id_supports_hidden(dos_data->system); + + case PED_PARTITION_LBA: + return dos_type_id_supports_lba(dos_data->system); + + case PED_PARTITION_BOOT: + return 1; + + default: + return 0; + } +} + + +int +msdos_partition_set_type_id (PedPartition* part, uint8_t id) +{ + DosPartitionData* dos_data = part->disk_specific; + + dos_data->system = id; + + return 1; +} + + +uint8_t _GL_ATTRIBUTE_PURE +msdos_partition_get_type_id (const PedPartition* part) +{ + const DosPartitionData* dos_data = part->disk_specific; + + return dos_data->system; +} + + +static PedGeometry* +_try_constraint (const PedPartition* part, const PedConstraint* external, + PedConstraint* internal) +{ + PedConstraint* intersection; + PedGeometry* solution; + + intersection = ped_constraint_intersect (external, internal); + ped_constraint_destroy (internal); + if (!intersection) + return NULL; + + solution = ped_constraint_solve_nearest (intersection, &part->geom); + ped_constraint_destroy (intersection); + return solution; +} + +static PedGeometry* +_best_solution (const PedPartition* part, const PedCHSGeometry* bios_geom, + PedGeometry* a, PedGeometry* b) +{ + PedSector cyl_size = bios_geom->heads * bios_geom->sectors; + int a_cylinder; + int b_cylinder; + + if (!a) + return b; + if (!b) + return a; + + a_cylinder = a->start / cyl_size; + b_cylinder = b->start / cyl_size; + + if (a_cylinder == b_cylinder) { + if ( (a->start / bios_geom->sectors) % bios_geom->heads + < (b->start / bios_geom->sectors) % bios_geom->heads) + goto choose_a; + else + goto choose_b; + } else { + PedSector a_delta; + PedSector b_delta; + + a_delta = llabs (part->geom.start - a->start); + b_delta = llabs (part->geom.start - b->start); + + if (a_delta < b_delta) + goto choose_a; + else + goto choose_b; + } + + return NULL; /* never get here! */ + +choose_a: + ped_geometry_destroy (b); + return a; + +choose_b: + ped_geometry_destroy (a); + return b; +} + +/* This constraint is for "normal" primary partitions, that start at the + * beginning of a cylinder, and end at the end of a cylinder. + * Note: you can't start a partition at the beginning of the 1st + * cylinder, because that's where the partition table is! There are different + * rules for that - see the _primary_start_constraint. + */ +static PedConstraint* +_primary_constraint (const PedDisk* disk, const PedCHSGeometry* bios_geom, + PedGeometry* min_geom) +{ + PedDevice* dev = disk->dev; + PedSector cylinder_size = bios_geom->sectors * bios_geom->heads; + PedAlignment start_align; + PedAlignment end_align; + PedGeometry start_geom; + PedGeometry end_geom; + + if (!ped_alignment_init (&start_align, 0, cylinder_size)) + return NULL; + if (!ped_alignment_init (&end_align, -1, cylinder_size)) + return NULL; + + if (min_geom) { + if (min_geom->start < cylinder_size) + return NULL; + if (!ped_geometry_init (&start_geom, dev, cylinder_size, + min_geom->start + 1 - cylinder_size)) + return NULL; + if (!ped_geometry_init (&end_geom, dev, min_geom->end, + dev->length - min_geom->end)) + return NULL; + } else { + /* Use cylinder_size as the starting sector number + when the device is large enough to accommodate that. + Otherwise, use sector 1. */ + PedSector start = (cylinder_size < dev->length + ? cylinder_size : 1); + if (!ped_geometry_init (&start_geom, dev, start, + dev->length - start)) + return NULL; + if (!ped_geometry_init (&end_geom, dev, 0, dev->length)) + return NULL; + } + + return ped_constraint_new (&start_align, &end_align, &start_geom, + &end_geom, 1, dev->length); +} + +/* This constraint is for partitions starting on the first cylinder. They + * must start on the 2nd head of the 1st cylinder. + * + * NOTE: We don't always start on the 2nd head of the 1st cylinder. Windows + * Vista aligns starting partitions at sector 2048 (0x800) by default. See: + * http://support.microsoft.com/kb/923332 + */ +static PedConstraint* +_primary_start_constraint (const PedDisk* disk, + const PedPartition *part, + const PedCHSGeometry* bios_geom, + const PedGeometry* min_geom) +{ + PedDevice* dev = disk->dev; + PedSector cylinder_size = bios_geom->sectors * bios_geom->heads; + PedAlignment start_align; + PedAlignment end_align; + PedGeometry start_geom; + PedGeometry end_geom; + PedSector start_pos; + + if (part->geom.start == 2048) + /* check for known Windows Vista (NTFS >= 3.1) alignments */ + /* sector 0x800 == 2048 */ + start_pos = 2048; + else + /* all other primary partitions on a DOS label align to */ + /* the 2nd head of the first cylinder (0x3F == 63) */ + start_pos = bios_geom->sectors; + + if (!ped_alignment_init (&start_align, start_pos, 0)) + return NULL; + if (!ped_alignment_init (&end_align, -1, cylinder_size)) + return NULL; + if (min_geom) { + if (!ped_geometry_init (&start_geom, dev, start_pos, 1)) + return NULL; + if (!ped_geometry_init (&end_geom, dev, min_geom->end, + dev->length - min_geom->end)) + return NULL; + } else { + if (!ped_geometry_init (&start_geom, dev, start_pos, + dev->length - start_pos)) + return NULL; + if (!ped_geometry_init (&end_geom, dev, 0, dev->length)) + return NULL; + } + + return ped_constraint_new (&start_align, &end_align, &start_geom, + &end_geom, 1, dev->length); +} + +/* constraints for logical partitions: + * - start_offset is the offset in the start alignment. "normally", + * this is bios_geom->sectors. exceptions: MINOR > 5 at the beginning of the + * extended partition, or MINOR == 5 in the middle of the extended partition + * - is_start_part == 1 if the constraint is for the first cylinder of + * the extended partition, or == 0 if the constraint is for the second cylinder + * onwards of the extended partition. + */ +static PedConstraint* +_logical_constraint (const PedDisk* disk, const PedCHSGeometry* bios_geom, + PedSector start_offset, int is_start_part) +{ + PedPartition* ext_part = ped_disk_extended_partition (disk); + PedDevice* dev = disk->dev; + PedSector cylinder_size = bios_geom->sectors * bios_geom->heads; + PedAlignment start_align; + PedAlignment end_align; + PedGeometry max_geom; + + PED_ASSERT (ext_part != NULL); + + if (!ped_alignment_init (&start_align, start_offset, cylinder_size)) + return NULL; + if (!ped_alignment_init (&end_align, -1, cylinder_size)) + return NULL; + if (is_start_part) { + if (!ped_geometry_init (&max_geom, dev, + ext_part->geom.start, + ext_part->geom.length)) + return NULL; + } else { + PedSector min_start; + PedSector max_length; + + min_start = ped_round_up_to (ext_part->geom.start + 1, + cylinder_size); + max_length = ext_part->geom.end - min_start + 1; + if (min_start >= ext_part->geom.end) + return NULL; + + if (!ped_geometry_init (&max_geom, dev, min_start, max_length)) + return NULL; + } + + return ped_constraint_new (&start_align, &end_align, &max_geom, + &max_geom, 1, dev->length); +} + +/* returns the minimum geometry for the extended partition, given that the + * extended partition must contain: + * * all logical partitions + * * all partition tables for all logical partitions (except the first) + * * the extended partition table + */ +static PedGeometry* +_get_min_extended_part_geom (const PedPartition* ext_part, + const PedCHSGeometry* bios_geom) +{ + PedDisk* disk = ext_part->disk; + PedSector head_size = bios_geom ? bios_geom->sectors : 1; + PedPartition* walk; + PedGeometry* min_geom; + + walk = ped_disk_get_partition (disk, 5); + if (!walk) + return NULL; + + min_geom = ped_geometry_duplicate (&walk->geom); + if (!min_geom) + return NULL; + /* We must always allow at least two sectors at the start, to leave + * room for LILO. See linux/fs/partitions/msdos.c. + */ + ped_geometry_set_start (min_geom, + walk->geom.start - PED_MAX (1 * head_size, 2)); + + for (walk = ext_part->part_list; walk; walk = walk->next) { + if (!ped_partition_is_active (walk) || walk->num == 5) + continue; + if (walk->geom.start < min_geom->start) + ped_geometry_set_start (min_geom, + walk->geom.start - 2 * head_size); + if (walk->geom.end > min_geom->end) + ped_geometry_set_end (min_geom, walk->geom.end); + } + + return min_geom; +} + +static int +_align_primary (PedPartition* part, const PedCHSGeometry* bios_geom, + const PedConstraint* constraint) +{ + PedDisk* disk = part->disk; + PedGeometry* min_geom = NULL; + PedGeometry* solution = NULL; + + if (part->type == PED_PARTITION_EXTENDED) + min_geom = _get_min_extended_part_geom (part, bios_geom); + + solution = _best_solution (part, bios_geom, solution, + _try_constraint (part, constraint, + _primary_start_constraint (disk, part, + bios_geom, min_geom))); + + solution = _best_solution (part, bios_geom, solution, + _try_constraint (part, constraint, + _primary_constraint (disk, bios_geom, + min_geom))); + + if (min_geom) + ped_geometry_destroy (min_geom); + + if (solution) { + ped_geometry_set (&part->geom, solution->start, + solution->length); + ped_geometry_destroy (solution); + return 1; + } + + return 0; +} + +static int +_logical_min_start_head (const PedPartition* part, + const PedCHSGeometry* bios_geom, + const PedPartition* ext_part, + int is_start_ext_part) +{ + PedSector cylinder_size = bios_geom->sectors * bios_geom->heads; + PedSector base_head; + + if (is_start_ext_part) + base_head = 1 + (ext_part->geom.start % cylinder_size) + / bios_geom->sectors; + else + base_head = 0; + + if (part->num == 5) + return base_head + 0; + else + return base_head + 1; +} + +/* Shamelessly copied and adapted from _partition_get_overlap_constraint + * (in disk.c) + * This should get rid of the infamous Assertion (metadata_length > 0) failed + * bug for extended msdos disklabels generated by Parted. + * 1) There always is a partition table at the start of ext_part, so we leave + * a one sector gap there. + * 2)*The partition table of part5 is always at the beginning of the ext_part + * so there is no need to leave a one sector gap before part5. + * *There always is a partition table at the beginning of each partition != 5. + * We don't need to worry to much about consistency with + * _partition_get_overlap_constraint because missing it means we are in edge + * cases anyway, and we don't lose anything by just refusing to do the job in + * those cases. + */ +static PedConstraint* +_log_meta_overlap_constraint (PedPartition* part, const PedGeometry* geom) +{ + PedGeometry safe_space; + PedSector min_start; + PedSector max_end; + PedPartition* ext_part = ped_disk_extended_partition (part->disk); + PedPartition* walk; + int not_5 = (part->num != 5); + + PED_ASSERT (ext_part != NULL); + + walk = ext_part->part_list; + + /* 1) 2) */ + min_start = ext_part->geom.start + 1 + not_5; + max_end = ext_part->geom.end; + + while (walk != NULL /* 2) 2) */ + && ( walk->geom.start - (walk->num != 5) < geom->start - not_5 + || walk->geom.start - (walk->num != 5) <= min_start )) { + if (walk != part && ped_partition_is_active (walk)) + min_start = walk->geom.end + 1 + not_5; /* 2) */ + walk = walk->next; + } + + while (walk && (walk == part || !ped_partition_is_active (walk))) + walk = walk->next; + + if (walk) + max_end = walk->geom.start - 1 - (walk->num != 5); /* 2) */ + + if (min_start >= max_end) + return NULL; + + ped_geometry_init (&safe_space, part->disk->dev, + min_start, max_end - min_start + 1); + return ped_constraint_new_from_max (&safe_space); +} + +static int +_align_logical (PedPartition* part, const PedCHSGeometry* bios_geom, + const PedConstraint* constraint) +{ + PedDisk* disk = part->disk; + PedPartition* ext_part = ped_disk_extended_partition (disk); + PedSector cyl_size = bios_geom->sectors * bios_geom->heads; + PedSector start_base; + int head; + PedGeometry* solution = NULL; + PedConstraint *intersect, *log_meta_overlap; + + PED_ASSERT (ext_part != NULL); + + log_meta_overlap = _log_meta_overlap_constraint(part, &part->geom); + intersect = ped_constraint_intersect (constraint, log_meta_overlap); + ped_constraint_destroy (log_meta_overlap); + if (!intersect) + return 0; + + start_base = ped_round_down_to (part->geom.start, cyl_size); + + for (head = _logical_min_start_head (part, bios_geom, ext_part, 0); + head < PED_MIN (5, bios_geom->heads); head++) { + PedConstraint* disk_constraint; + PedSector start = start_base + head * bios_geom->sectors; + + if (head >= _logical_min_start_head (part, bios_geom, + ext_part, 1)) + disk_constraint = + _logical_constraint (disk, bios_geom, start, 1); + else + disk_constraint = + _logical_constraint (disk, bios_geom, start, 0); + + solution = _best_solution (part, bios_geom, solution, + _try_constraint (part, intersect, + disk_constraint)); + } + + ped_constraint_destroy (intersect); + + if (solution) { + ped_geometry_set (&part->geom, solution->start, + solution->length); + ped_geometry_destroy (solution); + return 1; + } + + return 0; +} + +static int +_align (PedPartition* part, const PedCHSGeometry* bios_geom, + const PedConstraint* constraint) +{ + if (part->type == PED_PARTITION_LOGICAL) + return _align_logical (part, bios_geom, constraint); + else + return _align_primary (part, bios_geom, constraint); +} + +static PedConstraint* +_no_geom_constraint (const PedDisk* disk, PedSector start, PedSector end) +{ + PedGeometry max; + + ped_geometry_init (&max, disk->dev, start, end - start + 1); + return ped_constraint_new_from_max (&max); +} + +static PedConstraint* +_no_geom_extended_constraint (const PedPartition* part) +{ + PedDevice* dev = part->disk->dev; + PedGeometry* min = _get_min_extended_part_geom (part, NULL); + PedGeometry start_range; + PedGeometry end_range; + PedConstraint* constraint; + + if (min) { + ped_geometry_init (&start_range, dev, 1, min->start); + ped_geometry_init (&end_range, dev, min->end, + dev->length - min->end); + ped_geometry_destroy (min); + } else { + ped_geometry_init (&start_range, dev, 1, dev->length - 1); + ped_geometry_init (&end_range, dev, 1, dev->length - 1); + } + constraint = ped_constraint_new (ped_alignment_any, ped_alignment_any, + &start_range, &end_range, 1, dev->length); + return constraint; +} + +static int +_align_primary_no_geom (PedPartition* part, const PedConstraint* constraint) +{ + PedDisk* disk = part->disk; + PedGeometry* solution; + + if (part->type == PED_PARTITION_EXTENDED) { + solution = _try_constraint (part, constraint, + _no_geom_extended_constraint (part)); + } else { + solution = _try_constraint (part, constraint, + _no_geom_constraint (disk, 1, + disk->dev->length - 1)); + } + + if (solution) { + ped_geometry_set (&part->geom, solution->start, + solution->length); + ped_geometry_destroy (solution); + return 1; + } + return 0; +} + +static int +_align_logical_no_geom (PedPartition* part, const PedConstraint* constraint) +{ + PedGeometry* solution; + + solution = _try_constraint (part, constraint, + _log_meta_overlap_constraint (part, &part->geom)); + + if (solution) { + ped_geometry_set (&part->geom, solution->start, + solution->length); + ped_geometry_destroy (solution); + return 1; + } + return 0; +} + +static int +_align_no_geom (PedPartition* part, const PedConstraint* constraint) +{ + if (part->type == PED_PARTITION_LOGICAL) + return _align_logical_no_geom (part, constraint); + else + return _align_primary_no_geom (part, constraint); +} + +static int +msdos_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PedCHSGeometry bios_geom; + DosPartitionData* dos_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + dos_data = part->disk_specific; + + if (dos_data->system == PARTITION_LDM && dos_data->orig) { + PedGeometry *orig_geom = &dos_data->orig->geom; + + if (ped_geometry_test_equal (&part->geom, orig_geom) + && ped_constraint_is_solution (constraint, &part->geom)) + return 1; + + ped_geometry_set (&part->geom, orig_geom->start, + orig_geom->length); + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Parted can't resize partitions managed by " + "Windows Dynamic Disk.")); + return 0; + } + + partition_probe_bios_geometry (part, &bios_geom); + + DosDiskData *disk_specific = part->disk->disk_specific; + if (disk_specific->cylinder_alignment + && _align(part, &bios_geom, constraint)) + return 1; + if (_align_no_geom (part, constraint)) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +add_metadata_part (PedDisk* disk, PedPartitionType type, PedSector start, + PedSector end) +{ + PedPartition* new_part; + + PED_ASSERT (disk != NULL); + + new_part = ped_partition_new (disk, type | PED_PARTITION_METADATA, NULL, + start, end); + if (!new_part) + goto error; + if (!ped_disk_add_partition (disk, new_part, NULL)) + goto error_destroy_new_part; + + return 1; + +error_destroy_new_part: + ped_partition_destroy (new_part); +error: + return 0; +} + +/* There are a few objectives here: + * - avoid having lots of "free space" partitions lying around, to confuse + * the front end. + * - ensure that there's enough room to put in the extended partition + * tables, etc. + */ +static int +add_logical_part_metadata (PedDisk* disk, const PedPartition* log_part) +{ + PedPartition* ext_part = ped_disk_extended_partition (disk); + PedPartition* prev = log_part->prev; + PedCHSGeometry bios_geom; + PedSector cyl_size; + PedSector metadata_start; + PedSector metadata_end; + PedSector metadata_length; + + partition_probe_bios_geometry (ext_part, &bios_geom); + cyl_size = bios_geom.sectors * bios_geom.heads; + + /* if there's metadata shortly before the partition (on the same + * cylinder), then make this new metadata partition touch the end of + * the other. No point having 63 bytes (or whatever) of free space + * partition - just confuses front-ends, etc. + * Otherwise, start the metadata at the start of the cylinder + */ + + metadata_end = log_part->geom.start - 1; + metadata_start = ped_round_down_to (metadata_end, cyl_size); + if (prev) + metadata_start = PED_MAX (metadata_start, prev->geom.end + 1); + else + metadata_start = PED_MAX (metadata_start, + ext_part->geom.start + 1); + metadata_length = metadata_end - metadata_start + 1; + + /* partition 5 doesn't need to have any metadata */ + if (log_part->num == 5 && metadata_length < bios_geom.sectors) + return 1; + + PED_ASSERT (metadata_length > 0); + + return add_metadata_part (disk, PED_PARTITION_LOGICAL, + metadata_start, metadata_end); +} + +/* + * Find the starting sector number of the first non-free partition, + * set *SECTOR to that value, and return 1. + * If there is no non-free partition, don't modify *SECTOR and return 0. + */ +static int +get_start_first_nonfree_part (const PedDisk* disk, PedSector *sector) +{ + PedPartition* walk; + + // disk->part_list is the first partition on the disk. + if (!disk->part_list) + return 0; + + for (walk = disk->part_list; walk; walk = walk->next) { + if (walk->type == PED_PARTITION_NORMAL || + walk->type == PED_PARTITION_EXTENDED) { + *sector = walk->geom.start; + return 1; + } + } + return 0; +} + +/* + * Find the ending sector number of the last non-free partition, + * set *SECTOR to that value, and return 1. + * If there is no non-free partition, don't modify *SECTOR and return 0. + */ +static int +get_end_last_nonfree_part (const PedDisk* disk, PedSector *sector) +{ + PedPartition* last_part = NULL; + PedPartition* walk; + + // disk->part_list is the first partition on the disk. + if (!disk->part_list) + return 0; + + for (walk = disk->part_list; walk; walk = walk->next) { + if (walk->type == PED_PARTITION_NORMAL || + walk->type == PED_PARTITION_EXTENDED) { + last_part = walk; + } + } + + if (!last_part) + return 0; + else { + *sector = last_part->geom.end; + return 1; + } +} + +/* Adds metadata placeholder partitions to cover the partition table (and + * "free" space after it that often has bootloader stuff), and the last + * incomplete cylinder at the end of the disk. + * Parted has to be mindful of the uncertainty of dev->bios_geom. + * It therefore makes sure this metadata doesn't overlap with partitions. + */ +static int +add_startend_metadata (PedDisk* disk) +{ + PedDevice* dev = disk->dev; + PedSector cyl_size = dev->bios_geom.sectors * dev->bios_geom.heads; + PedSector init_start, init_end, final_start, final_end; + + // Ranges for the initial and final metadata partition. + init_start = 0; + if (!get_start_first_nonfree_part(disk, &init_end)) + init_end = dev->bios_geom.sectors - 1; + else + init_end = PED_MIN (dev->bios_geom.sectors - 1, init_end - 1); + + DosDiskData *disk_specific = disk->disk_specific; + if (!disk_specific->cylinder_alignment) + final_start = dev->length - 1; + else if (!get_end_last_nonfree_part(disk, &final_start)) + final_start = ped_round_down_to (dev->length, cyl_size); + else + final_start = PED_MAX (final_start + 1, + ped_round_down_to (dev->length, cyl_size)); + final_end = dev->length - 1; + + // Create the metadata partitions. + // init_end <= dev->length for devices that are _real_ small. + if (init_start < init_end && + init_end <= dev->length && + !add_metadata_part (disk, PED_PARTITION_NORMAL, + init_start, init_end)) + return 0; + + // init_end < final_start so they dont overlap. For very small devs. + if (final_start < final_end && + init_end < final_start && + final_end <= dev->length && + !add_metadata_part (disk, PED_PARTITION_NORMAL, + final_start, final_end)) + return 0; + + return 1; +} + +static int +msdos_alloc_metadata (PedDisk* disk) +{ + PedPartition* ext_part; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + + if (!add_startend_metadata (disk)) + return 0; + + ext_part = ped_disk_extended_partition (disk); + if (ext_part) { + int i; + PedSector start, end; + PedCHSGeometry bios_geom; + + for (i=5; 1; i++) { + PedPartition* log_part; + log_part = ped_disk_get_partition (disk, i); + if (!log_part) + break; + if (!add_logical_part_metadata (disk, log_part)) + return 0; + } + + partition_probe_bios_geometry (ext_part, &bios_geom); + start = ext_part->geom.start; + end = start + bios_geom.sectors - 1; + if (ext_part->part_list) + end = PED_MIN (end, + ext_part->part_list->geom.start - 1); + if (!add_metadata_part (disk, PED_PARTITION_LOGICAL, + start, end)) + return 0; + } + + return 1; +} + +static int +next_primary (const PedDisk* disk) +{ + int i; + for (i=1; i<=DOS_N_PRI_PARTITIONS; i++) { + if (!ped_disk_get_partition (disk, i)) + return i; + } + return -1; +} + +static int _GL_ATTRIBUTE_PURE +next_logical (const PedDisk* disk) +{ + int i; + for (i=5; i<=MAX_TOTAL_PART; i++) { + if (!ped_disk_get_partition (disk, i)) + return i; + } + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("cannot create any more partitions"), + disk->dev->path); + return -1; +} + +static int +msdos_partition_enumerate (PedPartition* part) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + + /* don't re-number a primary partition */ + if (part->num != -1 && part->num <= DOS_N_PRI_PARTITIONS) + return 1; + + part->num = -1; + + if (part->type & PED_PARTITION_LOGICAL) + part->num = next_logical (part->disk); + else + part->num = next_primary (part->disk); + if (part->num == -1) + return 0; + return 1; +} + +static int +msdos_get_max_primary_partition_count (const PedDisk* disk) +{ + return DOS_N_PRI_PARTITIONS; +} + +static bool +msdos_get_max_supported_partition_count(const PedDisk* disk, int *max_n) +{ + *max_n = MAX_TOTAL_PART; + return true; +} + +#include "pt-common.h" +PT_define_limit_functions (msdos) + +static PedDiskOps msdos_disk_ops = { + clobber: NULL, + write: NULL_IF_DISCOVER_ONLY (msdos_write), + + disk_set_flag: msdos_disk_set_flag, + disk_get_flag: msdos_disk_get_flag, + disk_is_flag_available: msdos_disk_is_flag_available, + + partition_set_name: NULL, + partition_get_name: NULL, + partition_set_type_id: msdos_partition_set_type_id, + partition_get_type_id: msdos_partition_get_type_id, + partition_set_type_uuid: NULL, + partition_get_type_uuid: NULL, + + PT_op_function_initializers (msdos) +}; + +static PedDiskType msdos_disk_type = { + next: NULL, + name: "msdos", + ops: &msdos_disk_ops, + features: PED_DISK_TYPE_EXTENDED | PED_DISK_TYPE_PARTITION_TYPE_ID +}; + +void +ped_disk_msdos_init () +{ + PED_ASSERT (sizeof (DosRawPartition) == 16); + PED_ASSERT (sizeof (DosRawTable) == 512); + + ped_disk_type_register (&msdos_disk_type); +} + +void +ped_disk_msdos_done () +{ + ped_disk_type_unregister (&msdos_disk_type); +} diff --git a/libparted/labels/dvh.c b/libparted/labels/dvh.c new file mode 100644 index 0000000..0f9124d --- /dev/null +++ b/libparted/labels/dvh.c @@ -0,0 +1,896 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2001-2002, 2005, 2007-2014, 2019-2023 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/>. +*/ + +#include <config.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <stdbool.h> + +#include "dvh.h" +#include "pt-tools.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/* Default size for volhdr part, same val as IRIX's fx uses */ +#define PTYPE_VOLHDR_DFLTSZ 4096 + +/* Partition numbers that seem to be strongly held convention */ +#define PNUM_VOLHDR 8 +#define PNUM_VOLUME 10 + +/* Other notes of interest: + * PED_PARTITION_EXTENDED is used for volume headers + * PED_PARTITION_LOGICAL is used for bootfiles + * PED_PARTITION_NORMAL is used for all else + */ + +typedef struct _DVHDiskData { + struct device_parameters dev_params; + int swap; /* part num of swap, 0=none */ + int root; /* part num of root, 0=none */ + int boot; /* part num of boot, 0=none */ +} DVHDiskData; + +typedef struct _DVHPartData { + int type; + char name[VDNAMESIZE + 1]; /* boot volumes only */ + int real_file_size; /* boot volumes only */ +} DVHPartData; + +static PedDiskType dvh_disk_type; + +static int +dvh_probe (const PedDevice *dev) +{ + struct volume_header *vh; + + void *label; + if (!ptt_read_sector (dev, 0, &label)) + return 0; + + vh = (struct volume_header *) label; + + bool found = PED_BE32_TO_CPU (vh->vh_magic) == VHMAGIC; + free (label); + return found; +} + +static PedDisk* +dvh_alloc (const PedDevice* dev) +{ + PedDisk* disk; + DVHDiskData* dvh_disk_data; + PedPartition* volume_part; + PedConstraint* constraint_any; + + disk = _ped_disk_alloc (dev, &dvh_disk_type); + if (!disk) + goto error; + + disk->disk_specific = dvh_disk_data + = ped_malloc (sizeof (DVHDiskData)); + if (!dvh_disk_data) + goto error_free_disk; + + memset (&dvh_disk_data->dev_params, 0, + sizeof (struct device_parameters)); + dvh_disk_data->swap = 0; + dvh_disk_data->root = 0; + dvh_disk_data->boot = 0; + + volume_part = ped_partition_new (disk, PED_PARTITION_EXTENDED, NULL, + 0, PTYPE_VOLHDR_DFLTSZ - 1); + if (!volume_part) + goto error_free_disk_specific; + volume_part->num = PNUM_VOLHDR + 1; + constraint_any = ped_constraint_any (dev); + if (!ped_disk_add_partition (disk, volume_part, constraint_any)) + goto error_destroy_constraint_any; + ped_constraint_destroy (constraint_any); + return disk; + +error_destroy_constraint_any: + ped_constraint_destroy (constraint_any); + ped_partition_destroy (volume_part); +error_free_disk_specific: + free (disk->disk_specific); +error_free_disk: + free (disk); +error: + return NULL; +} + +static PedDisk* +dvh_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + DVHDiskData* new_dvh_disk_data; + DVHDiskData* old_dvh_disk_data = disk->disk_specific; + + PED_ASSERT (old_dvh_disk_data != NULL); + + new_disk = ped_disk_new_fresh (disk->dev, &dvh_disk_type); + if (!new_disk) + goto error; + + new_disk->disk_specific = new_dvh_disk_data + = ped_malloc (sizeof (DVHDiskData)); + if (!new_dvh_disk_data) + goto error_free_new_disk; + + new_dvh_disk_data->dev_params = old_dvh_disk_data->dev_params; + return new_disk; + +error_free_new_disk: + free (new_disk); +error: + return NULL; +} + +static void +dvh_free (PedDisk* disk) +{ + free (disk->disk_specific); + _ped_disk_free (disk); +} + +/* two's complement 32-bit checksum */ +static uint32_t _GL_ATTRIBUTE_PURE +_checksum (const uint32_t* base, size_t size) +{ + uint32_t sum = 0; + size_t i; + + for (i = 0; i < size / sizeof (uint32_t); i++) + sum = sum - PED_BE32_TO_CPU (base[i]); + + return sum; +} + +/* try to make a reasonable volume header partition... */ +static PedExceptionOption +_handle_no_volume_header (PedDisk* disk) +{ + PedExceptionOption ret; + PedPartition* part; + PedConstraint* constraint; + + switch (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_FIX + PED_EXCEPTION_CANCEL, + _("%s has no extended partition (volume header partition)."), + disk->dev->path)) { + case PED_EXCEPTION_UNHANDLED: + case PED_EXCEPTION_FIX: + default: + part = ped_partition_new ( + disk, PED_PARTITION_EXTENDED, NULL, + 0, PTYPE_VOLHDR_DFLTSZ - 1); + if (!part) + goto error; + part->num = PNUM_VOLHDR + 1; + constraint = ped_constraint_any (part->disk->dev); + if (!constraint) + goto error_destroy_part; + if (!ped_disk_add_partition (disk, part, constraint)) + goto error_destroy_constraint; + ped_constraint_destroy (constraint); + ret = PED_EXCEPTION_FIX; + break; + + case PED_EXCEPTION_CANCEL: + goto error; + } + return ret; + +error_destroy_constraint: + ped_constraint_destroy (constraint); +error_destroy_part: + ped_partition_destroy (part); +error: + return PED_EXCEPTION_CANCEL; +} + +static PedPartition* +_parse_partition (PedDisk* disk, struct partition_table* pt) +{ + PedPartition* part; + DVHPartData* dvh_part_data; + PedSector start = PED_BE32_TO_CPU (pt->pt_firstlbn); + PedSector length = PED_BE32_TO_CPU (pt->pt_nblks); + + part = ped_partition_new (disk, + pt->pt_type ? 0 : PED_PARTITION_EXTENDED, + NULL, + start, start + length - 1); + if (!part) + return NULL; + + dvh_part_data = part->disk_specific; + dvh_part_data->type = PED_BE32_TO_CPU (pt->pt_type); + strcpy (dvh_part_data->name, ""); + + return part; +} + +static PedPartition* +_parse_boot_file (PedDisk* disk, struct volume_directory* vd) +{ + PedPartition* part; + DVHPartData* dvh_part_data; + PedSector start = PED_BE32_TO_CPU (vd->vd_lbn); + int length = PED_BE32_TO_CPU (vd->vd_nbytes); + + part = ped_partition_new (disk, PED_PARTITION_LOGICAL, NULL, + start, start + length/512 - 1); + if (!part) + return NULL; + + dvh_part_data = part->disk_specific; + dvh_part_data->real_file_size = length; + + memcpy (dvh_part_data->name, vd->vd_name, VDNAMESIZE); + dvh_part_data->name[VDNAMESIZE] = 0; + return part; +} + +static int dvh_write (const PedDisk* disk); + +/* YUCK + * + * If you remove a boot/root/swap partition, the disk->disk_specific + * thing isn't updated. (Probably reflects a design bug somewhere...) + * Anyway, the workaround is: flush stale flags whenever we allocate + * new partition numbers, and before we write to disk. + */ +static void +_flush_stale_flags (const PedDisk* disk) +{ + DVHDiskData* dvh_disk_data = disk->disk_specific; + + if (dvh_disk_data->root + && !ped_disk_get_partition (disk, dvh_disk_data->root)) + dvh_disk_data->root = 0; + if (dvh_disk_data->swap + && !ped_disk_get_partition (disk, dvh_disk_data->swap)) + dvh_disk_data->swap = 0; + if (dvh_disk_data->boot + && !ped_disk_get_partition (disk, dvh_disk_data->boot)) + dvh_disk_data->boot = 0; +} + +static int +dvh_read (PedDisk* disk) +{ + DVHDiskData* dvh_disk_data = disk->disk_specific; + int i; + struct volume_header vh; + char boot_name [BFNAMESIZE + 1]; +#ifndef DISCOVER_ONLY + int write_back = 0; +#endif + + PED_ASSERT (dvh_disk_data != NULL); + + ped_disk_delete_all (disk); + + void *s0; + if (!ptt_read_sector (disk->dev, 0, &s0)) + return 0; + memcpy (&vh, s0, sizeof vh); + free (s0); + + if (_checksum ((uint32_t*) &vh, sizeof (struct volume_header))) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Checksum is wrong, indicating the partition " + "table is corrupt.")) + == PED_EXCEPTION_CANCEL) + return 0; + } + + PED_ASSERT (PED_BE32_TO_CPU (vh.vh_magic) == VHMAGIC); + + dvh_disk_data->dev_params = vh.vh_dp; + boot_name[BFNAMESIZE] = 0; + strncpy (boot_name, vh.vh_bootfile, BFNAMESIZE); + + /* normal partitions */ + for (i = 0; i < NPARTAB; i++) { + PedPartition* part; + + if (!vh.vh_pt[i].pt_nblks) + continue; + /* Skip the whole-disk partition, parted disklikes overlap */ + if (PED_BE32_TO_CPU (vh.vh_pt[i].pt_type) == PTYPE_VOLUME) + continue; + + part = _parse_partition (disk, &vh.vh_pt[i]); + if (!part) + goto error_delete_all; + + part->fs_type = ped_file_system_probe (&part->geom); + part->num = i + 1; + + if (PED_BE16_TO_CPU (vh.vh_rootpt) == i) + ped_partition_set_flag (part, PED_PARTITION_ROOT, 1); + if (PED_BE16_TO_CPU (vh.vh_swappt) == i) + ped_partition_set_flag (part, PED_PARTITION_SWAP, 1); + + PedConstraint *constraint_exact + = ped_constraint_exact (&part->geom); + bool ok = ped_disk_add_partition (disk, part, constraint_exact); + ped_constraint_destroy (constraint_exact); + if (!ok) { + ped_partition_destroy (part); + goto error_delete_all; + } + } + + if (!ped_disk_extended_partition (disk)) { +#ifdef DISCOVER_ONLY + return 1; +#else + switch (_handle_no_volume_header (disk)) { + case PED_EXCEPTION_CANCEL: + return 0; + case PED_EXCEPTION_IGNORE: + return 1; + case PED_EXCEPTION_FIX: + write_back = 1; + break; + default: + break; + } +#endif + } + + /* boot partitions */ + for (i = 0; i < NVDIR; i++) { + PedPartition* part; + + if (!vh.vh_vd[i].vd_nbytes) + continue; + + part = _parse_boot_file (disk, &vh.vh_vd[i]); + if (!part) + goto error_delete_all; + + part->fs_type = ped_file_system_probe (&part->geom); + part->num = NPARTAB + i + 1; + + if (!strcmp (boot_name, ped_partition_get_name (part))) + ped_partition_set_flag (part, PED_PARTITION_BOOT, 1); + + PedConstraint *constraint_exact + = ped_constraint_exact (&part->geom); + bool ok = ped_disk_add_partition (disk, part, constraint_exact); + ped_constraint_destroy (constraint_exact); + if (!ok) { + ped_partition_destroy (part); + goto error_delete_all; + } + } +#ifndef DISCOVER_ONLY + if (write_back) + dvh_write (disk); +#endif + return 1; + +error_delete_all: + ped_disk_delete_all (disk); + return 0; +} + +#ifndef DISCOVER_ONLY +static void +_generate_partition (PedPartition* part, struct partition_table* pt) +{ + DVHPartData* dvh_part_data = part->disk_specific; + + /* Assert not a bootfile */ + PED_ASSERT ((part->type & PED_PARTITION_LOGICAL) == 0); + + pt->pt_nblks = PED_CPU_TO_BE32 (part->geom.length); + pt->pt_firstlbn = PED_CPU_TO_BE32 (part->geom.start); + pt->pt_type = PED_CPU_TO_BE32 (dvh_part_data->type); +} + +static void +_generate_boot_file (PedPartition* part, struct volume_directory* vd) +{ + DVHPartData* dvh_part_data = part->disk_specific; + + /* Assert it's a bootfile */ + PED_ASSERT ((part->type & PED_PARTITION_LOGICAL) != 0); + + vd->vd_nbytes = PED_CPU_TO_BE32 (dvh_part_data->real_file_size); + vd->vd_lbn = PED_CPU_TO_BE32 (part->geom.start); + + memset (vd->vd_name, 0, VDNAMESIZE); + memcpy (vd->vd_name, dvh_part_data->name, VDNAMESIZE); +} + +static int +dvh_write (const PedDisk* disk) +{ + DVHDiskData* dvh_disk_data = disk->disk_specific; + struct volume_header vh; + int i; + + PED_ASSERT (dvh_disk_data != NULL); + + _flush_stale_flags (disk); + + memset (&vh, 0, sizeof (struct volume_header)); + + vh.vh_magic = PED_CPU_TO_BE32 (VHMAGIC); + vh.vh_rootpt = PED_CPU_TO_BE16 (dvh_disk_data->root - 1); + vh.vh_swappt = PED_CPU_TO_BE16 (dvh_disk_data->swap - 1); + + if (dvh_disk_data->boot) { + PedPartition* boot_part; + boot_part = ped_disk_get_partition (disk, dvh_disk_data->boot); + strcpy (vh.vh_bootfile, ped_partition_get_name (boot_part)); + } + + vh.vh_dp = dvh_disk_data->dev_params; + /* Set up rudimentary device geometry */ + vh.vh_dp.dp_cyls + = PED_CPU_TO_BE16 ((short)disk->dev->bios_geom.cylinders); + vh.vh_dp.dp_trks0 = PED_CPU_TO_BE16 ((short)disk->dev->bios_geom.heads); + vh.vh_dp.dp_secs + = PED_CPU_TO_BE16 ((short)disk->dev->bios_geom.sectors); + vh.vh_dp.dp_secbytes = PED_CPU_TO_BE16 ((short)disk->dev->sector_size); + + for (i = 0; i < NPARTAB; i++) { + PedPartition* part = ped_disk_get_partition (disk, i + 1); + if (part) + _generate_partition (part, &vh.vh_pt[i]); + } + + /* whole disk partition + * This is only ever written here, and never modified + * (or even shown) as it must contain the entire disk, + * and parted does not like overlapping partitions + */ + vh.vh_pt[PNUM_VOLUME].pt_nblks = PED_CPU_TO_BE32 (disk->dev->length); + vh.vh_pt[PNUM_VOLUME].pt_firstlbn = PED_CPU_TO_BE32 (0); + vh.vh_pt[PNUM_VOLUME].pt_type = PED_CPU_TO_BE32 (PTYPE_VOLUME); + + for (i = 0; i < NVDIR; i++) { + PedPartition* part = ped_disk_get_partition (disk, + i + 1 + NPARTAB); + if (part) + _generate_boot_file (part, &vh.vh_vd[i]); + } + + vh.vh_csum = 0; + vh.vh_csum = PED_CPU_TO_BE32 (_checksum ((uint32_t*) &vh, + sizeof (struct volume_header))); + + return (ptt_write_sector (disk, &vh, sizeof vh) + && ped_device_sync (disk->dev)); +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +dvh_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + DVHPartData* dvh_part_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (!ped_partition_is_active (part)) { + part->disk_specific = NULL; + return part; + } + + dvh_part_data = part->disk_specific = + ped_malloc (sizeof (DVHPartData)); + if (!dvh_part_data) + goto error_free_part; + + dvh_part_data->type = (part_type == PED_PARTITION_EXTENDED) + ? PTYPE_VOLHDR + : PTYPE_RAW; + strcpy (dvh_part_data->name, ""); + dvh_part_data->real_file_size = part->geom.length * 512; + return part; + +error_free_part: + _ped_partition_free (part); +error: + return NULL; +} + +static PedPartition* +dvh_partition_duplicate (const PedPartition* part) +{ + PedPartition* result; + DVHPartData* part_data = part->disk_specific; + DVHPartData* result_data; + + result = _ped_partition_alloc (part->disk, part->type, part->fs_type, + part->geom.start, part->geom.end); + if (!result) + goto error; + result->num = part->num; + + if (!ped_partition_is_active (part)) { + result->disk_specific = NULL; + return result; + } + + result_data = result->disk_specific = + ped_malloc (sizeof (DVHPartData)); + if (!result_data) + goto error_free_part; + + result_data->type = part_data->type; + strcpy (result_data->name, part_data->name); + result_data->real_file_size = part_data->real_file_size; + return result; + +error_free_part: + _ped_partition_free (result); +error: + return NULL; +} + +static void +dvh_partition_destroy (PedPartition* part) +{ + if (ped_partition_is_active (part)) { + PED_ASSERT (part->disk_specific != NULL); + free (part->disk_specific); + } + _ped_partition_free (part); +} + +static int +dvh_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + DVHPartData* dvh_part_data = part->disk_specific; + + part->fs_type = fs_type; + + if (part->type == PED_PARTITION_EXTENDED) { + dvh_part_data->type = PTYPE_VOLHDR; + return 1; + } + + /* Is this a bootfile? */ + if (part->type == PED_PARTITION_LOGICAL) + return 1; + + if (fs_type && !strcmp (fs_type->name, "xfs")) + dvh_part_data->type = PTYPE_XFS; + else + dvh_part_data->type = PTYPE_RAW; + return 1; +} + +static int +dvh_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + DVHDiskData* dvh_disk_data = part->disk->disk_specific; + + switch (flag) { + case PED_PARTITION_ROOT: + if (part->type != 0 && state) { +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Only primary partitions can be root " + "partitions.")); +#endif + return 0; + } + dvh_disk_data->root = state ? part->num : 0; + break; + + case PED_PARTITION_SWAP: + if (part->type != 0 && state) { +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Only primary partitions can be swap " + "partitions.")); + return 0; +#endif + } + dvh_disk_data->swap = state ? part->num : 0; + break; + + case PED_PARTITION_BOOT: + if (part->type != PED_PARTITION_LOGICAL && state) { +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Only logical partitions can be a boot " + "file.")); +#endif + return 0; + } + dvh_disk_data->boot = state ? part->num : 0; + break; + + case PED_PARTITION_LVM: + case PED_PARTITION_LBA: + case PED_PARTITION_HIDDEN: + case PED_PARTITION_RAID: + default: + return 0; + } + return 1; +} + +static int _GL_ATTRIBUTE_PURE +dvh_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + DVHDiskData* dvh_disk_data = part->disk->disk_specific; + + switch (flag) { + case PED_PARTITION_ROOT: + return dvh_disk_data->root == part->num; + + case PED_PARTITION_SWAP: + return dvh_disk_data->swap == part->num; + + case PED_PARTITION_BOOT: + return dvh_disk_data->boot == part->num; + + case PED_PARTITION_LVM: + case PED_PARTITION_LBA: + case PED_PARTITION_HIDDEN: + case PED_PARTITION_RAID: + default: + return 0; + } + return 1; +} + +static int +dvh_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_ROOT: + case PED_PARTITION_SWAP: + case PED_PARTITION_BOOT: + return 1; + + case PED_PARTITION_LVM: + case PED_PARTITION_LBA: + case PED_PARTITION_HIDDEN: + case PED_PARTITION_RAID: + default: + return 0; + } + return 1; +} + +static void +dvh_partition_set_name (PedPartition* part, const char* name) +{ + DVHPartData* dvh_part_data = part->disk_specific; + + if (part->type == PED_PARTITION_LOGICAL) { + /* Bootfile */ + memcpy (dvh_part_data->name, name, VDNAMESIZE); + dvh_part_data->name[VDNAMESIZE] = 0; + } else { +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("failed to set dvh partition name to %s:\n" + "Only logical partitions (boot files) have a name."), + name); +#endif + } +} + +static const char* +dvh_partition_get_name (const PedPartition* part) +{ + DVHPartData* dvh_part_data = part->disk_specific; + return dvh_part_data->name; +} + +/* The constraint for the volume header partition is different, because it must + * contain the first sector of the disk. + */ +static PedConstraint* +_get_extended_constraint (PedDisk* disk) +{ + PedGeometry min_geom; + if (!ped_geometry_init (&min_geom, disk->dev, 0, 1)) + return NULL; + return ped_constraint_new_from_min (&min_geom); +} + +static PedConstraint* +_get_primary_constraint (PedDisk* disk) +{ + PedGeometry max_geom; + if (!ped_geometry_init (&max_geom, disk->dev, 1, disk->dev->length - 1)) + return NULL; + return ped_constraint_new_from_max (&max_geom); +} + +static int +dvh_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL); + + if (_ped_partition_attempt_align ( + part, constraint, + (part->type == PED_PARTITION_EXTENDED) + ? _get_extended_constraint (part->disk) + : _get_primary_constraint (part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +dvh_partition_enumerate (PedPartition* part) +{ + int i; + + /* never change the partition numbers */ + if (part->num != -1) + return 1; + + _flush_stale_flags (part->disk); + + if (part->type & PED_PARTITION_LOGICAL) { + /* Bootfile */ + for (i = 1 + NPARTAB; i <= NPARTAB + NVDIR; i++) { + if (!ped_disk_get_partition (part->disk, i)) { + part->num = i; + return 1; + } + } + PED_ASSERT (0); + } else if (part->type & PED_PARTITION_EXTENDED) { + /* Volheader */ + part->num = PNUM_VOLHDR + 1; + } else { + for (i = 1; i <= NPARTAB; i++) { + /* reserved for full volume partition */ + if (i == PNUM_VOLUME + 1) + continue; + + if (!ped_disk_get_partition (part->disk, i)) { + part->num = i; + return 1; + } + } + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Too many primary partitions")); + } + + return 0; +} + +static int +dvh_get_max_primary_partition_count (const PedDisk* disk) +{ + return NPARTAB; +} + +static bool +dvh_get_max_supported_partition_count (const PedDisk* disk, int *max_n) +{ + *max_n = NPARTAB; + return true; +} + + +static int +dvh_alloc_metadata (PedDisk* disk) +{ + PedPartition* part; + PedPartition* extended_part; + PedPartitionType metadata_type; + PED_ASSERT(disk != NULL); + + /* We don't need to "protect" the start of the disk from the volume + * header. + */ + extended_part = ped_disk_extended_partition (disk); + if (extended_part && extended_part->geom.start == 0) + metadata_type = PED_PARTITION_METADATA | PED_PARTITION_LOGICAL; + else + metadata_type = PED_PARTITION_METADATA; + + part = ped_partition_new (disk, metadata_type, NULL, 0, 0); + if (!part) + goto error; + + PedConstraint *constraint_exact + = ped_constraint_exact (&part->geom); + bool ok = ped_disk_add_partition (disk, part, constraint_exact); + ped_constraint_destroy (constraint_exact); + if (ok) + return 1; + + ped_partition_destroy (part); +error: + return 0; +} + +#include "pt-common.h" +PT_define_limit_functions (dvh) + +static PedDiskOps dvh_disk_ops = { + clobber: NULL, + write: NULL_IF_DISCOVER_ONLY (dvh_write), + + partition_set_name: dvh_partition_set_name, + partition_get_name: dvh_partition_get_name, + PT_op_function_initializers (dvh) +}; + +static PedDiskType dvh_disk_type = { + next: NULL, + name: "dvh", + ops: &dvh_disk_ops, + features: PED_DISK_TYPE_PARTITION_NAME | PED_DISK_TYPE_EXTENDED +}; + +void +ped_disk_dvh_init () +{ + PED_ASSERT (sizeof (struct volume_header) == 512); + + ped_disk_type_register (&dvh_disk_type); +} + +void +ped_disk_dvh_done () +{ + ped_disk_type_unregister (&dvh_disk_type); +} diff --git a/libparted/labels/dvh.h b/libparted/labels/dvh.h new file mode 100644 index 0000000..7e7fae7 --- /dev/null +++ b/libparted/labels/dvh.h @@ -0,0 +1,178 @@ +/* + Copyright (C) 1985 MIPS Computer Systems, Inc. + Copyright (C) 2000 Silicon Graphics Computer Systems, 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/>. +*/ + +#ifndef _SYS_DVH_H +#define _SYS_DVH_H + +/* + * Format for volume header information + * + * The volume header is a block located at the beginning of all disk + * media (sector 0). It contains information pertaining to physical + * device parameters and logical partition information. + * + * The volume header is manipulated by disk formatters/verifiers, + * partition builders (e.g. fx, dvhtool, and mkfs), and disk drivers. + * + * Previous versions of IRIX wrote a copy of the volume header is + * located at sector 0 of each track of cylinder 0. These copies were + * never used, and reduced the capacity of the volume header to hold large + * files, so this practice was discontinued. + * The volume header is constrained to be less than or equal to 512 + * bytes long. A particular copy is assumed valid if no drive errors + * are detected, the magic number is correct, and the 32 bit 2's complement + * of the volume header is correct. The checksum is calculated by initially + * zeroing vh_csum, summing the entire structure and then storing the + * 2's complement of the sum. Thus a checksum to verify the volume header + * should be 0. + * + * The error summary table, bad sector replacement table, and boot blocks are + * located by searching the volume directory within the volume header. + * + * Tables are sized simply by the integral number of table records that + * will fit in the space indicated by the directory entry. + * + * The amount of space allocated to the volume header, replacement blocks, + * and other tables is user defined when the device is formatted. + */ + +/* + * device parameters are in the volume header to determine mapping + * from logical block numbers to physical device addresses + * + * Linux doesn't care ... + */ +struct device_parameters { + unsigned char dp_skew; /* spiral addressing skew */ + unsigned char dp_gap1; /* words of 0 before header */ + unsigned char dp_gap2; /* words of 0 between hdr and data */ + unsigned char dp_spares_cyl; /* This is for drives (such as SCSI + that support zone oriented sparing, where the zone is larger + than one track. It gets subracteded from the cylinder size + ( dp_trks0 * dp_sec) when doing partition size calculations */ + unsigned short dp_cyls; /* number of usable cylinders (i.e., + doesn't include cylinders reserved by the drive for badblocks, + etc.). For drives with variable geometry, this number may be + decreased so that: + dp_cyls * ((dp_heads * dp_trks0) - dp_spares_cyl) <= actualcapacity + This happens on SCSI drives such as the Wren IV and Toshiba 156 + Also see dp_cylshi below */ + unsigned short dp_shd0; /* starting head vol 0 */ + unsigned short dp_trks0; /* number of tracks / cylinder vol 0*/ + unsigned char dp_ctq_depth; /* Depth of CTQ queue */ + unsigned char dp_cylshi; /* high byte of 24 bits of cylinder count */ + unsigned short dp_unused; /* not used */ + unsigned short dp_secs; /* number of sectors/track */ + unsigned short dp_secbytes; /* length of sector in bytes */ + unsigned short dp_interleave; /* sector interleave */ + int dp_flags; /* controller characteristics */ + int dp_datarate; /* bytes/sec for kernel stats */ + int dp_nretries; /* max num retries on data error */ + int dp_mspw; /* ms per word to xfer, for iostat */ + unsigned short dp_xgap1; /* Gap 1 for xylogics controllers */ + unsigned short dp_xsync; /* sync delay for xylogics controllers */ + unsigned short dp_xrdly; /* read delay for xylogics controllers */ + unsigned short dp_xgap2; /* gap 2 for xylogics controllers */ + unsigned short dp_xrgate; /* read gate for xylogics controllers */ + unsigned short dp_xwcont; /* write continuation for xylogics */ +}; + +/* + * Device characterization flags + * (dp_flags) + */ +#define DP_SECTSLIP 0x00000001 /* sector slip to spare sector */ +#define DP_SECTFWD 0x00000002 /* forward to replacement sector */ +#define DP_TRKFWD 0x00000004 /* forward to replacement track */ +#define DP_MULTIVOL 0x00000008 /* multiple volumes per spindle */ +#define DP_IGNOREERRORS 0x00000010 /* transfer data regardless of errors */ +#define DP_RESEEK 0x00000020 /* recalibrate as last resort */ +#define DP_CTQ_EN 0x00000040 /* enable command tag queueing */ + +/* + * Boot blocks, bad sector tables, and the error summary table, are located + * via the volume_directory. + */ +#define VDNAMESIZE 8 + +struct volume_directory { + char vd_name[VDNAMESIZE]; /* name */ + unsigned int vd_lbn; /* logical block number */ + unsigned int vd_nbytes; /* file length in bytes */ +}; + +/* + * partition table describes logical device partitions + * (device drivers examine this to determine mapping from logical units + * to cylinder groups, device formatters/verifiers examine this to determine + * location of replacement tracks/sectors, etc) + * + * NOTE: pt_firstlbn SHOULD BE CYLINDER ALIGNED + */ +struct partition_table { /* one per logical partition */ + unsigned int pt_nblks; /* # of logical blks in partition */ + unsigned int pt_firstlbn; /* first lbn of partition */ + int pt_type; /* use of partition */ +}; + +#define PTYPE_VOLHDR 0 /* partition is volume header */ +#define PTYPE_TRKREPL 1 /* partition is used for repl trks */ +#define PTYPE_SECREPL 2 /* partition is used for repl secs */ +#define PTYPE_RAW 3 /* partition is used for data */ +#define PTYPE_BSD42 4 /* partition is 4.2BSD file system */ +#define PTYPE_BSD 4 /* partition is 4.2BSD file system */ +#define PTYPE_SYSV 5 /* partition is SysV file system */ +#define PTYPE_VOLUME 6 /* partition is entire volume */ +#define PTYPE_EFS 7 /* partition is sgi EFS */ +#define PTYPE_LVOL 8 /* partition is part of a logical vol */ +#define PTYPE_RLVOL 9 /* part of a "raw" logical vol */ +#define PTYPE_XFS 10 /* partition is sgi XFS */ +#define PTYPE_XFSLOG 11 /* partition is sgi XFS log */ +#define PTYPE_XLV 12 /* partition is part of an XLV vol */ +#define PTYPE_XVM 13 /* partition is sgi XVM */ +#define NPTYPES 16 + +#define VHMAGIC 0xbe5a941 /* randomly chosen value */ +#define NPARTAB 16 /* 16 unix partitions */ +#define NVDIR 15 /* max of 15 directory entries */ +#define BFNAMESIZE 16 /* max 16 chars in boot file name */ + +/* Partition types for ARCS */ +#define NOT_USED 0 /* Not used */ +#define FAT_SHORT 1 /* FAT file system, 12-bit FAT entries */ +#define FAT_LONG 4 /* FAT file system, 16-bit FAT entries */ +#define EXTENDED 5 /* extended partition */ +#define HUGE 6 /* huge partition- MS/DOS 4.0 and later */ + +/* Active flags for ARCS */ +#define BOOTABLE 0x00; +#define NOT_BOOTABLE 0x80; + +struct volume_header { + int vh_magic; /* identifies volume header */ + short vh_rootpt; /* root partition number */ + short vh_swappt; /* swap partition number */ + char vh_bootfile[BFNAMESIZE]; /* name of file to boot */ + struct device_parameters vh_dp; /* device parameters */ + struct volume_directory vh_vd[NVDIR]; /* other vol hdr contents */ + struct partition_table vh_pt[NPARTAB]; /* device partition layout */ + int vh_csum; /* volume header checksum */ + int vh_fill; /* fill out to 512 bytes */ +}; + +#endif /* _SYS_DVH_H */ diff --git a/libparted/labels/efi_crc32.c b/libparted/labels/efi_crc32.c new file mode 100644 index 0000000..c25409f --- /dev/null +++ b/libparted/labels/efi_crc32.c @@ -0,0 +1,126 @@ +/* + * Dec 5, 2000 Matt Domsch <Matt_Domsch@dell.com> + * - Copied crc32.c from the linux/drivers/net/cipe directory. + * - Now pass seed as an arg + * - changed unsigned long to uint32_t, added #include<stdint.h> + * - changed len to be an unsigned long + * - changed crc32val to be a register + * - License remains unchanged! It's still GPL-compatable! + */ + + /* ============================================================= */ + /* 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 <config.h> +#include <stdint.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 + }; + +/* Return a 32-bit CRC of the contents of the buffer. */ + +uint32_t _GL_ATTRIBUTE_PURE +__efi_crc32(const void *buf, unsigned long len, uint32_t seed) +{ + unsigned long i; + register uint32_t crc32val; + const unsigned char *s = buf; + + crc32val = seed; + for (i = 0; i < len; i ++) + { + crc32val = + crc32_tab[(crc32val ^ s[i]) & 0xff] ^ + (crc32val >> 8); + } + return crc32val; +} diff --git a/libparted/labels/fdasd.c b/libparted/labels/fdasd.c new file mode 100644 index 0000000..cee4d46 --- /dev/null +++ b/libparted/labels/fdasd.c @@ -0,0 +1,1442 @@ +/* + * File...........: arch/s390/tools/fdasd.c + * Author(s)......: Volker Sameske <sameske@de.ibm.com> + * Bugreports.to..: <Linux390@de.ibm.com> + * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2001 + * + * History of changes (starts March 2001) + * 2001-04-11 possibility to change volume serial added + * possibility to change partition type added + * some changes to DS4HPCHR and DS4DSREC + * 2001-05-03 check for invalid partition numbers added + * wrong free_space calculation bug fixed + * 2001-06-26 '-a' option added, it is now possible to add a single + * partition in non-interactive mode + * 2001-06-26 long parameter support added + * + */ + +#include <config.h> +#include <arch/linux.h> +#include <parted/vtoc.h> +#include <parted/device.h> +#include <parted/fdasd.h> + +#include <parted/parted.h> + +#include <libintl.h> +#if ENABLE_NLS +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +static int +getpos (fdasd_anchor_t *anc, int dsn) +{ + PDEBUG + return anc->partno[dsn]; +} + +static int +getdsn (fdasd_anchor_t *anc, int pos) +{ + PDEBUG + int i; + + for (i=0; i<USABLE_PARTITIONS; i++) { + if (anc->partno[i] == pos) + return i; + } + + return -1; +} + +static void +setpos (fdasd_anchor_t *anc, int dsn, int pos) +{ + PDEBUG + anc->partno[dsn] = pos; +} + +static u_int32_t +get_usable_cylinders (fdasd_anchor_t *anc) +{ + u_int32_t cyl; + + /* large volume */ + if (anc->f4->DS4DEVCT.DS4DSCYL == LV_COMPAT_CYL && + anc->f4->DS4DCYL > anc->f4->DS4DEVCT.DS4DSCYL) + return anc->f4->DS4DCYL; + /* normal volume */ + if (anc->f4->DS4DEVCT.DS4DEVFG & ALTERNATE_CYLINDERS_USED) + cyl = anc->f4->DS4DEVCT.DS4DSCYL - + (u_int16_t) anc->f4->DS4DEVAC; + else + cyl = anc->f4->DS4DEVCT.DS4DSCYL; + return cyl; +} + +static void +get_addr_of_highest_f1_f8_label (fdasd_anchor_t *anc, cchhb_t *addr) +{ + + u_int8_t record; + /* We have to count the follwing labels: + * one format 4 + * one format 5 + * format 7 only if we have moren then BIG_DISK_SIZE tracks + * one for each format 1 or format 8 label == one for each partition + * one for each format 9 label before the last format 8 + * We assume that all partitions use format 8 labels when + * anc->formatted_cylinders > LV_COMPAT_CYL + * Note: Record zero is special, so block 0 on our disk is record 1! + */ + + record = anc->used_partitions + 2; + if (anc->big_disk) + record++; + if (anc->formatted_cylinders > LV_COMPAT_CYL) + record += anc->used_partitions - 1; + vtoc_set_cchhb(addr, VTOC_START_CC, VTOC_START_HH, record); +} + +void +fdasd_cleanup (fdasd_anchor_t *anchor) +{ + PDEBUG + int i; + partition_info_t *part_info, *next; + + if (anchor == NULL) + return; + + free(anchor->f4); + free(anchor->f5); + free(anchor->f7); + free(anchor->vlabel); + + part_info = anchor->first; + for (i = 1; i <= USABLE_PARTITIONS && part_info != NULL; i++) { + next = part_info->next; + free(part_info->f1); + free(part_info); + part_info = next; + } +} + +static void +fdasd_error (fdasd_anchor_t *anc, enum fdasd_failure why, char const *str) +{ + PDEBUG + char error[2*LINE_LENGTH], *message = error; + + switch (why) { + case unable_to_open_disk: + sprintf(error, "fdasd: %s -- %s\n", _("open error"), str); + break; + case unable_to_seek_disk: + sprintf(error, "fdasd: %s -- %s\n", _("seek error"), str); + break; + case unable_to_read_disk: + sprintf(error, "fdasd: %s -- %s\n", _("read error"), str); + break; + case read_only_disk: + sprintf(error, "fdasd: %s -- %s\n", _("write error"), str); + break; + case unable_to_ioctl: + sprintf(error, "fdasd: %s -- %s\n", _("ioctl() error"), str); + break; + case api_version_mismatch: + sprintf(error, "fdasd: %s -- %s\n", + _("API version mismatch"), str); + break; + case wrong_disk_type: + sprintf(error, "fdasd: %s -- %s\n", + _("Unsupported disk type"), str); + break; + case wrong_disk_format: + sprintf(error, "fdasd: %s -- %s\n", + _("Unsupported disk format"), str); + break; + case disk_in_use: + sprintf(error, "fdasd: %s -- %s\n", + _("Disk is in use"), str); + break; + case config_syntax_error: + sprintf(error, "fdasd: %s -- %s\n", + _("Syntax error in config file"), str); + break; + case vlabel_corrupted: + sprintf(error, "fdasd: %s -- %s\n", + _("Volume label is corrupted"), str); + break; + case dsname_corrupted: + sprintf(error, "fdasd: %s -- %s\n", + _("A data set name is corrupted"), str); + break; + case malloc_failed: + sprintf(error, "fdasd: %s -- %s\n", + _("Memory allocation failed"), str); + break; + case device_verification_failed: + sprintf(error, "fdasd: %s -- %s\n", + _("Device verification failed"), + _("The specified device is not a valid DASD device")); + break; + case volser_not_found: + sprintf(error, "fdasd: %s -- %s\n", _("VOLSER not found on device"), str); + break; + default: + sprintf(error, "fdasd: %s: %s\n", _("Fatal error"), str); + } + + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, message); +} + +/* + * initializes the anchor structure and allocates some + * memory for the labels + */ +void +fdasd_initialize_anchor (fdasd_anchor_t * anc) +{ + PDEBUG + int i; + volume_label_t *v; + partition_info_t *p = NULL; + partition_info_t *q = NULL; + + bzero(anc, sizeof(fdasd_anchor_t)); + + for (i=0; i<USABLE_PARTITIONS; i++) + setpos(anc, i, -1); + + bzero(anc->confdata, sizeof(config_data_t)); + + anc->f4 = malloc(sizeof(format4_label_t)); + if (anc->f4 == NULL) + fdasd_error(anc, malloc_failed, "FMT4 DSCB."); + + anc->f5 = malloc(sizeof(format5_label_t)); + if (anc->f5 == NULL) + fdasd_error(anc, malloc_failed, "FMT5 DSCB."); + + anc->f7 = malloc(sizeof(format7_label_t)); + if (anc->f7 == NULL) + fdasd_error(anc, malloc_failed, "FMT7 DSCB."); + + /* template for all format 9 labels */ + anc->f9 = malloc(sizeof(format9_label_t)); + if (anc->f9 == NULL) + fdasd_error(anc, malloc_failed, "FMT9 DSCB."); + + bzero(anc->f4, sizeof(format4_label_t)); + bzero(anc->f5, sizeof(format5_label_t)); + bzero(anc->f7, sizeof(format7_label_t)); + bzero(anc->f9, sizeof(format9_label_t)); + vtoc_init_format9_label(anc->f9); + + v = malloc(sizeof(volume_label_t)); + if (v == NULL) + fdasd_error(anc, malloc_failed, + _("No room for volume label.")); + bzero(v, sizeof(volume_label_t)); + anc->vlabel = v; + + for (i=1; i<=USABLE_PARTITIONS; i++) { + p = malloc(sizeof(partition_info_t)); + if (p == NULL) + fdasd_error(anc, malloc_failed, + _("No room for partition info.")); + bzero(p, sizeof(partition_info_t)); + + /* add p to double pointered list */ + if (i == 1) { + anc->first = p; + } else if (i == USABLE_PARTITIONS) { + anc->last = p; + p->prev = q; + q->next = p; + } else { + p->prev = q; + q->next = p; + } + + p->f1 = malloc(sizeof(format1_label_t)); + if (p->f1 == NULL) + fdasd_error(anc, malloc_failed, "FMT1 DSCB."); + bzero(p->f1, sizeof(format1_label_t)); + + q = p; + } + anc->hw_cylinders = 0; + anc->formatted_cylinders = 0; + anc->is_file = 0; +} + +/* + * writes all changes to dasd + */ +static void +fdasd_write_vtoc_labels (fdasd_anchor_t * anc, int fd) +{ + PDEBUG + partition_info_t *p; + unsigned long b, maxblk; + char dsno[6], s1[VOLSER_LENGTH + 1], s2[45], *c1, *c2, *ch; + int i = 0, k = 0; + cchhb_t f9addr; + format1_label_t emptyf1; + + b = (cchhb2blk (&anc->vlabel->vtoc, &anc->geo) - 1) * anc->blksize; + if (b <= 0) + fdasd_error (anc, vlabel_corrupted, ""); + maxblk = b + anc->blksize * 9; /* f4+f5+f7+3*f8+3*f9 */ + + /* write FMT4 DSCB */ + vtoc_write_label (fd, b, NULL, anc->f4, NULL, NULL, NULL); + b += anc->blksize; + + /* write FMT5 DSCB */ + vtoc_write_label (fd, b, NULL, NULL, anc->f5, NULL, NULL); + b += anc->blksize; + + /* write FMT7 DSCB */ + if (anc->big_disk) { + vtoc_write_label (fd, b, NULL, NULL, NULL, anc->f7, NULL); + b += anc->blksize; + } + + /* loop over all partitions (format 1 or format 8 DCB) */ + for (p = anc->first; p != NULL; p = p->next) { + + if (p->used != 0x01) { + continue; + } + + i++; + strncpy (p->f1->DS1DSSN, anc->vlabel->volid, 6); + + ch = p->f1->DS1DSNAM; + vtoc_ebcdic_dec (ch, ch, 44); + c1 = ch + 7; + + if (getdsn (anc, i-1) > -1) { + /* re-use the existing data set name */ + c2 = strchr (c1, '.'); + if (c2 != NULL) + strncpy (s2, c2, 31); + else + fdasd_error (anc, dsname_corrupted, ""); + + strncpy (s1, anc->vlabel->volid, 6); + vtoc_ebcdic_dec (s1, s1, 6); + s1[6] = ' '; + strncpy (c1, s1, 7); + c1 = strchr (ch, ' '); + strncpy (c1, s2, 31); + } else { + /* create a new data set name */ + while (getpos (anc, k) > -1) + k++; + + setpos (anc, k, i-1); + + strncpy (ch, "LINUX.V " " ", 44); + + strncpy (s1, anc->vlabel->volid, 6); + vtoc_ebcdic_dec (s1, s1, 6); + strncpy (c1, s1, 6); + + c1 = strchr (ch, ' '); + strncpy (c1, ".PART", 5); + c1 += 5; + + sprintf (dsno, "%04d.", k + 1); + strncpy (c1, dsno, 5); + + c1 += 5; + switch(p->type) { + case PARTITION_LINUX_LVM: + strncpy(c1, PART_TYPE_LVM, 6); + break; + case PARTITION_LINUX_RAID: + strncpy(c1, PART_TYPE_RAID, 6); + break; + case PARTITION_LINUX: + strncpy(c1, PART_TYPE_NATIVE, 6); + break; + case PARTITION_LINUX_SWAP: + strncpy(c1, PART_TYPE_SWAP, 6); + break; + default: + strncpy(c1, PART_TYPE_NATIVE, 6); + break; + } + } + + vtoc_ebcdic_enc (ch, ch, 44); + + if (p->f1->DS1FMTID == 0xf8 ) { + /* Now as we know where which label will be written, we + * can add the address of the format 9 label to the + * format 8 label. The f9 record will be written to the + * block after the current blk. Remember: records are of + * by one, so we have to add 2 and not just one. + */ + vtoc_set_cchhb(&f9addr, VTOC_START_CC, VTOC_START_HH, + ((b / anc->blksize) % anc->geo.sectors) + 2); + vtoc_update_format8_label(&f9addr, p->f1); + vtoc_write_label(fd, b, p->f1, NULL, NULL, NULL, NULL); + b += anc->blksize; + vtoc_write_label(fd, b, NULL, NULL, NULL, NULL, + anc->f9); + b += anc->blksize; + } else { + vtoc_write_label(fd, b, p->f1, NULL, NULL, NULL, NULL); + b += anc->blksize; + } + } + + /* write empty labels to the rest of the blocks */ + bzero(&emptyf1, sizeof(emptyf1)); + while (b < maxblk) { + vtoc_write_label(fd, b, &emptyf1, NULL, NULL, NULL, NULL); + b += anc->blksize; + } +} + +/* + * writes all changes to dasd + */ +int +fdasd_write_labels (fdasd_anchor_t * anc, int fd) +{ + PDEBUG + if (anc->vlabel_changed) + vtoc_write_volume_label (fd, anc->label_pos, anc->vlabel); + + if (anc->vtoc_changed) + fdasd_write_vtoc_labels (anc, fd); + + return 1; +} + +/* + * writes all changes to dasd + */ +int +fdasd_prepare_labels (fdasd_anchor_t *anc, int fd) +{ + PDEBUG + partition_info_t *p; + char dsno[6], s1[7], s2[45], *c1, *c2, *ch; + int i = 0, k = 0; + + /* loop over all partitions (format 1 or format 8 DCB) */ + for (p = anc->first; p != NULL; p = p->next) { + + if (p->used != 0x01) { + continue; + } + + i++; + strncpy (p->f1->DS1DSSN, anc->vlabel->volid, 6); + + ch = p->f1->DS1DSNAM; + vtoc_ebcdic_dec (ch, ch, 44); + c1 = ch + 7; + + if (getdsn (anc, i-1) > -1) { + /* re-use the existing data set name */ + c2 = strchr (c1, '.'); + if (c2 != NULL) + strncpy (s2, c2, 31); + else + fdasd_error (anc, dsname_corrupted, ""); + + strncpy (s1, anc->vlabel->volid, 6); + vtoc_ebcdic_dec (s1, s1, 6); + s1[6] = ' '; + strncpy (c1, s1, 7); + c1 = strchr (ch, ' '); + strncpy (c1, s2, 31); + } else { + /* create a new data set name */ + while (getpos (anc, k) > -1) + k++; + + setpos (anc, k, i-1); + + strncpy (ch, "LINUX.V " " ", 44); + + strncpy (s1, anc->vlabel->volid, 6); + vtoc_ebcdic_dec (s1, s1, 6); + strncpy (c1, s1, 6); + + c1 = strchr (ch, ' '); + strncpy (c1, ".PART", 5); + c1 += 5; + + sprintf (dsno, "%04d.", k + 1); + strncpy (c1, dsno, 5); + + c1 += 5; + switch(p->type) { + case PARTITION_LINUX_LVM: + strncpy(c1, PART_TYPE_LVM, 6); + break; + case PARTITION_LINUX_RAID: + strncpy(c1, PART_TYPE_RAID, 6); + break; + case PARTITION_LINUX: + strncpy(c1, PART_TYPE_NATIVE, 6); + break; + case PARTITION_LINUX_SWAP: + strncpy(c1, PART_TYPE_SWAP, 6); + break; + default: + strncpy(c1, PART_TYPE_NATIVE, 6); + break; + } + } + + vtoc_ebcdic_enc (ch, ch, 44); + } + + return 1; +} + +void +fdasd_recreate_vtoc (fdasd_anchor_t *anc) +{ + PDEBUG + partition_info_t *p = anc->first; + int i; + + vtoc_init_format4_label(anc->f4, + anc->geo.cylinders, + anc->formatted_cylinders, + anc->geo.heads, + anc->geo.sectors, + anc->blksize, + anc->dev_type); + + vtoc_init_format5_label(anc->f5); + vtoc_init_format7_label(anc->f7); + vtoc_set_freespace(anc->f4, anc->f5, anc->f7, + '+', anc->verbose, + FIRST_USABLE_TRK, + anc->formatted_cylinders * anc->geo.heads - 1, + anc->formatted_cylinders, anc->geo.heads); + + for (i = 0; i < USABLE_PARTITIONS; i++) { + bzero(p->f1, sizeof(format1_label_t)); + p->used = 0x00; + p->start_trk = 0; + p->end_trk = 0; + p->len_trk = 0; + p->fspace_trk = 0; + p->type = 0; + p = p->next; + } + + anc->used_partitions = 0; + anc->fspace_trk = anc->formatted_cylinders * anc->geo.heads - + FIRST_USABLE_TRK; + + for (i=0; i<USABLE_PARTITIONS; i++) + setpos(anc, i, -1); + + anc->vtoc_changed++; +} + + /* + * initialize the VOL1 volume label + */ +static void +fdasd_init_volume_label(fdasd_anchor_t *anc, int fd) +{ + volume_label_t *vlabel = anc->vlabel; + + vtoc_volume_label_init(vlabel); + vtoc_volume_label_set_key(vlabel, "VOL1"); + vtoc_volume_label_set_label(vlabel, "VOL1"); + + vtoc_set_cchhb(&vlabel->vtoc, VTOC_START_CC, VTOC_START_HH, 0x01); +} + + +/* + * sets some important partition data + * (like used, start_trk, end_trk, len_trk) + * by calculating these values with the + * information provided in the labels + */ +static void +fdasd_update_partition_info (fdasd_anchor_t *anc) +{ + PDEBUG + partition_info_t *q = NULL, *p = anc->first; + unsigned long max = anc->formatted_cylinders * anc->geo.heads - 1; + int i; + char *ch; + + anc->used_partitions = anc->geo.sectors - 2 - anc->f4->DS4DSREC; + + for (i = 1; i <= USABLE_PARTITIONS; i++) { + if (p->f1->DS1FMTID != 0xf1 && + p->f1->DS1FMTID != 0xf8) { + if (i == 1) + /* there is no partition at all */ + anc->fspace_trk = max - FIRST_USABLE_TRK + 1; + else + /* previous partition was the last one */ + q->fspace_trk = max - q->end_trk; + break; + } + + /* this is a valid format 1 label */ + p->used = 0x01; + p->start_trk = cchh2trk(&p->f1->DS1EXT1.llimit, &anc->geo); + p->end_trk = cchh2trk(&p->f1->DS1EXT1.ulimit, &anc->geo); + p->len_trk = p->end_trk - p->start_trk + 1; + + if (i == 1) { + /* first partition, there is at least one */ + anc->fspace_trk = p->start_trk - FIRST_USABLE_TRK; + } else { + if (i == USABLE_PARTITIONS) + /* last possible partition */ + p->fspace_trk = max - p->end_trk; + + /* set free space values of previous partition */ + q->fspace_trk = p->start_trk - q->end_trk - 1; + } + + ch = p->f1->DS1DSNAM; + vtoc_ebcdic_dec (ch, ch, 44); + if (strstr(ch, PART_TYPE_LVM)) + p->type = PARTITION_LINUX_LVM; + else if (strstr(ch, PART_TYPE_RAID)) + p->type = PARTITION_LINUX_RAID; + else if (strstr(ch, PART_TYPE_NATIVE)) + p->type = PARTITION_LINUX; + else if (strstr(ch, PART_TYPE_SWAP)) + p->type = PARTITION_LINUX_SWAP; + else + p->type = PARTITION_LINUX; + vtoc_ebcdic_enc (ch, ch, 44); + + q = p; + p = p->next; + } +} + +/* + * reorganizes all FMT1s, after that all used FMT1s should be right in + * front of all unused FMT1s + */ +static void +fdasd_reorganize_FMT1s (fdasd_anchor_t *anc) +{ + PDEBUG + int i, j; + format1_label_t *ltmp; + partition_info_t *ptmp; + + for (i=1; i<=USABLE_PARTITIONS - 1; i++) { + ptmp = anc->first; + + for (j=1; j<=USABLE_PARTITIONS - i; j++) { + if (ptmp->f1->DS1FMTID < ptmp->next->f1->DS1FMTID) { + ltmp = ptmp->f1; + ptmp->f1 = ptmp->next->f1; + ptmp->next->f1 = ltmp; + } + + ptmp=ptmp->next; + } + } +} + +static void +fdasd_process_valid_vtoc (fdasd_anchor_t * anc, unsigned long b, int fd) +{ + PDEBUG + int f5_counter = 0, f7_counter = 0, f1_counter = 0, oldfmt = 0; + int i, n, f1size = sizeof (format1_label_t); + partition_info_t *p = anc->first; + format1_label_t q; + char s[5], *ch; + + if (anc->f4->DS4DEVCT.DS4DSCYL == LV_COMPAT_CYL && + anc->f4->DS4DCYL > anc->f4->DS4DEVCT.DS4DSCYL) + anc->formatted_cylinders = anc->f4->DS4DCYL; + else + anc->formatted_cylinders = anc->f4->DS4DEVCT.DS4DSCYL; + anc->fspace_trk = anc->formatted_cylinders * anc->geo.heads - + FIRST_USABLE_TRK; + b += anc->blksize; + + for (i = 1; i < anc->geo.sectors; i++) { + bzero (&q, f1size); + vtoc_read_label (fd, b, &q, NULL, NULL, NULL); + + switch (q.DS1FMTID) { + case 0xf1: + case 0xf8: + if (p == NULL) + break; + memcpy (p->f1, &q, f1size); + + n = -1; + vtoc_ebcdic_dec (p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + ch = strstr (p->f1->DS1DSNAM, "PART"); + if (ch != NULL) { + strncpy (s, ch + 4, 4); + s[4] = '\0'; + n = atoi (s) - 1; + } + + vtoc_ebcdic_enc (p->f1->DS1DSNAM, p->f1->DS1DSNAM, 44); + + /* this dasd has data set names 0000-0002 + but we use now 0001-0003 */ + if (n == -1) + oldfmt++; + + if (((oldfmt == 0) && (n < 0)) || (n >= USABLE_PARTITIONS)) { + /* no op */ + } else { + if (oldfmt) { + /* correct +1 */ + setpos (anc, n + 1, f1_counter); + } else { + setpos (anc, n, f1_counter); + } + } + + p = p->next; + f1_counter++; + break; + case 0xf5: + memcpy (anc->f5, &q, f1size); + f5_counter++; + break; + case 0xf7: + if (f7_counter == 0) + memcpy (anc->f7, &q, f1size); + f7_counter++; + break; + case 0xf9: + /* each format 8 lable has an associated + * format 9 lable, but they are of no further + * use to us. + */ + break; + } + + b += anc->blksize; + } + + if (oldfmt > 0) { + /* this is the old format PART0000 - PART0002 */ + anc->vtoc_changed++; + } + + if ((f5_counter == 0) || (anc->big_disk)) + vtoc_init_format5_label (anc->f5); + + if (f7_counter == 0) + vtoc_init_format7_label (anc->f7); + + fdasd_reorganize_FMT1s (anc); + fdasd_update_partition_info (anc); +} + +static void +fdasd_invalid_vtoc_pointer(fdasd_anchor_t *anc) +{ + PDEBUG + anc->formatted_cylinders = anc->hw_cylinders; + anc->fspace_trk = anc->formatted_cylinders * anc->geo.heads + - FIRST_USABLE_TRK; + vtoc_init_format4_label(anc->f4, + anc->geo.cylinders, anc->formatted_cylinders, + anc->geo.heads, anc->geo.sectors, + anc->blksize, anc->dev_type); + + vtoc_init_format5_label(anc->f5); + vtoc_init_format7_label(anc->f7); + + vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', anc->verbose, + FIRST_USABLE_TRK, + anc->formatted_cylinders * anc->geo.heads - 1, + anc->formatted_cylinders, anc->geo.heads); + + vtoc_set_cchhb(&anc->vlabel->vtoc, VTOC_START_CC, VTOC_START_HH, 0x01); + anc->vtoc_changed++; + anc->vlabel_changed++; +} + +/* + * we have a invalid FMT4 DSCB and therefore we will re-create the VTOC + */ +static void +fdasd_process_invalid_vtoc(fdasd_anchor_t *anc) +{ + anc->formatted_cylinders = anc->hw_cylinders; + anc->fspace_trk = anc->formatted_cylinders * anc->geo.heads + - FIRST_USABLE_TRK; + vtoc_init_format4_label(anc->f4, + anc->geo.cylinders, anc->formatted_cylinders, + anc->geo.heads, anc->geo.sectors, + anc->blksize, anc->dev_type); + + vtoc_init_format5_label(anc->f5); + vtoc_init_format7_label(anc->f7); + vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', anc->verbose, + FIRST_USABLE_TRK, + anc->formatted_cylinders * anc->geo.heads - 1, + anc->formatted_cylinders, anc->geo.heads); + + anc->vtoc_changed++; +} + + +static int +fdasd_valid_vtoc_pointer(fdasd_anchor_t *anc, unsigned long b, int fd) +{ + PDEBUG + char str[LINE_LENGTH]; + + /* VOL1 label contains valid VTOC pointer */ + vtoc_read_label (fd, b, NULL, anc->f4, NULL, NULL); + + if (anc->f4->DS4IDFMT == 0xf4) { + fdasd_process_valid_vtoc (anc, b, fd); + return 0; + } else { + fdasd_process_invalid_vtoc(anc); + } + if (strncmp(anc->vlabel->volkey, vtoc_ebcdic_enc("LNX1",str,4),4) == 0 || + strncmp(anc->vlabel->volkey, vtoc_ebcdic_enc("CMS1",str,4),4) == 0) + return 0; + + fdasd_error(anc, wrong_disk_format, _("Invalid VTOC.")); + return 1; +} + +/* + * check the dasd for a volume label + */ +int +fdasd_check_volume (fdasd_anchor_t *anc, int fd) +{ + PDEBUG + volume_label_t *v = anc->vlabel; + long long b = -1; + char str[LINE_LENGTH]; + + memset(v, 0, sizeof(volume_label_t)); + vtoc_read_volume_label (fd, anc->label_pos, v); + + if (strncmp(v->vollbl, vtoc_ebcdic_enc ("VOL1", str, 4), 4) == 0) { + if (anc->FBA_layout != 1 ) { + /* found VOL1 volume label */ + b = (cchhb2blk (&v->vtoc, &anc->geo) - 1) * anc->blksize; + + if (b > 0) { + int rc; + rc = fdasd_valid_vtoc_pointer (anc, b, fd); + + if (rc < 0) + return 1; + else + return 0; + } else { + fdasd_invalid_vtoc_pointer(anc); + } + } + } else if (strncmp (v->volkey, vtoc_ebcdic_enc ("LNX1", str, 4), 4) == 0 || + strncmp (v->volkey, vtoc_ebcdic_enc ("CMS1", str, 4), 4) == 0) { + return 0; + } else if (anc->FBA_layout == 1) { + /* Some times LDL formatted disks does not + contain any volume label */ + return 1; + } else if (! anc->is_file) { + /* didn't find VOL1 volume label */ + anc->formatted_cylinders = anc->hw_cylinders; + anc->fspace_trk = anc->formatted_cylinders * anc->geo.heads + - FIRST_USABLE_TRK; + + fdasd_init_volume_label(anc, fd); + + vtoc_init_format4_label(anc->f4, + anc->geo.cylinders, anc->formatted_cylinders, + anc->geo.heads, anc->geo.sectors, + anc->blksize, anc->dev_type); + + vtoc_init_format5_label(anc->f5); + vtoc_init_format7_label(anc->f7); + + vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '+', + anc->verbose, FIRST_USABLE_TRK, + anc->formatted_cylinders * anc->geo.heads - 1, + anc->formatted_cylinders, anc->geo.heads); + return 0; + } + return 1; +} + +/* + * checks the current API version with the API version of the dasd driver + */ +void +fdasd_check_api_version (fdasd_anchor_t *anc, int f) +{ + PDEBUG + int api; + char s[2*LINE_LENGTH]; + + struct stat st; + if (fstat (f, &st) == 0 && S_ISREG (st.st_mode)) { + /* skip these tests when F is a regular file. */ + } + else { + if (ioctl(f, DASDAPIVER, &api) != 0) + fdasd_error(anc, unable_to_ioctl, + _("Could not retrieve API version.")); + + if (api != DASD_MIN_API_VERSION) { + sprintf(s, _("The current API version '%d' doesn't " \ + "match dasd driver API version " \ + "'%d'!"), api, DASD_MIN_API_VERSION); + fdasd_error(anc, api_version_mismatch, s); + } + } +} + +/* + * The following two functions match those in the DASD ECKD device driver. + * They are used to compute how many records of a given size can be stored + * in one track. + */ +static unsigned int ceil_quot(unsigned int d1, unsigned int d2) +{ + return (d1 + (d2 - 1)) / d2; +} + +/* kl: key length, dl: data length */ +static unsigned int recs_per_track(unsigned short dev_type, unsigned int kl, + unsigned int dl) +{ + unsigned int dn, kn; + + switch (dev_type) { + case DASD_3380_TYPE: + if (kl) + return 1499 / (15 + 7 + ceil_quot(kl + 12, 32) + + ceil_quot(dl + 12, 32)); + else + return 1499 / (15 + ceil_quot(dl + 12, 32)); + case DASD_3390_TYPE: + dn = ceil_quot(dl + 6, 232) + 1; + if (kl) { + kn = ceil_quot(kl + 6, 232) + 1; + return 1729 / (10 + 9 + ceil_quot(kl + 6 * kn, 34) + + 9 + ceil_quot(dl + 6 * dn, 34)); + } else + return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34)); + case DASD_9345_TYPE: + dn = ceil_quot(dl + 6, 232) + 1; + if (kl) { + kn = ceil_quot(kl + 6, 232) + 1; + return 1420 / (18 + 7 + ceil_quot(kl + 6 * kn, 34) + + ceil_quot(dl + 6 * dn, 34)); + } else + return 1420 / (18 + 7 + ceil_quot(dl + 6 * dn, 34)); + } + return 0; +} + +/* + * Verify that number of tracks (heads) per cylinder and number of + * sectors per track match the expected values for a given device type + * and block size. + * Returns 1 for a valid match and 0 otherwise. + */ +static int fdasd_verify_geometry(unsigned short dev_type, int blksize, + struct fdasd_hd_geometry *geometry) +{ + unsigned int expected_sectors; + if (geometry->heads != 15) + return 0; + expected_sectors = recs_per_track(dev_type, 0, blksize); + if (geometry->sectors == expected_sectors) + return 1; + return 0; +} + +/* + * reads dasd geometry data + */ +int +fdasd_get_geometry (const PedDevice *dev, fdasd_anchor_t *anc, int f) +{ + PDEBUG + int blksize = 0; + dasd_information_t dasd_info; + struct dasd_eckd_characteristics *characteristics; + unsigned long long size_in_bytes; + + /* We can't get geometry from a regular file, + so simulate something usable, for the sake of testing. */ + struct stat st; + if (fstat (f, &st) == 0 && S_ISREG (st.st_mode)) { + PedSector n_sectors = st.st_size / dev->sector_size; + anc->geo.heads = 15; + anc->geo.sectors = 12; + anc->geo.cylinders + = (n_sectors / (anc->geo.heads * anc->geo.sectors + * (dev->sector_size / dev->phys_sector_size))); + anc->geo.start = 0; + blksize = 4096; + memcpy (dasd_info.type, "ECKD", 4); + dasd_info.dev_type = 13200; + dasd_info.label_block = 2; + dasd_info.devno = 513; + dasd_info.label_block = 2; + dasd_info.FBA_layout = 0; + anc->hw_cylinders = ((st.st_size / blksize) / anc->geo.sectors) / + anc->geo.heads; + anc->is_file = 1; + } else { + if (ioctl(f, BLKGETSIZE64, &size_in_bytes) != 0) { + fdasd_error(anc, unable_to_ioctl, + _("Could not retrieve disk size.")); + goto error; + } + + if (ioctl(f, HDIO_GETGEO, &anc->geo) != 0 || + anc->geo.heads == 0 || + anc->geo.sectors == 0 || + anc->geo.cylinders == 0 ) { + fdasd_error(anc, unable_to_ioctl, + _("Could not retrieve disk geometry information.")); + goto error; + } + + if (ioctl(f, BLKSSZGET, &blksize) != 0) { + fdasd_error(anc, unable_to_ioctl, + _("Could not retrieve blocksize information.")); + goto error; + } + + /* get disk type */ + if (ioctl(f, BIODASDINFO, &dasd_info) != 0) { + /* verify that the geometry matches a 3390 DASD */ + if (!fdasd_verify_geometry(DASD_3390_TYPE, blksize, + &anc->geo)) { + fdasd_error(anc, wrong_disk_type, + _("Disk geometry does not match a " \ + "DASD device of type 3390.")); + goto error; + } + anc->dev_type = DASD_3390_TYPE; + anc->hw_cylinders = + size_in_bytes / (blksize * anc->geo.heads * anc->geo.sectors); + /* The VOL1 label on a CDL formatted ECKD DASD is in block 2 + * It will be verified later, if this position actually holds a + * valid label record. + */ + anc->label_pos = 2 * blksize; + /* A devno 0 is actually a valid devno, which could exist + * in the system. Since we use this number only to create + * a default volume serial, there is no serious conflict. + */ + anc->devno = 0; + } else { + characteristics = (struct dasd_eckd_characteristics *) + &dasd_info.characteristics; + if (characteristics->no_cyl == LV_COMPAT_CYL && + characteristics->long_no_cyl) + anc->hw_cylinders = characteristics->long_no_cyl; + else + anc->hw_cylinders = characteristics->no_cyl; + anc->dev_type = dasd_info.dev_type; + anc->label_pos = dasd_info.label_block * blksize; + anc->devno = dasd_info.devno; + anc->label_block = dasd_info.label_block; + anc->FBA_layout = dasd_info.FBA_layout; + } + + anc->is_file = 0; + } + + anc->blksize = blksize; + return 1; + + error: + return 0; +} + +/* + * returns unused partition info pointer if there + * is a free partition, otherwise NULL + */ +static partition_info_t * +fdasd_get_empty_f1_label (fdasd_anchor_t * anc) +{ + PDEBUG + if (anc->used_partitions < USABLE_PARTITIONS) + return anc->last; + else + return NULL; +} + +/* + * asks for and sets some important partition data + */ +static int +fdasd_get_partition_data (fdasd_anchor_t *anc, extent_t *part_extent, + partition_info_t *p, unsigned int *start_ptr, + unsigned int *stop_ptr) +{ + PDEBUG + unsigned int limit; + u_int32_t cc, c; + u_int16_t hh, h; + cchh_t llimit, ulimit; + partition_info_t *q; + u_int8_t b1, b2; + unsigned int start = *start_ptr, stop = *stop_ptr; + int i; + char *ch; + + c = get_usable_cylinders(anc); + h = anc->f4->DS4DEVCT.DS4DSTRK; + limit = (h * c - 1); + + /* check start value from user */ + q = anc->first; + for (i = 0; i < USABLE_PARTITIONS; i++) { + if ( q->next == NULL ) + break; + + if (start >= q->start_trk && start <= q->end_trk) { + /* start is within another partition */ + start = q->end_trk + 1; + + if (start > limit) { + start = FIRST_USABLE_TRK; + q = anc->first; + } + } + + if (start < q->start_trk) { + limit = q->start_trk - 1; + break; + } + + q = q->next; + } + + if (start == limit) + stop = start; + + /* update partition info */ + p->len_trk = stop - start + 1; + p->start_trk = start; + p->end_trk = stop; + + cc = start / anc->geo.heads; + hh = start - (cc * anc->geo.heads); + vtoc_set_cchh(&llimit, cc, hh); + + /* check for cylinder boundary */ + if (hh == 0) + b1 = 0x81; + else + b1 = 0x01; + + cc = stop / anc->geo.heads; + hh = stop - cc * anc->geo.heads; + vtoc_set_cchh(&ulimit, cc, hh); + + /* it is always the 1st extent */ + b2 = 0x00; + + vtoc_set_extent(part_extent, b1, b2, &llimit, &ulimit); + + *start_ptr = start; + *stop_ptr = stop; + + ch = p->f1->DS1DSNAM; + vtoc_ebcdic_dec (ch, ch, 44); + + if (strstr(ch, PART_TYPE_LVM)) + p->type = PARTITION_LINUX_LVM; + else if (strstr(ch, PART_TYPE_RAID)) + p->type = PARTITION_LINUX_RAID; + else if (strstr(ch, PART_TYPE_NATIVE)) + p->type = PARTITION_LINUX; + else if (strstr(ch, PART_TYPE_SWAP)) + p->type = PARTITION_LINUX_SWAP; + else + p->type = PARTITION_LINUX; + + vtoc_ebcdic_enc (ch, ch, 44); + + return 0; +} + +static void +fdasd_enqueue_new_partition (fdasd_anchor_t *anc) +{ + PDEBUG + partition_info_t *q = anc->first, *p = anc->last; + int i, k=0; + + for (i=1; i<USABLE_PARTITIONS; i++) { + if ((q->end_trk == 0) || (p->start_trk < q->start_trk)) { + break; + } else { + q = q->next; + k++; + } + } + + if (anc->first == q) + anc->first = p; + + if (p != q) { + anc->last->prev->next = NULL; + anc->last = anc->last->prev; + + p->next = q; + p->prev = q->prev; + q->prev = p; + + if (p->prev != NULL) + p->prev->next = p; + } + + p->used = 0x01; + p->type = PARTITION_LINUX; + + for (i=0; i<USABLE_PARTITIONS; i++) { + int j = getpos(anc, i); + if (j >= k) + setpos(anc, i, j + 1); + } + + /* update free-space counters */ + if (anc->first == p) { + /* partition is the first used partition */ + if (p->start_trk == FIRST_USABLE_TRK) { + /* partition starts right behind VTOC */ + p->fspace_trk = anc->fspace_trk - p->len_trk; + anc->fspace_trk = 0; + } else { + /* there is some space between VTOC and partition */ + p->fspace_trk = anc->fspace_trk - p->len_trk - p->start_trk + + FIRST_USABLE_TRK; + anc->fspace_trk = p->start_trk - FIRST_USABLE_TRK; + } + } else { + /* there are partitions in front of the new one */ + if (p->start_trk == p->prev->end_trk + 1) { + /* new partition is right behind the previous one */ + p->fspace_trk = p->prev->fspace_trk - p->len_trk; + p->prev->fspace_trk = 0; + } else { + /* there is some space between new and prev. part. */ + p->fspace_trk = p->prev->fspace_trk - p->len_trk + - p->start_trk + p->prev->end_trk + 1; + p->prev->fspace_trk = p->start_trk - p->prev->end_trk - 1; + } + } +} + +/* + * adds a new partition to the 'partition table' + */ +partition_info_t * +fdasd_add_partition (fdasd_anchor_t *anc, unsigned int start, + unsigned int stop) +{ + PDEBUG + cchhb_t hf1; + partition_info_t *p; + extent_t ext; + + PDEBUG; + + if ((p = fdasd_get_empty_f1_label(anc)) == NULL) { + PDEBUG; + return 0; + } + + PDEBUG; + if (fdasd_get_partition_data(anc, &ext, p, &start, &stop) != 0) + return 0; + + if (anc->formatted_cylinders > LV_COMPAT_CYL) { + vtoc_init_format8_label(anc->blksize, &ext, p->f1); + } else { + PDEBUG; + vtoc_init_format1_label(anc->blksize, &ext, p->f1); + } + + PDEBUG; + fdasd_enqueue_new_partition(anc); + + PDEBUG; + anc->used_partitions += 1; + + get_addr_of_highest_f1_f8_label(anc, &hf1); + vtoc_update_format4_label(anc->f4, &hf1, anc->f4->DS4DSREC - 1); + + PDEBUG; + + start = cchh2trk(&ext.llimit, &anc->geo); + stop = cchh2trk(&ext.ulimit, &anc->geo); + + PDEBUG; + vtoc_set_freespace(anc->f4, anc->f5, anc->f7, '-', anc->verbose, + start, stop, anc->formatted_cylinders, anc->geo.heads); + + anc->vtoc_changed++; + + PDEBUG; + return p; +} + +/* + * Check for valid volume serial characters (max. 6) - remove invalid. + * If volser is empty, fill with default volser. + */ +void fdasd_check_volser (char *volser, int devno) +{ + int from, to; + + for (from = 0, to = 0; volser[from] && from < VOLSER_LENGTH; from++) { + + if ((volser[from] >= 0x23 && + volser[from] <= 0x25) || + (volser[from] >= 0x30 && + volser[from] <= 0x39) || + (volser[from] >= 0x40 && + volser[from] <= 0x5a) || + (volser[from] >= 0x61 && + volser[from] <= 0x7a)) + volser[to++] = toupper(volser[from]); + } + + volser[to] = 0x00; + + if (volser[0] == 0x00) + sprintf(volser, "0X%04x", devno); +} + +/* + * get volser from vtoc + */ +int fdasd_get_volser (fdasd_anchor_t *anc, char *volser, int fd) +{ + volume_label_t vlabel; + + vtoc_read_volume_label(fd, anc->label_pos, &vlabel); + vtoc_volume_label_get_volser(&vlabel, volser); + return 0; +} + +/* Changes the volume serial (menu option) + * + */ +void fdasd_change_volser (fdasd_anchor_t *anc, char *str) +{ + fdasd_check_volser(str, anc->devno); + vtoc_volume_label_set_volser(anc->vlabel, str); + + vtoc_set_cchhb(&anc->vlabel->vtoc, VTOC_START_CC, VTOC_START_HH, 0x01); + anc->vlabel_changed++; + anc->vtoc_changed++; +} + +/* + * re-create all VTOC labels, but use the partition information + * from existing VTOC + */ +void fdasd_reuse_vtoc (fdasd_anchor_t *anc) +{ + partition_info_t *part_info = anc->first; + struct fdasd_hd_geometry geo = anc->geo; + format1_label_t f1; + format4_label_t f4; + format5_label_t f5; + format7_label_t f7; + + vtoc_init_format4_label(&f4, geo.cylinders, anc->formatted_cylinders, + geo.heads, geo.sectors, + anc->blksize, anc->dev_type); + + /* reuse some FMT4 values */ + f4.DS4HPCHR = anc->f4->DS4HPCHR; + f4.DS4DSREC = anc->f4->DS4DSREC; + + /* re-initialize both free-space labels */ + vtoc_init_format5_label(&f5); + vtoc_init_format7_label(&f7); + + if (anc->fspace_trk > 0) + vtoc_set_freespace(&f4, &f5, &f7, '+', anc->verbose, + FIRST_USABLE_TRK, + FIRST_USABLE_TRK + anc->fspace_trk - 1, + anc->formatted_cylinders, geo.heads); + + while (part_info != NULL) { + if (part_info->used != 0x01) { + part_info = part_info->next; + continue; + } + + if (anc->formatted_cylinders > LV_COMPAT_CYL) + vtoc_init_format8_label(anc->blksize, + &part_info->f1->DS1EXT1, &f1); + else + vtoc_init_format1_label(anc->blksize, + &part_info->f1->DS1EXT1, &f1); + + + strncpy(f1.DS1DSNAM, part_info->f1->DS1DSNAM, 44); + strncpy((char *)f1.DS1DSSN, (char *)part_info->f1->DS1DSSN, 6); + f1.DS1CREDT = part_info->f1->DS1CREDT; + + memcpy(part_info->f1, &f1, sizeof(format1_label_t)); + + if (part_info->fspace_trk > 0) + vtoc_set_freespace(&f4, &f5, &f7, '+', anc->verbose, + part_info->end_trk + 1, + part_info->end_trk + + part_info->fspace_trk, + anc->formatted_cylinders, geo.heads); + + part_info = part_info->next; + } + + /* over-write old labels with new ones */ + memcpy(anc->f4, &f4, sizeof(format4_label_t)); + memcpy(anc->f5, &f5, sizeof(format5_label_t)); + memcpy(anc->f7, &f7, sizeof(format7_label_t)); + + anc->vtoc_changed++; + + return; +} + +/* vim:set tabstop=4 shiftwidth=4 softtabstop=4: */ diff --git a/libparted/labels/gpt.c b/libparted/labels/gpt.c new file mode 100644 index 0000000..780fb70 --- /dev/null +++ b/libparted/labels/gpt.c @@ -0,0 +1,1941 @@ +/* + libparted - a library for manipulating disk partitions + + original version by Matt Domsch <Matt_Domsch@dell.com> + Disclaimed into the Public Domain + + Portions Copyright (C) 2001-2003, 2005-2012 Free Software Foundation, Inc. + + EFI GUID Partition Table handling + Per Intel EFI Specification v1.02 + http://developer.intel.com/technology/efi/efi.htm + + 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/>. +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <parted/crc32.h> +#include <inttypes.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <unistd.h> +#include <uuid/uuid.h> +#include <stdbool.h> +#include <errno.h> +#include <iconv.h> +#include <langinfo.h> +#include "xalloc.h" +#include "xalloc-oversized.h" +#include "verify.h" + +#include "pt-tools.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) gettext (String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#define EFI_PMBR_OSTYPE_EFI 0xEE +#define MSDOS_MBR_SIGNATURE 0xaa55 + +#define GPT_HEADER_SIGNATURE 0x5452415020494645LL + +/* NOTE: the document that describes revision 1.00 is labelled "version 1.02", + * so some implementors got confused... + */ +#define GPT_HEADER_REVISION_V1_02 0x00010200 +#define GPT_HEADER_REVISION_V1_00 0x00010000 +#define GPT_HEADER_REVISION_V0_99 0x00009900 + +typedef uint16_t efi_char16_t; /* UNICODE character */ +typedef struct _GuidPartitionTableHeader_t GuidPartitionTableHeader_t; +typedef struct _GuidPartitionEntryAttributes_t GuidPartitionEntryAttributes_t; +typedef struct _GuidPartitionEntry_t GuidPartitionEntry_t; +typedef struct _PartitionRecord_t PartitionRecord_t; +typedef struct _LegacyMBR_t LegacyMBR_t; +typedef struct _GPTDiskData GPTDiskData; + + +typedef struct +{ + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_hi_and_reserved; + uint8_t clock_seq_low; + uint8_t node[6]; +} /* __attribute__ ((packed)) */ efi_guid_t; +/* commented out "__attribute__ ((packed))" to work around gcc bug (fixed + * in gcc3.1): __attribute__ ((packed)) breaks addressing on initialized + * data. It turns out we don't need it in this case, so it doesn't break + * anything :) + */ + +#define UNUSED_ENTRY_GUID \ + ((efi_guid_t) { 0x00000000, 0x0000, 0x0000, 0x00, 0x00, \ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}) +#define PARTITION_SYSTEM_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xC12A7328), PED_CPU_TO_LE16 (0xF81F), \ + PED_CPU_TO_LE16 (0x11d2), 0xBA, 0x4B, \ + { 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B }}) +#define PARTITION_BIOS_GRUB_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0x21686148), PED_CPU_TO_LE16 (0x6449), \ + PED_CPU_TO_LE16 (0x6E6f), 0x74, 0x4E, \ + { 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 }}) +#define LEGACY_MBR_PARTITION_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0x024DEE41), PED_CPU_TO_LE16 (0x33E7), \ + PED_CPU_TO_LE16 (0x11d3, 0x9D, 0x69, \ + { 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F }}) +#define PARTITION_MSFT_RESERVED_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xE3C9E316), PED_CPU_TO_LE16 (0x0B5C), \ + PED_CPU_TO_LE16 (0x4DB8), 0x81, 0x7D, \ + { 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE }}) +#define PARTITION_MSFT_RECOVERY \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xDE94BBA4), PED_CPU_TO_LE16 (0x06D1), \ + PED_CPU_TO_LE16 (0x4D40), 0xA1, 0x6A, \ + { 0xBF, 0xD5, 0x01, 0x79, 0xD6, 0xAC }}) +#define PARTITION_BASIC_DATA_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xEBD0A0A2), PED_CPU_TO_LE16 (0xB9E5), \ + PED_CPU_TO_LE16 (0x4433), 0x87, 0xC0, \ + { 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7 }}) +#define PARTITION_RAID_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xa19d880f), PED_CPU_TO_LE16 (0x05fc), \ + PED_CPU_TO_LE16 (0x4d3b), 0xa0, 0x06, \ + { 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e }}) +#define PARTITION_SWAP_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0x0657fd6d), PED_CPU_TO_LE16 (0xa4ab), \ + PED_CPU_TO_LE16 (0x43c4), 0x84, 0xe5, \ + { 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f }}) +#define PARTITION_LINUX_DATA_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0x0FC63DAF), PED_CPU_TO_LE16 (0x8483), \ + PED_CPU_TO_LE16 (0x4772), 0x8E, 0x79, \ + { 0x3D, 0x69, 0xD8, 0x47, 0x7D, 0xE4 }}) +#define PARTITION_LVM_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xe6d6d379), PED_CPU_TO_LE16 (0xf507), \ + PED_CPU_TO_LE16 (0x44c2), 0xa2, 0x3c, \ + { 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28 }}) +#define PARTITION_RESERVED_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0x8da63339), PED_CPU_TO_LE16 (0x0007), \ + PED_CPU_TO_LE16 (0x60c0), 0xc4, 0x36, \ + { 0x08, 0x3a, 0xc8, 0x23, 0x09, 0x08 }}) +#define PARTITION_HPSERVICE_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xe2a1e728), PED_CPU_TO_LE16 (0x32e3), \ + PED_CPU_TO_LE16 (0x11d6), 0xa6, 0x82, \ + { 0x7b, 0x03, 0xa0, 0x00, 0x00, 0x00 }}) +#define PARTITION_APPLE_HFS_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0x48465300), PED_CPU_TO_LE16 (0x0000), \ + PED_CPU_TO_LE16 (0x11AA), 0xaa, 0x11, \ + { 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC }}) +#define PARTITION_APPLE_TV_RECOVERY_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0x5265636F), PED_CPU_TO_LE16 (0x7665), \ + PED_CPU_TO_LE16 (0x11AA), 0xaa, 0x11, \ + { 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC }}) +#define PARTITION_PREP_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0x9e1a2d38), PED_CPU_TO_LE16 (0xc612), \ + PED_CPU_TO_LE16 (0x4316), 0xaa, 0x26, \ + { 0x8b, 0x49, 0x52, 0x1e, 0x5a, 0x8b }}) +#define PARTITION_IRST_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xD3BFE2DE), PED_CPU_TO_LE16 (0x3DAF), \ + PED_CPU_TO_LE16 (0x11DF), 0xba, 0x40, \ + { 0xE3, 0xA5, 0x56, 0xD8, 0x95, 0x93 }}) +#define PARTITION_CHROMEOS_KERNEL_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xfe3a2a5d), PED_CPU_TO_LE16 (0x4f32), \ + PED_CPU_TO_LE16 (0x41a7), 0xb7, 0x25, \ + { 0xac, 0xcc, 0x32, 0x85, 0xa3, 0x09 }}) +#define PARTITION_BLS_BOOT_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0xbc13c2ff), PED_CPU_TO_LE16 (0x59e6), \ + PED_CPU_TO_LE16 (0x4262), 0xa3, 0x52, \ + { 0xb2, 0x75, 0xfd, 0x6f, 0x71, 0x72 }}) +#define PARTITION_LINUX_HOME_GUID \ + ((efi_guid_t) { PED_CPU_TO_LE32 (0x933ac7e1), PED_CPU_TO_LE16 (0x2eb4), \ + PED_CPU_TO_LE16 (0x4f13), 0xb8, 0x44, \ + { 0x0e, 0x14, 0xe2, 0xae, 0xf9, 0x15 }}) + +struct flag_uuid_mapping_t +{ + enum _PedPartitionFlag flag; + efi_guid_t type_uuid; +}; + +static const struct flag_uuid_mapping_t flag_uuid_mapping[] = +{ + { PED_PARTITION_APPLE_TV_RECOVERY, PARTITION_APPLE_TV_RECOVERY_GUID }, + { PED_PARTITION_BIOS_GRUB, PARTITION_BIOS_GRUB_GUID }, + { PED_PARTITION_BLS_BOOT, PARTITION_BLS_BOOT_GUID }, + { PED_PARTITION_BOOT, PARTITION_SYSTEM_GUID }, + { PED_PARTITION_CHROMEOS_KERNEL, PARTITION_CHROMEOS_KERNEL_GUID }, + { PED_PARTITION_DIAG, PARTITION_MSFT_RECOVERY }, + { PED_PARTITION_ESP, PARTITION_SYSTEM_GUID }, + { PED_PARTITION_HPSERVICE, PARTITION_HPSERVICE_GUID }, + { PED_PARTITION_IRST, PARTITION_IRST_GUID }, + { PED_PARTITION_LINUX_HOME, PARTITION_LINUX_HOME_GUID }, + { PED_PARTITION_LVM, PARTITION_LVM_GUID }, + { PED_PARTITION_MSFT_DATA, PARTITION_BASIC_DATA_GUID }, + { PED_PARTITION_MSFT_RESERVED, PARTITION_MSFT_RESERVED_GUID }, + { PED_PARTITION_PREP, PARTITION_PREP_GUID }, + { PED_PARTITION_RAID, PARTITION_RAID_GUID }, + { PED_PARTITION_SWAP, PARTITION_SWAP_GUID }, +}; + +static const efi_guid_t skip_set_system_guids[] = +{ + PARTITION_LVM_GUID, + PARTITION_SWAP_GUID, + PARTITION_RAID_GUID, + PARTITION_PREP_GUID, + PARTITION_SYSTEM_GUID, + PARTITION_BIOS_GRUB_GUID, + PARTITION_HPSERVICE_GUID, + PARTITION_MSFT_RESERVED_GUID, + PARTITION_BASIC_DATA_GUID, + PARTITION_MSFT_RECOVERY, + PARTITION_APPLE_TV_RECOVERY_GUID, + PARTITION_IRST_GUID, + PARTITION_CHROMEOS_KERNEL_GUID, + PARTITION_BLS_BOOT_GUID, +}; + +static const struct flag_uuid_mapping_t* _GL_ATTRIBUTE_CONST +gpt_find_flag_uuid_mapping (PedPartitionFlag flag) +{ + int n = sizeof(flag_uuid_mapping) / sizeof(flag_uuid_mapping[0]); + + for (int i = 0; i < n; ++i) + if (flag_uuid_mapping[i].flag == flag) + return &flag_uuid_mapping[i]; + + return NULL; +} + +struct __attribute__ ((packed)) _GuidPartitionTableHeader_t +{ + uint64_t Signature; + uint32_t Revision; + uint32_t HeaderSize; + uint32_t HeaderCRC32; + uint32_t Reserved1; + uint64_t MyLBA; + uint64_t AlternateLBA; + uint64_t FirstUsableLBA; + uint64_t LastUsableLBA; + efi_guid_t DiskGUID; + uint64_t PartitionEntryLBA; + uint32_t NumberOfPartitionEntries; + uint32_t SizeOfPartitionEntry; + uint32_t PartitionEntryArrayCRC32; + uint8_t *Reserved2; +}; + +struct __attribute__ ((packed)) _GuidPartitionEntryAttributes_t +{ +#ifdef __GNUC__ /* XXX narrow this down to !TinyCC */ + uint64_t RequiredToFunction:1; + uint64_t NoBlockIOProtocol:1; + uint64_t LegacyBIOSBootable:1; + uint64_t Reserved:45; + uint64_t GuidSpecific:15; + uint64_t NoAutomount:1; +#else +# warning "Using crippled partition entry type" + uint32_t RequiredToFunction:1; + uint32_t NoBlockIOProtocol:1; + uint32_t LegacyBIOSBootable:1; + uint32_t Reserved:30; + uint32_t LOST:5; + uint32_t GuidSpecific:15; + uint32_t NoAutomount:1; +#endif +}; + +struct __attribute__ ((packed)) _GuidPartitionEntry_t +{ + efi_guid_t PartitionTypeGuid; + efi_guid_t UniquePartitionGuid; + uint64_t StartingLBA; + uint64_t EndingLBA; + GuidPartitionEntryAttributes_t Attributes; + efi_char16_t PartitionName[36]; +}; + +#define GPT_PMBR_LBA 0 +#define GPT_PMBR_SECTORS 1 +#define GPT_PRIMARY_HEADER_LBA 1 +#define GPT_HEADER_SECTORS 1 +#define GPT_PRIMARY_PART_TABLE_LBA 2 + +/* + These values are only defaults. The actual on-disk structures + may define different sizes, so use those unless creating a new GPT disk! +*/ + +#define GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE 16384 + +/* Number of actual partition entries should be calculated as: */ +#define GPT_DEFAULT_PARTITION_ENTRIES \ + (GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / \ + sizeof(GuidPartitionEntry_t)) + +struct __attribute__ ((packed)) _PartitionRecord_t +{ + /* Not used by EFI firmware. Set to 0x80 to indicate that this + is the bootable legacy partition. */ + uint8_t BootIndicator; + + /* Start of partition in CHS address, not used by EFI firmware. */ + uint8_t StartHead; + + /* Start of partition in CHS address, not used by EFI firmware. */ + uint8_t StartSector; + + /* Start of partition in CHS address, not used by EFI firmware. */ + uint8_t StartTrack; + + /* OS type. A value of 0xEF defines an EFI system partition. + Other values are reserved for legacy operating systems, and + allocated independently of the EFI specification. */ + uint8_t OSType; + + /* End of partition in CHS address, not used by EFI firmware. */ + uint8_t EndHead; + + /* End of partition in CHS address, not used by EFI firmware. */ + uint8_t EndSector; + + /* End of partition in CHS address, not used by EFI firmware. */ + uint8_t EndTrack; + + /* Starting LBA address of the partition on the disk. Used by + EFI firmware to define the start of the partition. */ + uint32_t StartingLBA; + + /* Size of partition in LBA. Used by EFI firmware to determine + the size of the partition. */ + uint32_t SizeInLBA; +}; + +/* Protected Master Boot Record & Legacy MBR share same structure */ +/* Needs to be packed because the u16s force misalignment. */ +struct __attribute__ ((packed)) _LegacyMBR_t +{ + uint8_t BootCode[440]; + uint32_t UniqueMBRSignature; + uint16_t Unknown; + PartitionRecord_t PartitionRecord[4]; + uint16_t Signature; +}; + +/* uses libparted's disk_specific field in PedDisk, to store our info */ +struct __attribute__ ((packed, aligned(8))) _GPTDiskData +{ + PedGeometry data_area; + int entry_count; + efi_guid_t uuid; + int pmbr_boot; + PedSector AlternateLBA; +}; + +/* uses libparted's disk_specific field in PedPartition, to store our info */ +typedef struct _GPTPartitionData +{ + efi_guid_t type; + efi_guid_t uuid; + efi_char16_t name[37]; + char *translated_name; + GuidPartitionEntryAttributes_t attributes; +} GPTPartitionData; + +static PedDiskType gpt_disk_type; + +static inline uint32_t +pth_get_size (const PedDevice *dev) +{ + return GPT_HEADER_SECTORS * dev->sector_size; +} + +static inline uint32_t +pth_get_size_static (const PedDevice *dev) +{ + return sizeof (GuidPartitionTableHeader_t) - sizeof (uint8_t *); +} + +static inline uint32_t +pth_get_size_rsv2 (const PedDevice *dev) +{ + return pth_get_size (dev) - pth_get_size_static (dev); +} + +static GuidPartitionTableHeader_t * +pth_new (const PedDevice *dev) +{ + GuidPartitionTableHeader_t *pth = + ped_malloc (sizeof (GuidPartitionTableHeader_t) + sizeof (uint8_t)); + + pth->Reserved2 = ped_malloc (pth_get_size_rsv2 (dev)); + + return pth; +} + +static GuidPartitionTableHeader_t * +pth_new_zeroed (const PedDevice *dev) +{ + GuidPartitionTableHeader_t *pth = pth_new (dev); + + memset (pth, 0, pth_get_size_static (dev)); + memset (pth->Reserved2, 0, pth_get_size_rsv2 (dev)); + + return (pth); +} + +static GuidPartitionTableHeader_t * +pth_new_from_raw (const PedDevice *dev, const uint8_t *pth_raw) +{ + GuidPartitionTableHeader_t *pth = pth_new (dev); + + PED_ASSERT (pth_raw != NULL); + + memcpy (pth, pth_raw, pth_get_size_static (dev)); + memcpy (pth->Reserved2, pth_raw + pth_get_size_static (dev), + pth_get_size_rsv2 (dev)); + + return pth; +} + +static void +pth_free (GuidPartitionTableHeader_t *pth) +{ + if (pth == NULL) + return; + PED_ASSERT (pth->Reserved2 != NULL); + + free (pth->Reserved2); + free (pth); +} + +static uint8_t * +pth_get_raw (const PedDevice *dev, const GuidPartitionTableHeader_t *pth) +{ + PED_ASSERT (pth != NULL); + PED_ASSERT (pth->Reserved2 != NULL); + + int size_static = pth_get_size_static (dev); + uint8_t *pth_raw = ped_malloc (pth_get_size (dev)); + if (pth_raw == NULL) + return NULL; + + memcpy (pth_raw, pth, size_static); + memcpy (pth_raw + size_static, pth->Reserved2, pth_get_size_rsv2 (dev)); + + return pth_raw; +} + +/** + * swap_uuid_and_efi_guid() - converts between uuid formats + * @uuid - uuid_t in either format (converts it to the other) + * + * There are two different representations for Globally Unique Identifiers + * (GUIDs or UUIDs). + * + * The RFC specifies a UUID as a string of 16 bytes, essentially + * a big-endian array of char. + * Intel, in their EFI Specification, references the same RFC, but + * then defines a GUID as a structure of little-endian fields. + * Coincidentally, both structures have the same format when unparsed. + * + * When read from disk, EFI GUIDs are in struct of little endian format, + * and need to be converted to be treated as uuid_t in memory. + * + * When writing to disk, uuid_ts need to be converted into EFI GUIDs. + * + * Blame Intel. + */ +static void +swap_uuid_and_efi_guid (efi_guid_t *guid) +{ + PED_ASSERT (guid != NULL); + guid->time_low = PED_SWAP32 (guid->time_low); + guid->time_mid = PED_SWAP16 (guid->time_mid); + guid->time_hi_and_version = PED_SWAP16 (guid->time_hi_and_version); +} + +/* returns the EFI-style CRC32 value for buf + * This function uses the crc32 function by Gary S. Brown, + * but seeds the function with ~0, and xor's with ~0 at the end. + */ +static inline uint32_t +efi_crc32 (const void *buf, unsigned long len) +{ + return (__efi_crc32 (buf, len, ~0L) ^ ~0L); +} + +/* Compute the crc32 checksum of the partition table header + and store it in *CRC32. Return 0 upon success. Return 1 + upon failure to allocate space. */ +static int +pth_crc32 (const PedDevice *dev, const GuidPartitionTableHeader_t *pth, + uint32_t *crc32) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (pth != NULL); + + uint8_t *pth_raw = pth_get_raw (dev, pth); + if (pth_raw == NULL) + return 1; + + *crc32 = efi_crc32 (pth_raw, PED_LE32_TO_CPU (pth->HeaderSize)); + free (pth_raw); + + return 0; +} + +static inline int +guid_cmp (efi_guid_t left, efi_guid_t right) +{ + return memcmp (&left, &right, sizeof (efi_guid_t)); +} + +/* checks if 'mbr' is a protective MBR partition table */ +static inline int _GL_ATTRIBUTE_PURE +_pmbr_is_valid (const LegacyMBR_t *mbr) +{ + int i; + + PED_ASSERT (mbr != NULL); + + if (mbr->Signature != PED_CPU_TO_LE16 (MSDOS_MBR_SIGNATURE)) + return 0; + for (i = 0; i < 4; i++) + { + if (mbr->PartitionRecord[i].OSType == EFI_PMBR_OSTYPE_EFI) + return 1; + } + return 0; +} + +static int +gpt_probe (const PedDevice *dev) +{ + int gpt_sig_found = 0; + + PED_ASSERT (dev != NULL); + + if (dev->length <= 1) + return 0; + + void *label; + if (!ptt_read_sector (dev, 0, &label)) + return 0; + + if (!_pmbr_is_valid (label)) + { + free (label); + return 0; + } + free (label); + + void *pth_raw = ped_malloc (pth_get_size (dev)); + if (ped_device_read (dev, pth_raw, 1, GPT_HEADER_SECTORS) + || ped_device_read (dev, pth_raw, dev->length - 1, GPT_HEADER_SECTORS)) + { + GuidPartitionTableHeader_t *gpt = pth_new_from_raw (dev, pth_raw); + if (gpt->Signature == PED_CPU_TO_LE64 (GPT_HEADER_SIGNATURE)) + gpt_sig_found = 1; + pth_free (gpt); + } + free (pth_raw); + + return gpt_sig_found; +} + +static PedDisk * +gpt_alloc (const PedDevice *dev) +{ + PedDisk *disk; + GPTDiskData *gpt_disk_data; + PedSector data_start, data_end; + + disk = _ped_disk_alloc ((PedDevice *) dev, &gpt_disk_type); + if (!disk) + goto error; + + data_start = 2 + GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / dev->sector_size; + data_end = dev->length - 2 + - GPT_DEFAULT_PARTITION_ENTRY_ARRAY_SIZE / dev->sector_size; + + /* If the device is too small to accommodate GPT headers and one data + sector, reject it. */ + if (data_end < data_start) + { + ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_OK, + _("device is too small for GPT")); + goto error_free_disk; + } + + disk->disk_specific = gpt_disk_data = ped_malloc (sizeof (GPTDiskData)); + if (!disk->disk_specific) + goto error_free_disk; + + gpt_disk_data->AlternateLBA = dev->length - 1; + ped_geometry_init (&gpt_disk_data->data_area, dev, data_start, + data_end - data_start + 1); + gpt_disk_data->entry_count = GPT_DEFAULT_PARTITION_ENTRIES; + uuid_generate ((unsigned char *) &gpt_disk_data->uuid); + swap_uuid_and_efi_guid (&gpt_disk_data->uuid); + gpt_disk_data->pmbr_boot = 0; + return disk; + +error_free_disk: + free (disk); +error: + return NULL; +} + +static PedDisk * +gpt_duplicate (const PedDisk *disk) +{ + PedDisk *new_disk; + GPTDiskData *new_disk_data; + GPTDiskData *old_disk_data; + + new_disk = ped_disk_new_fresh (disk->dev, &gpt_disk_type); + if (!new_disk) + return NULL; + + old_disk_data = disk->disk_specific; + new_disk_data = new_disk->disk_specific; + + ped_geometry_init (&new_disk_data->data_area, disk->dev, + old_disk_data->data_area.start, + old_disk_data->data_area.length); + new_disk_data->entry_count = old_disk_data->entry_count; + new_disk_data->uuid = old_disk_data->uuid; + new_disk_data->pmbr_boot = old_disk_data->pmbr_boot; + return new_disk; +} + +static void +gpt_free (PedDisk *disk) +{ + ped_disk_delete_all (disk); + free (disk->disk_specific); + _ped_disk_free (disk); +} + +/* Given GUID Partition table header, GPT, read its partition array + entries from DISK into malloc'd storage. Set *PTES_BYTES to the + number of bytes required. Upon success, return a pointer to the + resulting buffer. Otherwise, set errno and return NULL. */ +static void * +gpt_read_PE_array (PedDisk const *disk, GuidPartitionTableHeader_t const *gpt, + size_t *ptes_bytes) +{ + uint32_t p_ent_size = PED_LE32_TO_CPU (gpt->SizeOfPartitionEntry); + *ptes_bytes = p_ent_size * PED_LE32_TO_CPU(gpt->NumberOfPartitionEntries); + size_t ptes_sectors = ped_div_round_up (*ptes_bytes, + disk->dev->sector_size); + + if (xalloc_oversized (ptes_sectors, disk->dev->sector_size)) + { + errno = ENOMEM; + return NULL; + } + void *ptes = ped_malloc (ptes_sectors * disk->dev->sector_size); + if (ptes == NULL) + return NULL; + + if (!ped_device_read (disk->dev, ptes, + PED_LE64_TO_CPU (gpt->PartitionEntryLBA), ptes_sectors)) + { + int saved_errno = errno; + free (ptes); + errno = saved_errno; + return NULL; + } + + return ptes; +} + +static int +check_PE_array_CRC (PedDisk const *disk, + GuidPartitionTableHeader_t const *gpt, bool *valid) +{ + size_t ptes_bytes; + void *ptes = gpt_read_PE_array (disk, gpt, &ptes_bytes); + if (ptes == NULL) + return 1; + + uint32_t ptes_crc = efi_crc32 (ptes, ptes_bytes); + *valid = (ptes_crc == PED_LE32_TO_CPU (gpt->PartitionEntryArrayCRC32)); + free (ptes); + return 0; +} + +static int +_header_is_valid (PedDisk const *disk, GuidPartitionTableHeader_t *gpt, + PedSector my_lba) +{ + uint32_t crc, origcrc; + PedDevice const *dev = disk->dev; + + if (PED_LE64_TO_CPU (gpt->Signature) != GPT_HEADER_SIGNATURE) + return 0; + /* + * "While the GUID Partition Table Header's size may increase + * in the future it cannot span more than one block on the + * device." EFI Specification, version 1.10, 11.2.2.1 + */ + if (PED_LE32_TO_CPU (gpt->HeaderSize) < pth_get_size_static (dev) + || PED_LE32_TO_CPU (gpt->HeaderSize) > dev->sector_size) + return 0; + + /* The SizeOfPartitionEntry must be a multiple of 8 and + no smaller than the size of the PartitionEntry structure. + We also require that it be no larger than 1/16th of UINT32_MAX, + as an additional sanity check. */ + uint32_t pe_size = PED_LE32_TO_CPU (gpt->SizeOfPartitionEntry); + if (pe_size % 8 != 0 + || ! (sizeof (GuidPartitionEntry_t) <= pe_size + && pe_size <= (UINT32_MAX >> 4))) + return 0; + + if (PED_LE64_TO_CPU (gpt->MyLBA) != my_lba) + return 0; + + PedSector alt_lba = PED_LE64_TO_CPU (gpt->AlternateLBA); + /* The backup table's AlternateLBA must be 1. */ + if (my_lba != 1 && alt_lba != 1) + return 0; + + /* The alt_lba must never be the same as my_lba. */ + if (alt_lba == my_lba) + return 0; + + bool crc_match; + if (check_PE_array_CRC (disk, gpt, &crc_match) != 0 || !crc_match) + return 0; + + PedSector first_usable = PED_LE64_TO_CPU (gpt->FirstUsableLBA); + if (first_usable < 3) + return 0; + + PedSector last_usable = PED_LE64_TO_CPU (gpt->LastUsableLBA); + if (last_usable < first_usable) + return 0; + + origcrc = gpt->HeaderCRC32; + gpt->HeaderCRC32 = 0; + if (pth_crc32 (dev, gpt, &crc) != 0) + return 0; + gpt->HeaderCRC32 = origcrc; + + return crc == PED_LE32_TO_CPU (origcrc); +} + +/* Return the number of sectors that should be used by the + * partition entry table. + */ +static PedSector +_ptes_sectors(PedDisk const *disk, GuidPartitionTableHeader_t const *gpt) +{ + size_t ptes_bytes = PED_LE32_TO_CPU (gpt->SizeOfPartitionEntry) * + PED_LE32_TO_CPU (gpt->NumberOfPartitionEntries); + /* Minimum amount of space reserved is 128 128 byte entries */ + if (ptes_bytes < 128*128) + ptes_bytes = 128*128; + return ped_div_round_up (ptes_bytes, disk->dev->sector_size); +} + +/* Return the header's idea of the last sector of the disk + * based on LastUsableLBA and the Partition Entry table. + */ +static PedSector +_hdr_disk_end(PedDisk const *disk, GuidPartitionTableHeader_t const *gpt) +{ + return PED_LE64_TO_CPU (gpt->LastUsableLBA) + 1 + _ptes_sectors(disk, gpt); +} + +static int +_parse_header (PedDisk *disk, const GuidPartitionTableHeader_t *gpt, + int *update_needed) +{ + GPTDiskData *gpt_disk_data = disk->disk_specific; + PedSector first_usable; + PedSector last_usable; + PedSector last_usable_if_grown; + +#ifndef DISCOVER_ONLY + if (PED_LE32_TO_CPU (gpt->Revision) > GPT_HEADER_REVISION_V1_02) + { + if (ped_exception_throw + (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The format of the GPT partition table is version " + "%x, which is newer than what Parted can " + "recognise. Please report this!"), + PED_LE32_TO_CPU (gpt->Revision)) != PED_EXCEPTION_IGNORE) + return 0; + } +#endif + + first_usable = PED_LE64_TO_CPU (gpt->FirstUsableLBA); + last_usable = PED_LE64_TO_CPU (gpt->LastUsableLBA); + + /* Need to check whether the volume has grown, the LastUsableLBA is + normally set to disk->dev->length - 2 - ptes_size (at least for parted + created volumes), where ptes_size is the number of entries * + size of each entry / sector size or 16k / sector size, whatever the greater. + If the volume has grown, offer the user the chance to use the new + space or continue with the current usable area. Only ask once per + parted invocation. */ + + last_usable_if_grown = disk->dev->length - 2 - _ptes_sectors(disk, gpt); + + if (last_usable <= first_usable + || disk->dev->length < last_usable) + return 0; + + if (last_usable_if_grown <= first_usable + || disk->dev->length < last_usable_if_grown) + return 0; + + if (last_usable < last_usable_if_grown) + { + PedExceptionOption q; + + q = ped_exception_throw + (PED_EXCEPTION_WARNING, + PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE, + _("Not all of the space available to %s appears " + "to be used, you can fix the GPT to use all of the " + "space (an extra %llu blocks) or continue with the " + "current setting? "), disk->dev->path, + (uint64_t) (last_usable_if_grown - last_usable)); + + if (q == PED_EXCEPTION_FIX) + { + last_usable = last_usable_if_grown; + /* clear the old backup gpt header */ + ptt_clear_sectors (disk->dev, + gpt_disk_data->AlternateLBA, 1); + gpt_disk_data->AlternateLBA = disk->dev->length - 1; + *update_needed = 1; + } + } + + ped_geometry_init (&gpt_disk_data->data_area, disk->dev, + first_usable, last_usable - first_usable + 1); + + gpt_disk_data->entry_count + = PED_LE32_TO_CPU (gpt->NumberOfPartitionEntries); + PED_ASSERT (gpt_disk_data->entry_count > 0); + PED_ASSERT (gpt_disk_data->entry_count <= 8192); + + gpt_disk_data->uuid = gpt->DiskGUID; + + return 1; +} + +static PedPartition * +_parse_part_entry (PedDisk *disk, GuidPartitionEntry_t *pte) +{ + PedPartition *part; + GPTPartitionData *gpt_part_data; + unsigned int i; + + part = ped_partition_new (disk, PED_PARTITION_NORMAL, NULL, + PED_LE64_TO_CPU (pte->StartingLBA), + PED_LE64_TO_CPU (pte->EndingLBA)); + if (!part) + return NULL; + + gpt_part_data = part->disk_specific; + gpt_part_data->type = pte->PartitionTypeGuid; + gpt_part_data->uuid = pte->UniquePartitionGuid; + for (i = 0; i < 36; i++) + gpt_part_data->name[i] = (efi_char16_t) pte->PartitionName[i]; + gpt_part_data->name[i] = 0; + gpt_part_data->translated_name = 0; + gpt_part_data->attributes = pte->Attributes; + + return part; +} + +/* Read the primary GPT at sector 1 of DEV. + Verify its CRC and that of its partition entry array. + If they are valid, read the backup GPT specified by AlternateLBA. + If not, read the backup GPT in the last sector of the disk. + Return 1 if any read fails. + Upon successful verification of the primary GPT, set *PRIMARY_GPT, else NULL. + Upon successful verification of the backup GPT, set *BACKUP_GPT, else NULL. + If we've set *BACKUP_GPT to non-NULL, set *BACKUP_SECTOR_NUM_P to the sector + number in which it was found. */ +static int +gpt_read_headers (PedDisk const *disk, + GuidPartitionTableHeader_t **primary_gpt, + GuidPartitionTableHeader_t **backup_gpt, + PedSector *backup_sector_num_p) +{ + *primary_gpt = NULL; + *backup_gpt = NULL; + PedDevice const *dev = disk->dev; + GPTDiskData *gpt_disk_data = disk->disk_specific; + LegacyMBR_t *mbr; + + if (!ptt_read_sector (dev, 0, (void *)&mbr)) + return 1; + + if (mbr->PartitionRecord[0].BootIndicator == 0x80) + gpt_disk_data->pmbr_boot = 1; + free (mbr); + + void *s1; + if (!ptt_read_sector (dev, 1, &s1)) + return 1; + + GuidPartitionTableHeader_t *t = pth_new_from_raw (dev, s1); + free (s1); + if (t == NULL) + return 1; + GuidPartitionTableHeader_t *pri = t; + + bool valid_primary = _header_is_valid (disk, pri, 1); + if (valid_primary) + *primary_gpt = pri; + else + pth_free (pri); + + gpt_disk_data->AlternateLBA = + (valid_primary + ? PED_LE64_TO_CPU (pri->AlternateLBA) + : dev->length - 1); + + void *s_bak; + if (!ptt_read_sector (dev, gpt_disk_data->AlternateLBA ,&s_bak)) + return 1; + t = pth_new_from_raw (dev, s_bak); + free (s_bak); + if (t == NULL) + return 1; + + GuidPartitionTableHeader_t *bak = t; + if (_header_is_valid (disk, bak, gpt_disk_data->AlternateLBA)) + { + *backup_gpt = bak; + *backup_sector_num_p = gpt_disk_data->AlternateLBA; + } + else + pth_free (bak); + + return 0; +} + +/************************************************************ + * Intel is changing the EFI Spec. (after v1.02) to say that a + * disk is considered to have a GPT label only if the GPT + * structures are correct, and the MBR is actually a Protective + * MBR (has one 0xEE type partition). + * Problem occurs when a GPT-partitioned disk is then + * edited with a legacy (non-GPT-aware) application, such as + * fdisk (which doesn't generally erase the PGPT or AGPT). + * How should such a disk get handled? As a GPT disk (throwing + * away the fdisk changes), or as an MSDOS disk (throwing away + * the GPT information). Previously, I've taken the GPT-is-right, + * MBR is wrong, approach, to stay consistent with the EFI Spec. + * Intel disagrees, saying the disk should then be treated + * as having a msdos label, not a GPT label. If this is true, + * then what's the point of having an AGPT, since if the PGPT + * is screwed up, likely the PMBR is too, and the PMBR becomes + * a single point of failure. + * So, in the Linux kernel, I'm going to test for PMBR, and + * warn if it's not there, and treat the disk as MSDOS, with a note + * for users to use Parted to "fix up" their disk if they + * really want it to be considered GPT. + ************************************************************/ +static int +gpt_read (PedDisk *disk) +{ + GPTDiskData *gpt_disk_data = disk->disk_specific; + int i; +#ifndef DISCOVER_ONLY + int write_back = 0; +#endif + + ped_disk_delete_all (disk); + + /* motivation: let the user decide about the pmbr... during + ped_disk_probe(), they probably didn't get a choice... */ + if (!gpt_probe (disk->dev)) + goto error; + + GuidPartitionTableHeader_t *gpt = NULL; + GuidPartitionTableHeader_t *primary_gpt; + GuidPartitionTableHeader_t *backup_gpt; + PedSector backup_sector_num; + int read_failure = gpt_read_headers (disk, &primary_gpt, &backup_gpt, + &backup_sector_num); + if (read_failure) + { + /* This includes the case in which there used to be a GPT partition + table here, with an alternate LBA that extended beyond the current + end-of-device. It's treated as a non-match. */ + + /* Another possibility: + The primary header is ok, but backup is corrupt. + In the UEFI spec, this means the primary GUID table + is officially invalid. */ + pth_free (backup_gpt); + pth_free (primary_gpt); + return 0; + } + + if (primary_gpt && backup_gpt) + { + /* Both are valid. */ +#ifndef DISCOVER_ONLY + /* The backup header must be at the end of the disk, or at what the primary + * header thinks is the end of the disk. + */ + gpt_disk_data->AlternateLBA = PED_LE64_TO_CPU (primary_gpt->AlternateLBA); + PedSector pri_disk_end = _hdr_disk_end(disk, primary_gpt); + + if (gpt_disk_data->AlternateLBA != disk->dev->length -1 && + gpt_disk_data->AlternateLBA != pri_disk_end) + { + if (ped_exception_throw + (PED_EXCEPTION_ERROR, + (PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE), + _("The backup GPT table is not at the end of the disk, as it " + "should be. Fix, by moving the backup to the end " + "(and removing the old backup)?")) == PED_EXCEPTION_FIX) + { + ptt_clear_sectors (disk->dev, + PED_LE64_TO_CPU (primary_gpt->AlternateLBA), 1); + gpt_disk_data->AlternateLBA = disk->dev->length -1; + write_back = 1; + } + } +#endif /* !DISCOVER_ONLY */ + pth_free (backup_gpt); + gpt = primary_gpt; + } + else if (!primary_gpt && !backup_gpt) + { + /* Both are corrupt. */ + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Both the primary and backup GPT tables " + "are corrupt. Try making a fresh table, " + "and using Parted's rescue feature to " + "recover partitions.")); + goto error; + } + else if (primary_gpt && !backup_gpt) + { + /* The primary header is ok, but backup is corrupt. */ + if (ped_exception_throw + (PED_EXCEPTION_ERROR, PED_EXCEPTION_OK_CANCEL, + _("The backup GPT table is corrupt, but the " + "primary appears OK, so that will be used.")) + == PED_EXCEPTION_CANCEL) + goto error_free_gpt; + + gpt = primary_gpt; + } + else /* !primary_gpt && backup_gpt */ + { + /* primary GPT corrupt, backup is ok. */ + if (ped_exception_throw + (PED_EXCEPTION_ERROR, PED_EXCEPTION_OK_CANCEL, + _("The primary GPT table is corrupt, but the " + "backup appears OK, so that will be used.")) + == PED_EXCEPTION_CANCEL) + goto error_free_gpt; + + gpt = backup_gpt; + } + backup_gpt = NULL; + primary_gpt = NULL; + + if (!_parse_header (disk, gpt, &write_back)) + goto error_free_gpt; + + size_t ptes_bytes; + void *ptes = gpt_read_PE_array (disk, gpt, &ptes_bytes); + if (ptes == NULL) + goto error_free_gpt; + + uint32_t ptes_crc = efi_crc32 (ptes, ptes_bytes); + if (ptes_crc != PED_LE32_TO_CPU (gpt->PartitionEntryArrayCRC32)) + { + ped_exception_throw + (PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("primary partition table array CRC mismatch")); + goto error_free_ptes; + } + + uint32_t p_ent_size = PED_LE32_TO_CPU (gpt->SizeOfPartitionEntry); + for (i = 0; i < gpt_disk_data->entry_count; i++) + { + GuidPartitionEntry_t *pte + = (GuidPartitionEntry_t *) ((char *) ptes + i * p_ent_size); + PedPartition *part; + + if (!guid_cmp (pte->PartitionTypeGuid, UNUSED_ENTRY_GUID)) + continue; + + part = _parse_part_entry (disk, pte); + if (!part) + goto error_delete_all; + + part->fs_type = ped_file_system_probe (&part->geom); + part->num = i + 1; + + PedConstraint *constraint_exact = ped_constraint_exact (&part->geom); + if (!ped_disk_add_partition (disk, part, constraint_exact)) + { + ped_constraint_destroy (constraint_exact); + ped_partition_destroy (part); + goto error_delete_all; + } + ped_constraint_destroy (constraint_exact); + } + free (ptes); + +#ifndef DISCOVER_ONLY + if (write_back) + ped_disk_commit_to_dev (disk); +#endif + + pth_free (gpt); + return 1; + +error_delete_all: + ped_disk_delete_all (disk); +error_free_ptes: + free (ptes); +error_free_gpt: + pth_free (primary_gpt); + pth_free (backup_gpt); + pth_free (gpt); +error: + return 0; +} + +#ifndef DISCOVER_ONLY +/* Write the protective MBR (to keep DOS happy) */ +static int +_write_pmbr (PedDevice *dev, bool pmbr_boot) +{ + /* The UEFI spec is not clear about what to do with the following + elements of the Protective MBR (pmbr): BootCode (0-440B), + UniqueMBRSignature (440B-444B) and Unknown (444B-446B). + With this in mind, we try not to modify these elements. */ + void *s0; + if (!ptt_read_sector (dev, 0, &s0)) + return 0; + LegacyMBR_t *pmbr = s0; + + /* Zero out the legacy partitions. */ + memset (pmbr->PartitionRecord, 0, sizeof pmbr->PartitionRecord); + + pmbr->Signature = PED_CPU_TO_LE16 (MSDOS_MBR_SIGNATURE); + pmbr->PartitionRecord[0].OSType = EFI_PMBR_OSTYPE_EFI; + pmbr->PartitionRecord[0].StartSector = 2; + pmbr->PartitionRecord[0].EndHead = 0xFF; + pmbr->PartitionRecord[0].EndSector = 0xFF; + pmbr->PartitionRecord[0].EndTrack = 0xFF; + pmbr->PartitionRecord[0].StartingLBA = PED_CPU_TO_LE32 (1); + if ((dev->length - 1ULL) > 0xFFFFFFFFULL) + pmbr->PartitionRecord[0].SizeInLBA = PED_CPU_TO_LE32 (0xFFFFFFFF); + else + pmbr->PartitionRecord[0].SizeInLBA = PED_CPU_TO_LE32 (dev->length - 1UL); + if (pmbr_boot) + pmbr->PartitionRecord[0].BootIndicator = 0x80; + + int write_ok = ped_device_write (dev, pmbr, GPT_PMBR_LBA, + GPT_PMBR_SECTORS); + free (s0); + return write_ok; +} + +static int +_generate_header (const PedDisk *disk, int alternate, uint32_t ptes_crc, + GuidPartitionTableHeader_t **gpt_p) +{ + GPTDiskData *gpt_disk_data = disk->disk_specific; + GuidPartitionTableHeader_t *gpt; + + *gpt_p = pth_new_zeroed (disk->dev); + + gpt = *gpt_p; + + gpt->Signature = PED_CPU_TO_LE64 (GPT_HEADER_SIGNATURE); + gpt->Revision = PED_CPU_TO_LE32 (GPT_HEADER_REVISION_V1_00); + + /* per 1.00 spec */ + gpt->HeaderSize = PED_CPU_TO_LE32 (pth_get_size_static (disk->dev)); + gpt->HeaderCRC32 = 0; + gpt->Reserved1 = 0; + + if (alternate) + { + size_t ss = disk->dev->sector_size; + PedSector ptes_bytes = (gpt_disk_data->entry_count + * sizeof (GuidPartitionEntry_t)); + PedSector ptes_sectors = (ptes_bytes + ss - 1) / ss; + + gpt->MyLBA = PED_CPU_TO_LE64 (gpt_disk_data->AlternateLBA); + gpt->AlternateLBA = PED_CPU_TO_LE64 (1); + gpt->PartitionEntryLBA + = PED_CPU_TO_LE64 (gpt_disk_data->AlternateLBA - ptes_sectors); + } + else + { + gpt->MyLBA = PED_CPU_TO_LE64 (1); + gpt->AlternateLBA = PED_CPU_TO_LE64 (gpt_disk_data->AlternateLBA); + gpt->PartitionEntryLBA = PED_CPU_TO_LE64 (2); + } + + gpt->FirstUsableLBA = PED_CPU_TO_LE64 (gpt_disk_data->data_area.start); + gpt->LastUsableLBA = PED_CPU_TO_LE64 (gpt_disk_data->data_area.end); + gpt->DiskGUID = gpt_disk_data->uuid; + gpt->NumberOfPartitionEntries + = PED_CPU_TO_LE32 (gpt_disk_data->entry_count); + gpt->SizeOfPartitionEntry = PED_CPU_TO_LE32 (sizeof (GuidPartitionEntry_t)); + gpt->PartitionEntryArrayCRC32 = PED_CPU_TO_LE32 (ptes_crc); + + uint32_t crc; + if (pth_crc32 (disk->dev, gpt, &crc) != 0) + return 1; + + gpt->HeaderCRC32 = PED_CPU_TO_LE32 (crc); + return 0; +} + +static void +_partition_generate_part_entry (PedPartition *part, GuidPartitionEntry_t *pte) +{ + GPTPartitionData *gpt_part_data = part->disk_specific; + unsigned int i; + + PED_ASSERT (gpt_part_data != NULL); + + pte->PartitionTypeGuid = gpt_part_data->type; + pte->UniquePartitionGuid = gpt_part_data->uuid; + pte->StartingLBA = PED_CPU_TO_LE64 (part->geom.start); + pte->EndingLBA = PED_CPU_TO_LE64 (part->geom.end); + pte->Attributes = gpt_part_data->attributes; + + for (i = 0; i < 36; i++) + pte->PartitionName[i] = gpt_part_data->name[i]; +} + +static int +gpt_write (const PedDisk *disk) +{ + GPTDiskData *gpt_disk_data; + uint32_t ptes_crc; + uint8_t *pth_raw; + GuidPartitionTableHeader_t *gpt; + PedPartition *part; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + PED_ASSERT (disk->disk_specific != NULL); + + gpt_disk_data = disk->disk_specific; + + size_t ptes_bytes = (gpt_disk_data->entry_count + * sizeof (GuidPartitionEntry_t)); + size_t ss = disk->dev->sector_size; + PedSector ptes_sectors = (ptes_bytes + ss - 1) / ss; + /* Note that we allocate a little more than ptes_bytes, + when that number is not a multiple of sector size. */ + GuidPartitionEntry_t *ptes = calloc (ptes_sectors, ss); + if (!ptes) + goto error; + for (part = ped_disk_next_partition (disk, NULL); part; + part = ped_disk_next_partition (disk, part)) + { + if (part->type != 0) + continue; + _partition_generate_part_entry (part, &ptes[part->num - 1]); + } + + ptes_crc = efi_crc32 (ptes, ptes_bytes); + + /* Write protective MBR */ + if (!_write_pmbr (disk->dev, gpt_disk_data->pmbr_boot)) + goto error_free_ptes; + + /* Write PTH and PTEs */ + /* FIXME: Caution: this code is nearly identical to what's just below. */ + if (_generate_header (disk, 0, ptes_crc, &gpt) != 0) { + pth_free(gpt); + goto error_free_ptes; + } + pth_raw = pth_get_raw (disk->dev, gpt); + pth_free (gpt); + if (pth_raw == NULL) + goto error_free_ptes; + int write_ok = ped_device_write (disk->dev, pth_raw, 1, 1); + free (pth_raw); + if (!write_ok) + goto error_free_ptes; + if (!ped_device_write (disk->dev, ptes, 2, ptes_sectors)) + goto error_free_ptes; + + /* Write Alternate PTH & PTEs */ + /* FIXME: Caution: this code is nearly identical to what's just above. */ + if (_generate_header (disk, 1, ptes_crc, &gpt) != 0) { + pth_free(gpt); + goto error_free_ptes; + } + pth_raw = pth_get_raw (disk->dev, gpt); + pth_free (gpt); + if (pth_raw == NULL) + goto error_free_ptes; + write_ok = ped_device_write (disk->dev, pth_raw, gpt_disk_data->AlternateLBA, 1); + free (pth_raw); + if (!write_ok) + goto error_free_ptes; + if (!ped_device_write (disk->dev, ptes, + gpt_disk_data->AlternateLBA - ptes_sectors, ptes_sectors)) + goto error_free_ptes; + + free (ptes); + return ped_device_sync (disk->dev); + +error_free_ptes: + free (ptes); +error: + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static int +add_metadata_part (PedDisk *disk, PedSector start, PedSector length) +{ + PedPartition *part; + PedConstraint *constraint_exact; + PED_ASSERT (disk != NULL); + + part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, + start, start + length - 1); + if (!part) + goto error; + + constraint_exact = ped_constraint_exact (&part->geom); + if (!ped_disk_add_partition (disk, part, constraint_exact)) + goto error_destroy_constraint; + ped_constraint_destroy (constraint_exact); + return 1; + +error_destroy_constraint: + ped_constraint_destroy (constraint_exact); + ped_partition_destroy (part); +error: + return 0; +} + +static PedPartition * +gpt_partition_new (const PedDisk *disk, + PedPartitionType part_type, + const PedFileSystemType *fs_type, PedSector start, + PedSector end) +{ + PedPartition *part; + GPTPartitionData *gpt_part_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (part_type != 0) + return part; + + gpt_part_data = part->disk_specific = + ped_malloc (sizeof (GPTPartitionData)); + if (!gpt_part_data) + goto error_free_part; + + gpt_part_data->type = PARTITION_LINUX_DATA_GUID; + gpt_part_data->translated_name = 0; + uuid_generate ((unsigned char *) &gpt_part_data->uuid); + swap_uuid_and_efi_guid (&gpt_part_data->uuid); + memset (gpt_part_data->name, 0, sizeof gpt_part_data->name); + memset (&gpt_part_data->attributes, 0, sizeof gpt_part_data->attributes); + return part; + +error_free_part: + _ped_partition_free (part); +error: + return NULL; +} + +static PedPartition * +gpt_partition_duplicate (const PedPartition *part) +{ + PedPartition *result; + GPTPartitionData *part_data = part->disk_specific; + GPTPartitionData *result_data; + + result = _ped_partition_alloc (part->disk, part->type, part->fs_type, + part->geom.start, part->geom.end); + if (!result) + goto error; + result->num = part->num; + + if (result->type != 0) + return result; + + result_data = result->disk_specific = + ped_malloc (sizeof (GPTPartitionData)); + if (!result_data) + goto error_free_part; + + *result_data = *part_data; + if (part_data->translated_name) { + result_data->translated_name = xstrdup (part_data->translated_name); + } else { + result_data->translated_name = 0; + } + return result; + +error_free_part: + _ped_partition_free (result); +error: + return NULL; +} + +static void +gpt_partition_destroy (PedPartition *part) +{ + if (part->type == 0) + { + PED_ASSERT (part->disk_specific != NULL); + GPTPartitionData *gpt_part_data = part->disk_specific; + free (gpt_part_data->translated_name); + free (part->disk_specific); + } + + _ped_partition_free (part); +} + +/* is_skip_guid checks the guid against the list of guids that should not be + * overridden by set_system. It returns a 1 if it is in the list. +*/ +static bool +is_skip_guid(efi_guid_t guid) { + int n = sizeof(skip_set_system_guids) / sizeof(skip_set_system_guids[0]); + for (int i = 0; i < n; ++i) { + if (guid_cmp(guid, skip_set_system_guids[i]) == 0) { + return true; + } + } + + return false; +} + +static int +gpt_partition_set_system (PedPartition *part, + const PedFileSystemType *fs_type) +{ + GPTPartitionData *gpt_part_data = part->disk_specific; + + PED_ASSERT (gpt_part_data != NULL); + + part->fs_type = fs_type; + + // Is this a GUID that should skip fs_type checking? + if (is_skip_guid(gpt_part_data->type)) { + return 1; + } + + if (fs_type) + { + if (strncmp (fs_type->name, "fat", 3) == 0 + || strcmp (fs_type->name, "udf") == 0 + || strcmp (fs_type->name, "ntfs") == 0) + { + gpt_part_data->type = PARTITION_BASIC_DATA_GUID; + return 1; + } + if (strncmp (fs_type->name, "hfs", 3) == 0) + { + gpt_part_data->type = PARTITION_APPLE_HFS_GUID; + return 1; + } + if (strstr (fs_type->name, "swap")) + { + gpt_part_data->type = PARTITION_SWAP_GUID; + return 1; + } + } + + gpt_part_data->type = PARTITION_LINUX_DATA_GUID; + return 1; +} + +/* Allocate metadata partitions for the GPTH and PTES */ +static int +gpt_alloc_metadata (PedDisk *disk) +{ + PedSector gptlength, pteslength = 0; + GPTDiskData *gpt_disk_data; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + PED_ASSERT (disk->disk_specific != NULL); + gpt_disk_data = disk->disk_specific; + + gptlength = ped_div_round_up (sizeof (GuidPartitionTableHeader_t), + disk->dev->sector_size); + pteslength = ped_div_round_up (gpt_disk_data->entry_count + * sizeof (GuidPartitionEntry_t), + disk->dev->sector_size); + + /* metadata at the start of the disk includes the MBR */ + if (!add_metadata_part (disk, GPT_PMBR_LBA, + GPT_PMBR_SECTORS + gptlength + pteslength)) + return 0; + + /* metadata at the end of the disk */ + if (!add_metadata_part (disk, disk->dev->length - gptlength - pteslength, + gptlength + pteslength)) + return 0; + + return 1; +} + +/* Does nothing, as the read/new/destroy functions maintain part->num */ +static int +gpt_partition_enumerate (PedPartition *part) +{ + GPTDiskData *gpt_disk_data = part->disk->disk_specific; + int i; + + /* never change the partition numbers */ + if (part->num != -1) + return 1; + + for (i = 1; i <= gpt_disk_data->entry_count; i++) + { + if (!ped_disk_get_partition (part->disk, i)) + { + part->num = i; + return 1; + } + } + + PED_ASSERT (0); + + return 0; /* used if debug is disabled */ +} + +static int +gpt_disk_set_flag (PedDisk *disk, PedDiskFlag flag, int state) +{ + GPTDiskData *gpt_disk_data = disk->disk_specific; + switch (flag) + { + case PED_DISK_GPT_PMBR_BOOT: + gpt_disk_data->pmbr_boot = state; + return 1; + default: + return 0; + } +} + +static int +gpt_disk_is_flag_available(const PedDisk *disk, PedDiskFlag flag) +{ + switch (flag) + { + case PED_DISK_GPT_PMBR_BOOT: + return 1; + default: + return 0; + } +} + +static uint8_t* +gpt_disk_get_uuid (const PedDisk *disk) +{ + GPTDiskData *gpt_disk_data = disk->disk_specific; + + efi_guid_t uuid = gpt_disk_data->uuid; + + /* uuid is always LE, while uint8_t is always kind of BE */ + + uuid.time_low = PED_SWAP32(uuid.time_low); + uuid.time_mid = PED_SWAP16(uuid.time_mid); + uuid.time_hi_and_version = PED_SWAP16(uuid.time_hi_and_version); + + uint8_t *buf = ped_malloc(sizeof (uuid_t)); + memcpy(buf, &uuid, sizeof (uuid_t)); + return buf; +} + +static int +gpt_disk_get_flag (const PedDisk *disk, PedDiskFlag flag) +{ + GPTDiskData *gpt_disk_data = disk->disk_specific; + switch (flag) + { + case PED_DISK_GPT_PMBR_BOOT: + return gpt_disk_data->pmbr_boot; + break; + default: + return 0; + } +} + +static int +gpt_partition_set_flag (PedPartition *part, PedPartitionFlag flag, int state) +{ + GPTPartitionData *gpt_part_data; + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + gpt_part_data = part->disk_specific; + + const struct flag_uuid_mapping_t* p = gpt_find_flag_uuid_mapping (flag); + if (p) + { + if (state) { + gpt_part_data->type = p->type_uuid; + } else if (guid_cmp (gpt_part_data->type, p->type_uuid) == 0) { + // Clear the GUID so that fs_type will be used to return it to the default + gpt_part_data->type = PARTITION_LINUX_DATA_GUID; + return gpt_partition_set_system (part, part->fs_type); + } + return 1; + } + + switch (flag) + { + case PED_PARTITION_HIDDEN: + gpt_part_data->attributes.RequiredToFunction = state; + return 1; + case PED_PARTITION_LEGACY_BOOT: + gpt_part_data->attributes.LegacyBIOSBootable = state; + return 1; + case PED_PARTITION_NO_AUTOMOUNT: + gpt_part_data->attributes.NoAutomount = state; + return 1; + case PED_PARTITION_ROOT: + case PED_PARTITION_LBA: + default: + return 0; + } + return 1; +} + +static int _GL_ATTRIBUTE_PURE +gpt_partition_get_flag (const PedPartition *part, PedPartitionFlag flag) +{ + GPTPartitionData *gpt_part_data; + PED_ASSERT (part->disk_specific != NULL); + gpt_part_data = part->disk_specific; + + const struct flag_uuid_mapping_t* p = gpt_find_flag_uuid_mapping (flag); + if (p) + return guid_cmp (gpt_part_data->type, p->type_uuid) == 0; + + switch (flag) + { + case PED_PARTITION_HIDDEN: + return gpt_part_data->attributes.RequiredToFunction; + case PED_PARTITION_LEGACY_BOOT: + return gpt_part_data->attributes.LegacyBIOSBootable; + case PED_PARTITION_NO_AUTOMOUNT: + return gpt_part_data->attributes.NoAutomount; + case PED_PARTITION_LBA: + case PED_PARTITION_ROOT: + default: + return 0; + } + return 0; +} + +static int +gpt_partition_is_flag_available (const PedPartition *part, + PedPartitionFlag flag) +{ + if (gpt_find_flag_uuid_mapping (flag)) + return 1; + + switch (flag) + { + case PED_PARTITION_HIDDEN: + case PED_PARTITION_LEGACY_BOOT: + case PED_PARTITION_NO_AUTOMOUNT: + return 1; + case PED_PARTITION_ROOT: + case PED_PARTITION_LBA: + default: + return 0; + } + return 0; +} + +static void +gpt_partition_set_name (PedPartition *part, const char *name) +{ + GPTPartitionData *gpt_part_data = part->disk_specific; + + free(gpt_part_data->translated_name); + gpt_part_data->translated_name = xstrdup(name); + iconv_t conv = iconv_open ("UCS-2LE", nl_langinfo (CODESET)); + if (conv == (iconv_t)-1) + goto err; + char *inbuff = gpt_part_data->translated_name; + char *outbuff = (char *)&gpt_part_data->name; + size_t inbuffsize = strlen (inbuff) + 1; + size_t outbuffsize = 72; + if (iconv (conv, &inbuff, &inbuffsize, &outbuff, &outbuffsize) == -1) + goto err; + iconv_close (conv); + return; + err: + ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("failed to translate partition name")); + iconv_close (conv); +} + +static const char * +gpt_partition_get_name (const PedPartition *part) +{ + GPTPartitionData *gpt_part_data = part->disk_specific; + if (gpt_part_data->translated_name == NULL) + { + char buffer[200]; + iconv_t conv = iconv_open (nl_langinfo (CODESET), "UCS-2LE"); + if (conv == (iconv_t)-1) + goto err; + char *inbuff = (char *)&gpt_part_data->name; + char *outbuff = buffer; + size_t inbuffsize = 72; + size_t outbuffsize = sizeof(buffer); + if (iconv (conv, &inbuff, &inbuffsize, &outbuff, &outbuffsize) == -1) + goto err; + iconv_close (conv); + *outbuff = 0; + gpt_part_data->translated_name = xstrdup (buffer); + return gpt_part_data->translated_name; + err: + ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("failed to translate partition name")); + iconv_close (conv); + return ""; + } + return gpt_part_data->translated_name; +} + + +static int +gpt_partition_set_type_uuid (PedPartition *part, const uint8_t *uuid) +{ + GPTPartitionData *gpt_part_data = part->disk_specific; + + efi_guid_t* type_uuid = &gpt_part_data->type; + memcpy(type_uuid, uuid, sizeof (efi_guid_t)); + + /* type_uuid is always LE, while uint8_t is always kind of BE */ + + type_uuid->time_low = PED_SWAP32(type_uuid->time_low); + type_uuid->time_mid = PED_SWAP16(type_uuid->time_mid); + type_uuid->time_hi_and_version = PED_SWAP16(type_uuid->time_hi_and_version); + + return 1; +} + + +static uint8_t* +gpt_partition_get_type_uuid (const PedPartition *part) +{ + const GPTPartitionData *gpt_part_data = part->disk_specific; + + efi_guid_t type_uuid = gpt_part_data->type; + + /* type_uuid is always LE, while uint8_t is always kind of BE */ + + type_uuid.time_low = PED_SWAP32(type_uuid.time_low); + type_uuid.time_mid = PED_SWAP16(type_uuid.time_mid); + type_uuid.time_hi_and_version = PED_SWAP16(type_uuid.time_hi_and_version); + + uint8_t *buf = ped_malloc(sizeof (uuid_t)); + memcpy(buf, &type_uuid, sizeof (uuid_t)); + return buf; +} + +static uint8_t* +gpt_partition_get_uuid (const PedPartition *part) +{ + const GPTPartitionData *gpt_part_data = part->disk_specific; + + efi_guid_t uuid = gpt_part_data->uuid; + + /* uuid is always LE, while uint8_t is always kind of BE */ + + uuid.time_low = PED_SWAP32(uuid.time_low); + uuid.time_mid = PED_SWAP16(uuid.time_mid); + uuid.time_hi_and_version = PED_SWAP16(uuid.time_hi_and_version); + + uint8_t *buf = ped_malloc(sizeof (uuid_t)); + memcpy(buf, &uuid, sizeof (uuid_t)); + return buf; +} + +static int +gpt_get_max_primary_partition_count (const PedDisk *disk) +{ + const GPTDiskData *gpt_disk_data = disk->disk_specific; + return gpt_disk_data->entry_count; +} + +/* + * From (http://developer.apple.com/technotes/tn2006/tn2166.html Chapter 5). + * According to the specs the first LBA (LBA0) is not relevant (it exists + * to maintain compatibility). on the second LBA(LBA1) gpt places the + * header. The header is as big as the block size. After the header we + * find the Entry array. Each element of said array, describes each + * partition. One can have as much elements as can fit between the end of + * the second LBA (where the header ends) and the FirstUsableLBA. + * FirstUsableLBA is the first logical block that is used for contents + * and is defined in header. + * + * /---------------------------------------------------\ + * | BLOCK0 | HEADER | Entry Array | First Usable LBA | + * | | BLOCK1 | | | + * \---------------------------------------------------/ + * / \ + * /----------/ \----------\ + * /-----------------------------------------\ + * | E1 | E2 | E3 |...............| EN | + * \-----------------------------------------/ + * + * The number of possible partitions or supported partitions is: + * SP = FirstUsableLBA*Blocksize - 2*Blocksize / SizeOfPartitionEntry + * SP = Blocksize(FirstusableLBA - 2) / SizeOfPartitoinEntry + */ +static bool +gpt_get_max_supported_partition_count (const PedDisk *disk, int *max_n) +{ + GuidPartitionTableHeader_t *pth = NULL; + uint8_t *pth_raw = ped_malloc (pth_get_size (disk->dev)); + + if (ped_device_read (disk->dev, pth_raw, 1, GPT_HEADER_SECTORS) + || ped_device_read (disk->dev, pth_raw, + disk->dev->length, GPT_HEADER_SECTORS)) + pth = pth_new_from_raw (disk->dev, pth_raw); + free (pth_raw); + + if (pth == NULL) + return false; + + if (!_header_is_valid (disk, pth, 1)) + { + pth->FirstUsableLBA = PED_CPU_TO_LE64 (34); + pth->SizeOfPartitionEntry + = PED_CPU_TO_LE32 (sizeof (GuidPartitionEntry_t)); + } + + *max_n = (disk->dev->sector_size * (PED_LE64_TO_CPU (pth->FirstUsableLBA) - 2) + / PED_LE32_TO_CPU (pth->SizeOfPartitionEntry)); + pth_free (pth); + return true; +} + +static PedConstraint * +_non_metadata_constraint (const PedDisk *disk) +{ + GPTDiskData *gpt_disk_data = disk->disk_specific; + + return ped_constraint_new_from_max (&gpt_disk_data->data_area); +} + +static int +gpt_partition_align (PedPartition *part, const PedConstraint *constraint) +{ + PED_ASSERT (part != NULL); + + if (_ped_partition_attempt_align (part, constraint, + _non_metadata_constraint (part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +#include "pt-common.h" +PT_define_limit_functions (gpt) + +static PedDiskOps gpt_disk_ops = +{ + clobber: NULL, + write: NULL_IF_DISCOVER_ONLY (gpt_write), + + partition_set_name: gpt_partition_set_name, + partition_get_name: gpt_partition_get_name, + partition_set_type_id: NULL, + partition_get_type_id: NULL, + partition_set_type_uuid: gpt_partition_set_type_uuid, + partition_get_type_uuid: gpt_partition_get_type_uuid, + partition_get_uuid: gpt_partition_get_uuid, + disk_set_flag: gpt_disk_set_flag, + disk_get_flag: gpt_disk_get_flag, + disk_is_flag_available: gpt_disk_is_flag_available, + disk_get_uuid: gpt_disk_get_uuid, + + PT_op_function_initializers (gpt) +}; + +static PedDiskType gpt_disk_type = +{ + next: NULL, + name: "gpt", + ops: &gpt_disk_ops, + features: PED_DISK_TYPE_PARTITION_NAME | PED_DISK_TYPE_PARTITION_TYPE_UUID | + PED_DISK_TYPE_DISK_UUID | PED_DISK_TYPE_PARTITION_UUID +}; + +void +ped_disk_gpt_init () +{ + ped_disk_type_register (&gpt_disk_type); +} + +void +ped_disk_gpt_done () +{ + ped_disk_type_unregister (&gpt_disk_type); +} + +verify (sizeof (GuidPartitionEntryAttributes_t) == 8); +verify (sizeof (GuidPartitionEntry_t) == 128); diff --git a/libparted/labels/loop.c b/libparted/labels/loop.c new file mode 100644 index 0000000..45d169d --- /dev/null +++ b/libparted/labels/loop.c @@ -0,0 +1,316 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2000, 2007-2014, 2019-2023 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/>. +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <stdbool.h> + +#include "pt-tools.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#define LOOP_SIGNATURE "GNU Parted Loopback 0" + +static PedDiskType loop_disk_type; + +static PedDisk* loop_alloc (const PedDevice* dev); +static void loop_free (PedDisk* disk); + +static int +loop_probe (const PedDevice* dev) +{ + PedDisk *disk = loop_alloc (dev); + if (!disk) + goto error; + + void *buf; + if (!ptt_read_sector (dev, 0, &buf)) + goto error_destroy_disk; + int found_sig = !strncmp (buf, LOOP_SIGNATURE, strlen (LOOP_SIGNATURE)); + free (buf); + + int result; + if (found_sig) { + result = 1; + } else { + PedGeometry* geom; + + geom = ped_geometry_new (dev, 0, disk->dev->length); + if (!geom) + goto error_destroy_disk; + result = ped_file_system_probe (geom) != NULL; + ped_geometry_destroy (geom); + } + loop_free (disk); + return result; + +error_destroy_disk: + loop_free (disk); +error: + return 0; +} + +static PedDisk* +loop_alloc (const PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + + if (dev->length < 256) + return NULL; + PedDisk *disk = _ped_disk_alloc ((PedDevice*)dev, &loop_disk_type); + PED_ASSERT (disk != NULL); + PedGeometry *geom = ped_geometry_new (dev, 0, dev->length); + PED_ASSERT (geom != NULL); + PedPartition *part = ped_partition_new (disk, PED_PARTITION_NORMAL, + NULL, geom->start, geom->end); + PED_ASSERT (part != NULL); + ped_geometry_destroy (geom); + PedConstraint *constraint_any = ped_constraint_any (dev); + if (!ped_disk_add_partition (disk, part, constraint_any)) + goto error; + ped_constraint_destroy (constraint_any); + return disk; + error: + ped_constraint_destroy (constraint_any); + ped_disk_destroy (disk); + return NULL; +} + +static PedDisk* +loop_duplicate (const PedDisk* disk) +{ + return ped_disk_new_fresh (disk->dev, &loop_disk_type); +} + +static void +loop_free (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + + _ped_disk_free (disk); +} + +static int +loop_read (PedDisk* disk) +{ + PedDevice* dev = NULL; + PedGeometry* geom; + PedFileSystemType* fs_type; + PedPartition* part; + PedConstraint* constraint_any; + + PED_ASSERT (disk != NULL); + dev = disk->dev; + constraint_any = ped_constraint_any (dev); + + ped_disk_delete_all (disk); + + void *buf; + if (!ptt_read_sector (dev, 0, &buf)) + goto error; + + int found_sig = !strncmp (buf, LOOP_SIGNATURE, strlen (LOOP_SIGNATURE)); + free (buf); + geom = ped_geometry_new (dev, 0, dev->length); + if (!geom) + goto error; + + fs_type = ped_file_system_probe (geom); + if (!fs_type && !found_sig) + goto error_free_geom; + + part = ped_partition_new (disk, PED_PARTITION_NORMAL, + fs_type, geom->start, geom->end); + ped_geometry_destroy (geom); + if (!part) + goto error; + + if (!ped_disk_add_partition (disk, part, constraint_any)) + goto error; + ped_constraint_destroy (constraint_any); + return 1; + +error_free_geom: + ped_geometry_destroy (geom); +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +#ifndef DISCOVER_ONLY +static int +loop_write (const PedDisk* disk) +{ + size_t buflen = disk->dev->sector_size; + char *buf = alloca (buflen); + PedPartition *part = ped_disk_get_partition (disk, 1); + /* if there is already a filesystem on the disk, we don't need to write the signature */ + if (part && part->fs_type) + return 1; + if (!ped_device_read (disk->dev, buf, 0, 1)) + return 0; + strcpy (buf, LOOP_SIGNATURE); + + return ped_device_write (disk->dev, buf, 0, 1); +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +loop_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + return NULL; + part->disk_specific = NULL; + return part; +} + +static PedPartition* +loop_partition_duplicate (const PedPartition* part) +{ + PedPartition* result; + + result = ped_partition_new (part->disk, part->type, part->fs_type, + part->geom.start, part->geom.end); + if (result == NULL) + return NULL; + result->num = part->num; + return result; +} + +static void +loop_partition_destroy (PedPartition* part) +{ + free (part); +} + +static int +loop_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + part->fs_type = fs_type; + return 1; +} + +static int +loop_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + return 0; +} + +static int +loop_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + return 0; +} + +static int +loop_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PedGeometry* new_geom; + + new_geom = ped_constraint_solve_nearest (constraint, &part->geom); + if (!new_geom) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the " + "partition.")); + return 0; + } + ped_geometry_set (&part->geom, new_geom->start, new_geom->length); + ped_geometry_destroy (new_geom); + return 1; +} + +static int +loop_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + return 0; +} + +static int +loop_partition_enumerate (PedPartition* part) +{ + part->num = 1; + return 1; +} + +static int +loop_alloc_metadata (PedDisk* disk) +{ + return 1; +} + +static int +loop_get_max_primary_partition_count (const PedDisk* disk) +{ + return 1; +} + +static bool +loop_get_max_supported_partition_count (const PedDisk* disk, int *max_n) +{ + *max_n = 1; + return true; +} + +#include "pt-common.h" +PT_define_limit_functions (loop) + +static PedDiskOps loop_disk_ops = { + clobber: NULL, + write: NULL_IF_DISCOVER_ONLY (loop_write), + + partition_set_name: NULL, + partition_get_name: NULL, + + PT_op_function_initializers (loop) +}; + +static PedDiskType loop_disk_type = { + next: NULL, + name: "loop", + ops: &loop_disk_ops, + features: 0 +}; + +void +ped_disk_loop_init () +{ + ped_disk_type_register (&loop_disk_type); +} + +void +ped_disk_loop_done () +{ + ped_disk_type_unregister (&loop_disk_type); +} diff --git a/libparted/labels/mac.c b/libparted/labels/mac.c new file mode 100644 index 0000000..6711d87 --- /dev/null +++ b/libparted/labels/mac.c @@ -0,0 +1,1604 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2002, 2004, 2007-2014, 2019-2023 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/>. +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <stdbool.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "misc.h" +#include "pt-tools.h" + +/* struct's hacked from Linux source: fs/partitions/mac.h + * I believe it was originally written by Paul Mackerras (from comments in + * Quik source) + * + * See also: + * http://developer.apple.com/documentation/mac/Devices/Devices-126.html + * http://developer.apple.com/documentation/mac/Devices/Devices-121.html + * http://devworld.apple.com/technotes/tn/tn1189.html + * + * Partition types: + * Apple_Bootstrap new-world (HFS) boot partition + * Apple_partition_map partition map (table) + * Apple_Driver device driver + * Apple_Driver43 SCSI Manager 4.3 device driver + * Apple_MFS original Macintosh File System + * Apple_HFS Hierarchical File System (and +) + * Apple_HFSX HFS+ with case sensitivity and more + * Apple_UNIX_SVR2 UNIX file system (UFS?) + * Apple_PRODOS ProDOS file system + * Apple_Free unused space + * Apple_Scratch empty + * Apple_Void padding for iso9660 + * Apple_Extra an unused partition map entry + * + * Quick explanation: + * ------------------ + * Terminology: + * + * Parted Apple + * ------ ----- + * device disk/device + * disk no equivalent. + * partition volume or partition + * sector block + * + * * All space must be accounted for, except block 0 (driver block) and + * block 1-X (the partition map: i.e. lots of MacRawPartitions) + * + * * It's really hard to grow/shrink the number of MacRawPartition + * entries in the partition map, because the first partition starts + * immediately after the partition map. When we can move the start of + * HFS and ext2 partitions, this problem will disappear ;-) + */ + +#define MAC_PARTITION_MAGIC_1 0x5453 /* old */ +#define MAC_PARTITION_MAGIC_2 0x504d +#define MAC_DISK_MAGIC 0x4552 + +#define MAC_STATUS_BOOTABLE 8 /* partition is bootable */ + +typedef struct _MacRawPartition MacRawPartition; +typedef struct _MacRawDisk MacRawDisk; +typedef struct _MacDeviceDriver MacDeviceDriver; +typedef struct _MacPartitionData MacPartitionData; +typedef struct _MacDiskData MacDiskData; + +struct __attribute__ ((packed)) _MacRawPartition { + uint16_t signature; /* expected to be MAC_PARTITION_MAGIC */ + uint16_t res1; + 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; + uint32_t boot_count; + uint32_t boot_load; + uint32_t boot_load2; + uint32_t boot_entry; + uint32_t boot_entry2; + uint32_t boot_cksum; + char processor[16]; /* Contains 680x0, x=0,2,3,4; or empty */ + uint32_t driver_sig; + char _padding[372]; +}; + +/* Driver descriptor structure, in block 0 */ +struct __attribute__ ((packed)) _MacRawDisk { + uint16_t signature; /* expected to be MAC_DRIVER_MAGIC */ + uint16_t block_size; /* physical sector size */ + uint32_t block_count; /* size of device in blocks */ + uint16_t dev_type; /* reserved */ + uint16_t dev_id; /* reserved */ + uint32_t data; /* reserved */ + uint16_t driver_count; /* # of driver descriptor entries */ + uint8_t driverlist[488];/* info about available drivers */ + uint16_t padding[3]; /* pad to 512 bytes */ +}; + +struct __attribute__ ((packed)) _MacDeviceDriver { + uint32_t block; /* startblock in MacRawDisk->block_size units */ + uint16_t size; /* size in 512 byte units */ + uint16_t type; /* operating system type (MacOS = 1) */ +}; + +struct _MacPartitionData { + char volume_name[33]; /* eg: "Games" */ + char system_name[33]; /* eg: "Apple_Unix_SVR2" */ + char processor_name[17]; + + int is_boot; + int is_driver; + int has_driver; + int is_root; + int is_swap; + int is_lvm; + int is_raid; + + PedSector data_region_length; + PedSector boot_region_length; + + uint32_t boot_base_address; + uint32_t boot_entry_address; + uint32_t boot_checksum; + + uint32_t status; + uint32_t driver_sig; +}; + +struct _MacDiskData { + int ghost_size; /* sectors per "driver" block */ + int part_map_entry_count; /* # entries (incl. ghost) */ + int part_map_entry_num; /* partition map location */ + + int active_part_entry_count; /* # real partitions */ + int free_part_entry_count; /* # free space */ + int last_part_entry_num; /* last entry number */ + + uint16_t block_size; /* physical sector size */ + uint16_t driver_count; + MacDeviceDriver driverlist[1 + 60]; /* 488 bytes */ +}; + +static PedDiskType mac_disk_type; + +static int +_check_signature (MacRawDisk const *raw_disk) +{ + if (PED_BE16_TO_CPU (raw_disk->signature) != MAC_DISK_MAGIC) { +#ifdef DISCOVER_ONLY + return 0; +#else + return ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Invalid signature %x for Mac disk labels."), + (int) PED_BE16_TO_CPU (raw_disk->signature)) + == PED_EXCEPTION_IGNORE; +#endif + } + + return 1; +} + +static int +_rawpart_check_signature (MacRawPartition* raw_part) +{ + int sig = (int) PED_BE16_TO_CPU (raw_part->signature); + return sig == MAC_PARTITION_MAGIC_1 || sig == MAC_PARTITION_MAGIC_2; +} + +static int +mac_probe (const PedDevice * dev) +{ + PED_ASSERT (dev != NULL); + + if (dev->sector_size < sizeof (MacRawDisk)) + return 0; + + void *label; + if (!ptt_read_sector (dev, 0, &label)) + return 0; + + int valid = _check_signature (label); + + free (label); + return valid; +} + +static int +_disk_add_part_map_entry (PedDisk* disk, int warn) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + PedPartition* new_part; + MacPartitionData* mac_part_data; + PedSector part_map_size; + PedConstraint* constraint_any = ped_constraint_any (disk->dev); + +#ifndef DISCOVER_ONLY + if (warn && ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL, + _("Partition map has no partition map entry!")) + != PED_EXCEPTION_FIX) + goto error; +#endif /* !DISCOVER_ONLY */ + + part_map_size + = ped_round_up_to (mac_disk_data->last_part_entry_num, 64); + if (part_map_size == 0) + part_map_size = 64; + + new_part = ped_partition_new (disk, PED_PARTITION_NORMAL, NULL, + 1, part_map_size - 1); + if (!new_part) + goto error; + + mac_part_data = new_part->disk_specific; + strcpy (mac_part_data->volume_name, "Apple"); + strcpy (mac_part_data->system_name, "Apple_partition_map"); + + if (!ped_disk_add_partition (disk, new_part, constraint_any)) + goto error_destroy_new_part; + + mac_disk_data->part_map_entry_num = new_part->num; + mac_disk_data->part_map_entry_count + = new_part->geom.end - mac_disk_data->ghost_size; + ped_constraint_destroy (constraint_any); + return 1; + +error_destroy_new_part: + ped_partition_destroy (new_part); +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +static PedDisk* +mac_alloc (const PedDevice* dev) +{ + PedDisk* disk; + MacDiskData* mac_disk_data; + + PED_ASSERT (dev != NULL); + +#ifndef DISCOVER_ONLY + if (dev->length < 256) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s is too small for a Mac disk label!"), + dev->path); + goto error; + } +#endif + + disk = _ped_disk_alloc (dev, &mac_disk_type); + if (!disk) + goto error; + + mac_disk_data = (MacDiskData*) ped_malloc (sizeof (MacDiskData)); + if (!mac_disk_data) + goto error_free_disk; + disk->disk_specific = mac_disk_data; + mac_disk_data->ghost_size = 1; + mac_disk_data->active_part_entry_count = 0; + mac_disk_data->free_part_entry_count = 1; + mac_disk_data->last_part_entry_num = 1; + mac_disk_data->block_size = 0; + mac_disk_data->driver_count = 0; + memset(&mac_disk_data->driverlist[0], 0, sizeof(mac_disk_data->driverlist)); + + if (!_disk_add_part_map_entry (disk, 0)) + goto error_free_disk; + return disk; + +error_free_disk: + _ped_disk_free (disk); +error: + return NULL; +} + +static PedDisk* +mac_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + MacDiskData* new_mac_data; + MacDiskData* old_mac_data = (MacDiskData*) disk->disk_specific; + + new_disk = ped_disk_new_fresh (disk->dev, &mac_disk_type); + if (!new_disk) + return NULL; + + new_mac_data = (MacDiskData*) new_disk->disk_specific; + + /* remove the partition map partition - it will be duplicated + * later. + */ + PedSector first_part_map_sector = old_mac_data->ghost_size; + PedPartition *partition_map + = ped_disk_get_partition_by_sector (new_disk, first_part_map_sector); + PED_ASSERT (partition_map != NULL); + + /* ped_disk_remove_partition may be used only to delete a "normal" + partition. Trying to delete at least "freespace" or "metadata" + partitions leads to a violation of assumptions in + ped_disk_remove_partition, since it calls _disk_push_update_mode, + which destroys all "freespace" and "metadata" partitions, and + depends on that destruction not freeing its PART parameter. */ + PED_ASSERT (partition_map->type == PED_PARTITION_NORMAL); + ped_disk_remove_partition (new_disk, partition_map); + + /* ugly, but C is ugly :p */ + memcpy (new_mac_data, old_mac_data, sizeof (MacDiskData)); + return new_disk; +} + +static void +mac_free (PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + + _ped_disk_free (disk); + free (mac_disk_data); +} + +static int +_rawpart_cmp_type (const MacRawPartition* raw_part, const char* type) +{ + return strncasecmp (raw_part->type, type, 32) == 0; +} + +static int +_rawpart_cmp_name (const MacRawPartition* raw_part, const char* name) +{ + return strncasecmp (raw_part->name, name, 32) == 0; +} + +static int +_rawpart_is_partition_map (const MacRawPartition* raw_part) +{ + return _rawpart_cmp_type (raw_part, "Apple_partition_map"); +} + +static int +strncasestr (const char* haystack, const char* needle, int n) +{ + int needle_size = strlen (needle); + int i; + + for (i = 0; haystack[i] && i < n - needle_size; i++) { + if (strncasecmp (haystack + i, needle, needle_size) == 0) + return 1; + } + + return 0; +} + +static int +_rawpart_is_boot (const MacRawPartition* raw_part) +{ + if (!strcasecmp(raw_part->type, "Apple_Bootstrap")) + return 1; + + if (!strcasecmp(raw_part->type, "Apple_Boot")) + return 1; + + return 0; +} + +static int +_rawpart_is_driver (const MacRawPartition* raw_part) +{ + if (strncmp (raw_part->type, "Apple_", 6) != 0) + return 0; + if (!strncasestr (raw_part->type, "driver", 32)) + return 0; + return 1; +} + +static int _GL_ATTRIBUTE_PURE +_rawpart_has_driver (const MacRawPartition* raw_part, MacDiskData* mac_disk_data) +{ + MacDeviceDriver *driverlist; + uint16_t i; + uint32_t start_block, block_count; + + start_block = PED_BE32_TO_CPU(raw_part->start_block); + block_count = PED_BE32_TO_CPU(raw_part->block_count); + driverlist = &mac_disk_data->driverlist[0]; + for (i = 0; i < mac_disk_data->driver_count; i++) { + if (start_block == PED_BE32_TO_CPU(driverlist->block) && + block_count == PED_BE16_TO_CPU(driverlist->size)) + return 1; + driverlist++; + } + return 0; +} + +static int +_rawpart_is_root (MacRawPartition* raw_part) +{ + if (!_rawpart_cmp_type (raw_part, "Apple_UNIX_SVR2")) + return 0; + if (strcmp (raw_part->name, "root") != 0) + return 0; + return 1; +} + +static int +_rawpart_is_swap (MacRawPartition* raw_part) +{ + if (!_rawpart_cmp_type (raw_part, "Apple_UNIX_SVR2")) + return 0; + if (strcmp (raw_part->name, "swap") != 0) + return 0; + return 1; +} + +static int +_rawpart_is_lvm (MacRawPartition* raw_part) +{ + if (strcmp (raw_part->type, "Linux_LVM") != 0) + return 0; + return 1; +} + +static int +_rawpart_is_raid (MacRawPartition* raw_part) +{ + if (strcmp (raw_part->type, "Linux_RAID") != 0) + return 0; + return 1; +} + +static int +_rawpart_is_void (MacRawPartition* raw_part) +{ + return _rawpart_cmp_type (raw_part, "Apple_Void"); +} + +/* returns 1 if the raw_part represents a partition that is "unused space", or + * doesn't represent a partition at all. NOTE: some people make Apple_Free + * partitions with MacOS, because they can't select another type. So, if the + * name is anything other than "Extra" or "", it is treated as a "real" + * partition. + */ +static int +_rawpart_is_active (MacRawPartition* raw_part) +{ + if (_rawpart_cmp_type (raw_part, "Apple_Free") + && (_rawpart_cmp_name (raw_part, "Extra") + || _rawpart_cmp_name (raw_part, ""))) + return 0; + if (_rawpart_cmp_type (raw_part, "Apple_Void")) + return 0; + if (_rawpart_cmp_type (raw_part, "Apple_Scratch")) + return 0; + if (_rawpart_cmp_type (raw_part, "Apple_Extra")) + return 0; + + return 1; +} + +static PedPartition* +_rawpart_analyse (MacRawPartition* raw_part, PedDisk* disk, int num) +{ + MacDiskData* mac_disk_data; + PedPartition* part; + MacPartitionData* mac_part_data; + PedSector start, length; + + if (!_rawpart_check_signature (raw_part)) { +#ifndef DISCOVER_ONLY + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Partition %d has an invalid signature %x."), + num, + (int) PED_BE16_TO_CPU (raw_part->signature)) + != PED_EXCEPTION_IGNORE) +#endif + goto error; + } + + mac_disk_data = (MacDiskData*) disk->disk_specific; + + start = PED_BE32_TO_CPU (raw_part->start_block); + length = PED_BE32_TO_CPU (raw_part->block_count); + if (length == 0) { +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Partition %d has an invalid length of 0 bytes!"), + num); +#endif + return NULL; + } + part = ped_partition_new (disk, PED_PARTITION_NORMAL, NULL, + start, start + length - 1); + if (!part) + goto error; + + mac_part_data = part->disk_specific; + + strncpy (mac_part_data->volume_name, raw_part->name, 32); + strncpy (mac_part_data->system_name, raw_part->type, 32); + strncpy (mac_part_data->processor_name, raw_part->processor, 16); + + mac_part_data->is_boot = _rawpart_is_boot (raw_part); + mac_part_data->is_driver = _rawpart_is_driver (raw_part); + if (mac_part_data->is_driver) + mac_part_data->has_driver = _rawpart_has_driver(raw_part, mac_disk_data); + mac_part_data->is_root = _rawpart_is_root (raw_part); + mac_part_data->is_swap = _rawpart_is_swap (raw_part); + mac_part_data->is_lvm = _rawpart_is_lvm (raw_part); + mac_part_data->is_raid = _rawpart_is_raid (raw_part); + + /* "data" region */ +#ifndef DISCOVER_ONLY + if (raw_part->data_start) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The data region doesn't start at the start " + "of the partition.")); + goto error_destroy_part; + } +#endif /* !DISCOVER_ONLY */ + mac_part_data->data_region_length + = PED_BE32_TO_CPU (raw_part->data_count); + + mac_part_data->boot_region_length + = PED_BE32_TO_CPU (raw_part->boot_count); + +#ifndef DISCOVER_ONLY + if (mac_part_data->has_driver) { + if (mac_part_data->boot_region_length < part->geom.length) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("The partition's boot region doesn't occupy " + "the entire partition.")) + != PED_EXCEPTION_IGNORE) + goto error_destroy_part; + } + } else { + if (mac_part_data->data_region_length < part->geom.length && + !mac_part_data->is_boot) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("The partition's data region doesn't occupy " + "the entire partition.")) + != PED_EXCEPTION_IGNORE) + goto error_destroy_part; + } + } +#endif /* !DISCOVER_ONLY */ + + mac_part_data->boot_base_address + = PED_BE32_TO_CPU (raw_part->boot_load); + mac_part_data->boot_entry_address + = PED_BE32_TO_CPU (raw_part->boot_entry); + mac_part_data->boot_checksum + = PED_BE32_TO_CPU (raw_part->boot_cksum); + + mac_part_data->status = PED_BE32_TO_CPU (raw_part->status); + mac_part_data->driver_sig = PED_BE32_TO_CPU (raw_part->driver_sig); + + return part; + +error_destroy_part: + ped_partition_destroy (part); +error: + return NULL; +} + +/* looks at the partition map size field in a mac raw partition, and calculates + * what the size of the partition map should be, from it + */ +static int +_rawpart_get_partmap_size (MacRawPartition* raw_part, PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + PedSector part_map_start; + PedSector part_map_end; + + part_map_start = mac_disk_data->ghost_size; + part_map_end = PED_BE32_TO_CPU (raw_part->map_count); + + return part_map_end - part_map_start + 1; +} + +static int +_disk_analyse_block_size (PedDisk* disk, MacRawDisk* raw_disk) +{ + PedSector block_size; + + if (PED_BE16_TO_CPU (raw_disk->block_size) % 512) { +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Weird block size on device descriptor: %d bytes is " + "not divisible by 512."), + (int) PED_BE16_TO_CPU (raw_disk->block_size)); +#endif + goto error; + } + + block_size = PED_BE16_TO_CPU (raw_disk->block_size) / 512; + if (block_size != disk->dev->sector_size / 512) { +#ifndef DISCOVER_ONLY + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The driver descriptor says the physical block size " + "is %d bytes, but Linux says it is %d bytes."), + (int) block_size * 512, + (int) disk->dev->sector_size) + != PED_EXCEPTION_IGNORE) + goto error; +#endif + disk->dev->sector_size = block_size * 512; + } + + return 1; + +error: + return 0; +} + +/* Tries to figure out the block size used by the drivers, for the ghost + * partitioning scheme. Ghost partitioning works like this: the OpenFirmware + * (OF) sees 512 byte blocks, but some drivers use 2048 byte blocks (and, + * perhaps, some other number?). To remain compatible, the partition map + * only has "real" partition map entries on ghost-aligned block numbers (and + * the others are padded with Apple_Void partitions). This function tries + * to figure out what the "ghost-aligned" size is... (which, believe-it-or-not, + * doesn't always equal 2048!!!) + */ +static int +_disk_analyse_ghost_size (PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + + void *buf = ped_malloc (disk->dev->sector_size); + if (!buf) + return 0; + + int i; + int found = 0; + for (i = 1; i < 64; i *= 2) { + if (!ped_device_read (disk->dev, buf, i, 1)) + break; + if (_rawpart_check_signature (buf) + && !_rawpart_is_void (buf)) { + mac_disk_data->ghost_size = i; + found = (i <= disk->dev->sector_size / 512); + break; + } + } + free (buf); + +#ifndef DISCOVER_ONLY + if (!found) + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("No valid partition map found.")); +#endif + return found; +} + +static int +mac_read (PedDisk* disk) +{ + MacDiskData* mac_disk_data; + PedPartition* part; + int num; + PedSector ghost_size; + int last_part_entry_num = 0; + + PED_ASSERT (disk != NULL); + + mac_disk_data = disk->disk_specific; + mac_disk_data->part_map_entry_num = 0; /* 0 == none */ + + void *buf; + if (!ptt_read_sector (disk->dev, 0, &buf)) + return 0; + + MacRawDisk *raw_disk = buf; + + if (!_check_signature (raw_disk)) + goto error; + + /* Record the original sector size; this function may change it. */ + PedSector ss0 = disk->dev->sector_size; + if (!_disk_analyse_block_size (disk, raw_disk)) + goto error; + + if (!_disk_analyse_ghost_size (disk)) + goto error; + ghost_size = mac_disk_data->ghost_size; + + if (!ped_disk_delete_all (disk)) + goto error; + + if (PED_BE16_TO_CPU(raw_disk->driver_count) && + PED_BE16_TO_CPU(raw_disk->driver_count) < 62) { + memcpy(&mac_disk_data->driverlist[0], &raw_disk->driverlist[0], + sizeof(mac_disk_data->driverlist)); + mac_disk_data->driver_count = PED_BE16_TO_CPU(raw_disk->driver_count); + mac_disk_data->block_size = PED_BE16_TO_CPU(raw_disk->block_size); + } + + /* If _disk_analyse_block_size has increased the sector_size, + reallocate this buffer, so we can still read a sector into it. */ + if (ss0 < disk->dev->sector_size) { + free (buf); + buf = ped_malloc (disk->dev->sector_size); + if (buf == NULL) + goto error; + } + + for (num=1; num==1 || num <= last_part_entry_num; num++) { + void *raw_part = buf; + if (!ped_device_read (disk->dev, raw_part, + num * ghost_size, 1)) + goto error_delete_all; + + if (!_rawpart_check_signature (raw_part)) + continue; + + if (num == 1) + last_part_entry_num + = _rawpart_get_partmap_size (raw_part, disk); + if (_rawpart_get_partmap_size (raw_part, disk) + != last_part_entry_num) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Conflicting partition map entry sizes! " + "Entry 1 says it is %d, but entry %d says " + "it is %d!"), + last_part_entry_num, + _rawpart_get_partmap_size (raw_part, disk)) + != PED_EXCEPTION_IGNORE) + goto error_delete_all; + } + + if (!_rawpart_is_active (raw_part)) + continue; + + part = _rawpart_analyse (raw_part, disk, num); + if (!part) + goto error_delete_all; + part->num = num; + part->fs_type = ped_file_system_probe (&part->geom); + PedConstraint *constraint_exact + = ped_constraint_exact (&part->geom); + if (constraint_exact == NULL) + goto error_delete_all; + bool ok = ped_disk_add_partition (disk, part, constraint_exact); + ped_constraint_destroy (constraint_exact); + if (!ok) + goto error_delete_all; + + if (_rawpart_is_partition_map (raw_part)) { + if (mac_disk_data->part_map_entry_num + && ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Weird! There are 2 partitions " + "map entries!")) + != PED_EXCEPTION_IGNORE) + goto error_delete_all; + + mac_disk_data->part_map_entry_num = num; + mac_disk_data->part_map_entry_count + = part->geom.end - ghost_size + 1; + } + } + + if (!mac_disk_data->part_map_entry_num) { + if (!_disk_add_part_map_entry (disk, 1)) + goto error_delete_all; + ped_disk_commit_to_dev (disk); + } + free (buf); + return 1; + +error_delete_all: + ped_disk_delete_all (disk); +error: + free (buf); + return 0; +} + +#ifndef DISCOVER_ONLY +/* The Ghost partition: is a blank entry, used to pad out each block (where + * there physical block size > 512 bytes). This is because OpenFirmware uses + * 512 byte blocks, but device drivers Think Different TM, with a different + * lbock size, so we need to do this to avoid a clash (!) + */ +static int +_pad_raw_part (PedDisk* disk, int num, MacRawPartition* part_map) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + int i; + + size_t ss = disk->dev->sector_size; + void *buf = ped_calloc (ss); + if (!buf) + return 0; + + MacRawPartition *ghost_entry = buf; + ghost_entry->signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2); + strcpy (ghost_entry->type, "Apple_Void"); + ghost_entry->map_count + = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num); + + for (i=0; i < mac_disk_data->ghost_size - 1; i++) { + PedSector idx = i + (num - 1) * mac_disk_data->ghost_size; + memcpy ((char*)part_map + idx * ss, ghost_entry, ss); + } + + free (buf); + return 1; +} + +static void +_update_driver_count (MacRawPartition* part_map_entry, + MacDiskData *mac_driverdata, const MacDiskData* mac_disk_data) +{ + uint16_t i; + uint32_t start_block, block_count; + + start_block = PED_BE32_TO_CPU(part_map_entry->start_block); + block_count = PED_BE32_TO_CPU(part_map_entry->block_count); + + for (i = 0; i < mac_disk_data->driver_count; i++) { + if (start_block == PED_BE32_TO_CPU(mac_disk_data->driverlist[i].block) && + block_count == PED_BE16_TO_CPU(mac_disk_data->driverlist[i].size)) { + uint16_t count_cur = mac_driverdata->driver_count; + mac_driverdata->driverlist[count_cur].block + = mac_disk_data->driverlist[i].block; + mac_driverdata->driverlist[count_cur].size + = mac_disk_data->driverlist[i].size; + mac_driverdata->driverlist[count_cur].type + = mac_disk_data->driverlist[i].type; + mac_driverdata->driver_count++; + break; + } + } +} + +static MacRawPartition * +get_pme (MacRawPartition const *part_map, PedSector i, PedDisk const *disk) +{ + MacDiskData const *mac_disk_data = disk->disk_specific; + PedSector idx = i * mac_disk_data->ghost_size - 1; + return (MacRawPartition *) ((char*)part_map + + idx * disk->dev->sector_size); +} + +/* Initialize the disk->dev->sector_size bytes of part_map[part->num]. */ +static int +_generate_raw_part (PedDisk* disk, PedPartition* part, + MacRawPartition* part_map, MacDiskData *mac_driverdata) +{ + MacDiskData* mac_disk_data; + MacPartitionData* mac_part_data; + + PED_ASSERT (part->num > 0); + + mac_disk_data = disk->disk_specific; + mac_part_data = part->disk_specific; + + MacRawPartition *part_map_entry = get_pme (part_map, part->num, disk); + memset (part_map_entry, 0, disk->dev->sector_size); + + part_map_entry->signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2); + part_map_entry->map_count + = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num); + part_map_entry->start_block = PED_CPU_TO_BE32 (part->geom.start); + part_map_entry->block_count = PED_CPU_TO_BE32 (part->geom.length); + strncpy (part_map_entry->name, mac_part_data->volume_name, 31); + part_map_entry->name[31] = '\0'; + strncpy (part_map_entry->type, mac_part_data->system_name, 31); + part_map_entry->type[31] = '\0'; + + if (mac_part_data->is_driver) { + if (mac_part_data->has_driver) + _update_driver_count(part_map_entry, mac_driverdata, + mac_disk_data); + } else + mac_part_data->data_region_length = part->geom.length; + part_map_entry->data_count = PED_CPU_TO_BE32 ( + mac_part_data->data_region_length); + part_map_entry->boot_count = PED_CPU_TO_BE32 ( + mac_part_data->boot_region_length); + part_map_entry->status = PED_CPU_TO_BE32 (mac_part_data->status); + part_map_entry->driver_sig + = PED_CPU_TO_BE32 (mac_part_data->driver_sig); + + part_map_entry->boot_load = + PED_CPU_TO_BE32 (mac_part_data->boot_base_address); + part_map_entry->boot_entry = + PED_CPU_TO_BE32 (mac_part_data->boot_entry_address); + part_map_entry->boot_cksum = + PED_CPU_TO_BE32 (mac_part_data->boot_checksum); + + strncpy (part_map_entry->processor, mac_part_data->processor_name, 15); + part_map_entry->processor[15] = '\0'; + + if (!_pad_raw_part (disk, part->num, part_map)) + goto error; + + return 1; + +error: + return 0; +} + +static int +_generate_raw_freespace_part (PedDisk* disk, PedGeometry* geom, int num, + MacRawPartition* part_map) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + + PED_ASSERT (num > 0); + + MacRawPartition *part_map_entry = get_pme (part_map, num, disk); + + part_map_entry->signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2); + part_map_entry->map_count + = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num); + part_map_entry->start_block = PED_CPU_TO_BE32 (geom->start); + part_map_entry->block_count = PED_CPU_TO_BE32 (geom->length); + strcpy (part_map_entry->name, "Extra"); + strcpy (part_map_entry->type, "Apple_Free"); + + part_map_entry->data_count = PED_CPU_TO_BE32 (geom->length); + part_map_entry->status = 0; + part_map_entry->driver_sig = 0; + + if (!_pad_raw_part (disk, num, part_map)) + goto error; + + return 1; + +error: + return 0; +} + +static int +_generate_empty_part (PedDisk* disk, int num, MacRawPartition* part_map) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + + PED_ASSERT (num > 0); + + MacRawPartition *part_map_entry = get_pme (part_map, num, disk); + part_map_entry->signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2); + part_map_entry->map_count + = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num); + strcpy (part_map_entry->type, "Apple_Void"); + + return _pad_raw_part (disk, num, part_map); +} + +/* returns the first empty entry in the partition map */ +static int _GL_ATTRIBUTE_PURE +_get_first_empty_part_entry (PedDisk* disk, MacRawPartition* part_map) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + int i; + + for (i=1; i <= mac_disk_data->last_part_entry_num; i++) { + MacRawPartition *part_map_entry = get_pme (part_map, i, disk); + if (!part_map_entry->signature) + return i; + } + + return 0; +} + +static int +write_block_zero (PedDisk* disk, MacDiskData* mac_driverdata) +{ + PedDevice* dev = disk->dev; + void *s0; + if (!ptt_read_sector (dev, 0, &s0)) + return 0; + MacRawDisk *raw_disk = (MacRawDisk *) s0; + + raw_disk->signature = PED_CPU_TO_BE16 (MAC_DISK_MAGIC); + raw_disk->block_size = PED_CPU_TO_BE16 (dev->sector_size); + raw_disk->block_count = PED_CPU_TO_BE32 (dev->length); + + raw_disk->driver_count = PED_CPU_TO_BE16(mac_driverdata->driver_count); + memcpy(&raw_disk->driverlist[0], &mac_driverdata->driverlist[0], + sizeof(raw_disk->driverlist)); + + int write_ok = ped_device_write (dev, raw_disk, 0, 1); + free (s0); + return write_ok; +} + +static int +mac_write (PedDisk* disk) +{ + MacRawPartition* part_map; + MacDiskData* mac_disk_data; + MacDiskData* mac_driverdata; /* updated driver list */ + PedPartition* part; + int num; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->disk_specific != NULL); + PED_ASSERT (disk->dev != NULL); + PED_ASSERT (!disk->update_mode); + + mac_disk_data = disk->disk_specific; + + if (!ped_disk_get_partition (disk, mac_disk_data->part_map_entry_num)) { + if (!_disk_add_part_map_entry (disk, 1)) + goto error; + } + + mac_driverdata = ped_malloc(sizeof(MacDiskData)); + if (!mac_driverdata) + goto error; + memset (mac_driverdata, 0, sizeof(MacDiskData)); + + size_t pmap_bytes = (mac_disk_data->part_map_entry_count + * mac_disk_data->ghost_size + * disk->dev->sector_size); + part_map = (MacRawPartition*) ped_calloc (pmap_bytes); + if (!part_map) + goto error_free_driverdata; + +/* write (to memory) the "real" partitions */ + for (part = ped_disk_next_partition (disk, NULL); part; + part = ped_disk_next_partition (disk, part)) { + if (!ped_partition_is_active (part)) + continue; + if (!_generate_raw_part (disk, part, part_map, mac_driverdata)) + goto error_free_part_map; + } + +/* write the "free space" partitions */ + for (part = ped_disk_next_partition (disk, NULL); part; + part = ped_disk_next_partition (disk, part)) { + if (part->type != PED_PARTITION_FREESPACE) + continue; + num = _get_first_empty_part_entry (disk, part_map); + if (!_generate_raw_freespace_part (disk, &part->geom, num, + part_map)) + goto error_free_part_map; + } + +/* write the "void" (empty) partitions */ + for (num = _get_first_empty_part_entry (disk, part_map); num; + num = _get_first_empty_part_entry (disk, part_map)) + _generate_empty_part (disk, num, part_map); + +/* write to disk */ + if (!ped_device_write (disk->dev, part_map, 1, + mac_disk_data->part_map_entry_count)) + goto error_free_part_map; + free (part_map); + int write_ok = write_block_zero (disk, mac_driverdata); + free (mac_driverdata); + return write_ok; + +error_free_part_map: + free (part_map); +error_free_driverdata: + free (mac_driverdata); +error: + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +mac_partition_new ( + const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, PedSector start, PedSector end) +{ + PedPartition* part; + MacPartitionData* mac_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (ped_partition_is_active (part)) { + part->disk_specific + = mac_data = ped_malloc (sizeof (MacPartitionData)); + if (!mac_data) + goto error_free_part; + + memset (mac_data, 0, sizeof (MacPartitionData)); + strcpy (mac_data->volume_name, "untitled"); + } else { + part->disk_specific = NULL; + } + return part; + +error_free_part: + free (part); +error: + return NULL; +} + +static PedPartition* +mac_partition_duplicate (const PedPartition* part) +{ + PedPartition* new_part; + MacPartitionData* new_mac_data; + MacPartitionData* old_mac_data; + + new_part = ped_partition_new (part->disk, part->type, + part->fs_type, part->geom.start, + part->geom.end); + if (!new_part) + return NULL; + new_part->num = part->num; + + old_mac_data = (MacPartitionData*) part->disk_specific; + new_mac_data = (MacPartitionData*) new_part->disk_specific; + + /* ugly, but C is ugly :p */ + memcpy (new_mac_data, old_mac_data, sizeof (MacPartitionData)); + return new_part; +} + +static void +mac_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL); + + if (ped_partition_is_active (part)) + free (part->disk_specific); + free (part); +} + +static int +mac_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + MacPartitionData* mac_data = part->disk_specific; + + part->fs_type = fs_type; + + if (fs_type && is_linux_swap (fs_type->name)) + ped_partition_set_flag (part, PED_PARTITION_SWAP, 1); + + if (mac_data->is_boot) { + strcpy (mac_data->system_name, "Apple_Bootstrap"); + mac_data->status = 0x33; + return 1; + } + + if (fs_type && (!strcmp (fs_type->name, "hfs") + || !strcmp (fs_type->name, "hfs+"))) { + strcpy (mac_data->system_name, "Apple_HFS"); + mac_data->status |= 0x7f; + } else if (fs_type && !strcmp (fs_type->name, "hfsx")) { + strcpy (mac_data->system_name, "Apple_HFSX"); + mac_data->status |= 0x7f; + } else { + strcpy (mac_data->system_name, "Apple_UNIX_SVR2"); + mac_data->status = 0x33; + } + + return 1; +} + +static int +mac_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + MacPartitionData* mac_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + mac_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_BOOT: + mac_data->is_boot = state; + + if (part->fs_type) + return mac_partition_set_system (part, part->fs_type); + + if (state) { + strcpy (mac_data->system_name, "Apple_Bootstrap"); + mac_data->status = 0x33; + } + return 1; + + case PED_PARTITION_ROOT: + if (state) { + strcpy (mac_data->volume_name, "root"); + mac_data->is_swap = 0; + } else { + if (mac_data->is_root) + strcpy (mac_data->volume_name, "untitled"); + } + mac_data->is_root = state; + return 1; + + case PED_PARTITION_SWAP: + if (state) { + strcpy (mac_data->volume_name, "swap"); + mac_data->is_root = 0; + } else { + if (mac_data->is_swap) + strcpy (mac_data->volume_name, "untitled"); + } + mac_data->is_swap = state; + return 1; + + case PED_PARTITION_LVM: + if (state) { + strcpy (mac_data->system_name, "Linux_LVM"); + mac_data->is_lvm = state; + } else { + if (mac_data->is_lvm) + mac_partition_set_system (part, part->fs_type); + } + return 1; + + case PED_PARTITION_RAID: + if (state) { + strcpy (mac_data->system_name, "Linux_RAID"); + mac_data->is_raid = state; + } else { + if (mac_data->is_raid) + mac_partition_set_system (part, part->fs_type); + } + return 1; + + default: + return 0; + } +} + +static int _GL_ATTRIBUTE_PURE +mac_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + MacPartitionData* mac_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + mac_data = part->disk_specific; + switch (flag) { + case PED_PARTITION_BOOT: + return mac_data->is_boot; + + case PED_PARTITION_ROOT: + return mac_data->is_root; + + case PED_PARTITION_SWAP: + return mac_data->is_swap; + + case PED_PARTITION_LVM: + return mac_data->is_lvm; + + case PED_PARTITION_RAID: + return mac_data->is_raid; + + default: + return 0; + } +} + +static int +mac_partition_is_flag_available ( + const PedPartition* part, PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_BOOT: + case PED_PARTITION_ROOT: + case PED_PARTITION_SWAP: + case PED_PARTITION_LVM: + case PED_PARTITION_RAID: + return 1; + + default: + return 0; + } +} + +static void +mac_partition_set_name (PedPartition* part, const char* name) +{ + MacPartitionData* mac_data; + int i; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + mac_data = part->disk_specific; + +#ifndef DISCOVER_ONLY + if (mac_data->is_root || mac_data->is_swap) { + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Changing the name of a root or swap partition " + "will prevent Linux from recognising it as such.")) + != PED_EXCEPTION_IGNORE) + return; + mac_data->is_root = mac_data->is_swap = 0; + } +#endif + + strncpy (mac_data->volume_name, name, 32); + mac_data->volume_name [32] = 0; + for (i = strlen (mac_data->volume_name) - 1; + mac_data->volume_name[i] == ' '; i--) + mac_data->volume_name [i] = 0; +} + +static const char* _GL_ATTRIBUTE_PURE +mac_partition_get_name (const PedPartition* part) +{ + MacPartitionData* mac_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + mac_data = part->disk_specific; + + return mac_data->volume_name; +} + +static PedAlignment* +mac_get_partition_alignment(const PedDisk *disk) +{ + return ped_alignment_new(0, 1); +} + +static PedConstraint* +_primary_constraint (PedDisk* disk) +{ + PedAlignment start_align; + PedAlignment end_align; + PedGeometry max_geom; + + if (!ped_alignment_init (&start_align, 0, 1)) + return NULL; + if (!ped_alignment_init (&end_align, -1, 1)) + return NULL; + if (!ped_geometry_init (&max_geom, disk->dev, 1, disk->dev->length - 1)) + return NULL; + + return ped_constraint_new (&start_align, &end_align, &max_geom, + &max_geom, 1, disk->dev->length); +} + +static int +mac_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL); + + if (_ped_partition_attempt_align (part, constraint, + _primary_constraint (part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +mac_partition_enumerate (PedPartition* part) +{ + PedDisk* disk; + MacDiskData* mac_disk_data; + int i; + int max_part_count; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + + disk = part->disk; + mac_disk_data = (MacDiskData*) disk->disk_specific; + + max_part_count = ped_disk_get_max_primary_partition_count (disk); + + if (part->num > 0 && part->num <= mac_disk_data->part_map_entry_count) + return 1; + + for (i = 1; i <= max_part_count; i++) { + if (!ped_disk_get_partition (disk, i)) { + part->num = i; + return 1; + } + } + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't add another partition -- the partition map is too " + "small!")); +#endif + + return 0; +} + +static int +_disk_count_partitions (PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + PedPartition* part = NULL; + PedPartition* last = NULL; + + PED_ASSERT (disk->update_mode); + + mac_disk_data->active_part_entry_count = 0; + mac_disk_data->free_part_entry_count = 0; + mac_disk_data->last_part_entry_num = 0; + + /* subtle: we only care about free space after the partition map. + * the partition map is an "active" partition, BTW... */ + for (part = ped_disk_next_partition (disk, part); part; + part = ped_disk_next_partition (disk, part)) { + if (!ped_partition_is_active (part)) + continue; + + mac_disk_data->active_part_entry_count++; + if (last && last->geom.end + 1 < part->geom.start) + mac_disk_data->free_part_entry_count++; + mac_disk_data->last_part_entry_num + = PED_MAX (mac_disk_data->last_part_entry_num, + part->num); + + last = part; + } + + if (last && last->geom.end < disk->dev->length - 1) + mac_disk_data->free_part_entry_count++; + + mac_disk_data->last_part_entry_num + = PED_MAX (mac_disk_data->last_part_entry_num, + mac_disk_data->active_part_entry_count + + mac_disk_data->free_part_entry_count); + return 1; +} + +static int +add_metadata_part (PedDisk* disk, PedSector start, PedSector end) +{ + PedPartition* new_part; + PedConstraint* constraint_any = ped_constraint_any (disk->dev); + + PED_ASSERT (disk != NULL); + + new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, + start, end); + if (!new_part) + goto error; + if (!ped_disk_add_partition (disk, new_part, constraint_any)) + goto error_destroy_new_part; + + ped_constraint_destroy (constraint_any); + return 1; + +error_destroy_new_part: + ped_partition_destroy (new_part); +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +static int +mac_alloc_metadata (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->disk_specific != NULL); + PED_ASSERT (disk->dev != NULL); + + if (!add_metadata_part (disk, 0, 0)) + return 0; + + /* hack: this seems to be a good place, to update the partition map + * entry count, since mac_alloc_metadata() gets called during + * _disk_pop_update_mode() + */ + return _disk_count_partitions (disk); +} + +static int +mac_get_max_primary_partition_count (const PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + PedPartition* part_map_partition; + + part_map_partition = ped_disk_get_partition (disk, + mac_disk_data->part_map_entry_num); + + /* HACK: if we haven't found the partition map partition (yet), + * we return this. + */ + if (!part_map_partition) { + mac_disk_data->part_map_entry_num = 0; + return 65536; + } + + /* HACK: since Mac labels need an entry for free-space regions, we + * must allow half plus 1 entries for free-space partitions. I hate + * this, but things get REALLY complicated, otherwise. + * (I'm prepared to complicate things later, but I want to get + * everything working, first) + */ + return mac_disk_data->part_map_entry_count / mac_disk_data->ghost_size + - mac_disk_data->free_part_entry_count + 1; +} + +static bool +mac_get_max_supported_partition_count (const PedDisk* disk, int *max_n) +{ + *max_n = 65536; + return true; +} + +#include "pt-common.h" +PT_define_limit_functions (mac) + +static PedDiskOps mac_disk_ops = { + clobber: NULL, + /* FIXME: remove this cast, once mac_write is fixed not to + modify its *DISK parameter. */ + write: NULL_IF_DISCOVER_ONLY ((int (*) (const PedDisk*)) mac_write), + + partition_set_name: mac_partition_set_name, + partition_get_name: mac_partition_get_name, + + get_partition_alignment: mac_get_partition_alignment, + + PT_op_function_initializers (mac) +}; + +static PedDiskType mac_disk_type = { + next: NULL, + name: "mac", + ops: &mac_disk_ops, + features: PED_DISK_TYPE_PARTITION_NAME +}; + +void +ped_disk_mac_init () +{ + PED_ASSERT (sizeof (MacRawPartition) == 512); + PED_ASSERT (sizeof (MacRawDisk) == 512); + + ped_disk_type_register (&mac_disk_type); +} + +void +ped_disk_mac_done () +{ + ped_disk_type_unregister (&mac_disk_type); +} diff --git a/libparted/labels/misc.h b/libparted/labels/misc.h new file mode 100644 index 0000000..7c11388 --- /dev/null +++ b/libparted/labels/misc.h @@ -0,0 +1,48 @@ +/* -*- Mode: c; indent-tabs-mode: nil -*- + + libparted - a library for manipulating disk partitions + Copyright (C) 2007, 2009-2014, 2019-2023 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/>. */ + +#include <inttypes.h> +#include <uuid/uuid.h> + +/* hack: use the ext2 uuid library to generate a reasonably random (hopefully + * with /dev/random) number. Unfortunately, we can only use 4 bytes of it. + * We make sure to avoid returning zero which may be interpreted as no FAT + * serial number or no MBR signature. + */ +static inline uint32_t +generate_random_uint32 (void) +{ + union { + uuid_t uuid; + uint32_t i; + } uu32; + + uuid_generate (uu32.uuid); + + return uu32.i > 0 ? uu32.i : 0xffffffff; +} + +/* Return nonzero if FS_TYPE_NAME starts with "linux-swap". + This must match the NUL-terminated "linux-swap" as well + as "linux-swap(v0)" and "linux-swap(v1)". */ +static inline int +is_linux_swap (char const *fs_type_name) +{ + char const *prefix = "linux-swap"; + return strncmp (fs_type_name, prefix, strlen (prefix)) == 0; +} diff --git a/libparted/labels/pc98.c b/libparted/labels/pc98.c new file mode 100644 index 0000000..cfa3ba4 --- /dev/null +++ b/libparted/labels/pc98.c @@ -0,0 +1,813 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000-2001, 2007-2014, 2019-2023 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/>. +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#include "pt-tools.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/* hacked from Linux/98 source: fs/partitions/nec98.h + * + * See also: + * http://people.FreeBSD.org/~kato/pc98.html + * http://www.kmc.kyoto-u.ac.jp/proj/linux98/index-english.html + * + * Partition types: + * + * id0(mid): + * bit 7: 1=bootable, 0=not bootable + * # Linux uses this flag to make a distinction between ext2 and swap. + * bit 6--0: + * 00H : N88-BASIC(data)?, PC-UX(data)? + * 04H : PC-UX(data) + * 06H : N88-BASIC + * 10H : N88-BASIC + * 14H : *BSD, PC-UX + * 20H : DOS(data), Windows95/98/NT, Linux + * 21H..2FH : DOS(system#1 .. system#15) + * 40H : Minix + * + * id1(sid): + * bit 7: 1=active, 0=sleep(hidden) + * # PC-UX uses this flag to make a distinction between its file system + * # and its swap. + * bit 6--0: + * 01H: FAT12 + * 11H: FAT16, <32MB [accessible to DOS 3.3] + * 21H: FAT16, >=32MB [Large Partition] + * 31H: NTFS + * 28H: Windows NT (Volume/Stripe Set?) + * 41H: Windows NT (Volume/Stripe Set?) + * 48H: Windows NT (Volume/Stripe Set?) + * 61H: FAT32 + * 04H: PC-UX + * 06H: N88-BASIC + * 44H: *BSD + * 62H: ext2, linux-swap + */ + +#define MAX_PART_COUNT 16 +#define PC9800_EXTFMT_MAGIC 0xAA55 + +#define BIT(x) (1 << (x)) +#define GET_BIT(n,bit) (((n) & BIT(bit)) != 0) +#define SET_BIT(n,bit,val) n = (val)? (n | BIT(bit)) : (n & ~BIT(bit)) + +typedef struct _PC98RawPartition PC98RawPartition; +typedef struct _PC98RawTable PC98RawTable; + +/* ripped from Linux/98 source */ +struct _PC98RawPartition { + uint8_t mid; /* 0x80 - boot */ + uint8_t sid; /* 0x80 - active */ + uint8_t dum1; /* dummy for padding */ + uint8_t dum2; /* dummy for padding */ + uint8_t ipl_sect; /* IPL sector */ + uint8_t ipl_head; /* IPL head */ + uint16_t ipl_cyl; /* IPL cylinder */ + uint8_t sector; /* starting sector */ + uint8_t head; /* starting head */ + uint16_t cyl; /* starting cylinder */ + uint8_t end_sector; /* end sector */ + uint8_t end_head; /* end head */ + uint16_t end_cyl; /* end cylinder */ + char name[16]; +} __attribute__((packed)); + +struct _PC98RawTable { + uint8_t boot_code [510]; + uint16_t magic; + PC98RawPartition partitions [MAX_PART_COUNT]; +} __attribute__((packed)); + +typedef struct { + PedSector ipl_sector; + int system; + int boot; + int hidden; + char name [17]; +} PC98PartitionData; + +/* this MBR boot code is dummy */ +static const char MBR_BOOT_CODE[] = { + 0xcb, /* retf */ + 0x00, 0x00, 0x00, /* */ + 0x49, 0x50, 0x4c, 0x31 /* "IPL1" */ +}; + +static PedDiskType pc98_disk_type; + +static PedSector chs_to_sector (const PedDevice* dev, int c, int h, int s); +static void sector_to_chs (const PedDevice* dev, PedSector sector, + int* c, int* h, int* s); + +/* magic(?) check */ +static int +pc98_check_magic (const PC98RawTable *part_table) +{ + /* check "extended-format" (have partition table?) */ + if (PED_LE16_TO_CPU(part_table->magic) != PC9800_EXTFMT_MAGIC) + return 0; + + return 1; +} + +static int +pc98_check_ipl_signature (const PC98RawTable *part_table) +{ + if (memcmp (part_table->boot_code + 4, "IPL1", 4) == 0) + return 1; + else if (memcmp (part_table->boot_code + 4, "Linux 98", 8) == 0) + return 1; + else if (memcmp (part_table->boot_code + 4, "GRUB/98 ", 8) == 0) + return 1; + else + return 0; +} + +static int +pc98_probe (const PedDevice *dev) +{ + PC98RawTable part_table; + + PED_ASSERT (dev != NULL); + + if (dev->sector_size != 512) + return 0; + + if (!ped_device_read (dev, &part_table, 0, 2)) + return 0; + + /* check magic */ + if (!pc98_check_magic (&part_table)) + return 0; + + /* check for boot loader signatures */ + return pc98_check_ipl_signature (&part_table); +} + +static PedDisk* +pc98_alloc (const PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + + return _ped_disk_alloc (dev, &pc98_disk_type); +} + +static PedDisk* +pc98_duplicate (const PedDisk* disk) +{ + return ped_disk_new_fresh (disk->dev, &pc98_disk_type); +} + +static void +pc98_free (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + + _ped_disk_free (disk); +} + +static PedSector _GL_ATTRIBUTE_PURE +chs_to_sector (const PedDevice* dev, int c, int h, int s) +{ + PED_ASSERT (dev != NULL); + return (c * dev->hw_geom.heads + h) * dev->hw_geom.sectors + s; +} + +static void +sector_to_chs (const PedDevice* dev, PedSector sector, int* c, int* h, int* s) +{ + PedSector cyl_size; + + PED_ASSERT (dev != NULL); + PED_ASSERT (c != NULL); + PED_ASSERT (h != NULL); + PED_ASSERT (s != NULL); + + cyl_size = dev->hw_geom.heads * dev->hw_geom.sectors; + + *c = sector / cyl_size; + *h = (sector) % cyl_size / dev->hw_geom.sectors; + *s = (sector) % cyl_size % dev->hw_geom.sectors; +} + +static PedSector _GL_ATTRIBUTE_PURE +legacy_start (const PedDisk* disk, const PC98RawPartition* raw_part) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (raw_part != NULL); + + return chs_to_sector (disk->dev, PED_LE16_TO_CPU(raw_part->cyl), + raw_part->head, raw_part->sector); +} + +static PedSector _GL_ATTRIBUTE_PURE +legacy_end (const PedDisk* disk, const PC98RawPartition* raw_part) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (raw_part != NULL); + + if (raw_part->end_head == 0 && raw_part->end_sector == 0) { + return chs_to_sector (disk->dev, + PED_LE16_TO_CPU(raw_part->end_cyl), + disk->dev->hw_geom.heads - 1, + disk->dev->hw_geom.sectors - 1); + } else { + return chs_to_sector (disk->dev, + PED_LE16_TO_CPU(raw_part->end_cyl), + raw_part->end_head, + raw_part->end_sector); + } +} + +static int +is_unused_partition(const PC98RawPartition* raw_part) +{ + if (raw_part->mid || raw_part->sid + || raw_part->ipl_sect + || raw_part->ipl_head + || PED_LE16_TO_CPU(raw_part->ipl_cyl) + || raw_part->sector + || raw_part->head + || PED_LE16_TO_CPU(raw_part->cyl) + || raw_part->end_sector + || raw_part->end_head + || PED_LE16_TO_CPU(raw_part->end_cyl)) + return 0; + return 1; +} + +static int +read_table (PedDisk* disk) +{ + int i; + PC98RawTable table; + PedConstraint* constraint_any; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + + constraint_any = ped_constraint_any (disk->dev); + + if (!ped_device_read (disk->dev, (void*) &table, 0, 2)) + goto error; + + if (!pc98_check_magic(&table)) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL, + _("Invalid partition table on %s."), + disk->dev->path)) + goto error; + } + + for (i = 0; i < MAX_PART_COUNT; i++) { + PC98RawPartition* raw_part; + PedPartition* part; + PC98PartitionData* pc98_data; + PedSector part_start; + PedSector part_end; + + raw_part = &table.partitions [i]; + + if (is_unused_partition(raw_part)) + continue; + + part_start = legacy_start (disk, raw_part); + part_end = legacy_end (disk, raw_part); + + part = ped_partition_new (disk, PED_PARTITION_NORMAL, + NULL, part_start, part_end); + if (!part) + goto error; + pc98_data = part->disk_specific; + PED_ASSERT (pc98_data != NULL); + + pc98_data->system = (raw_part->mid << 8) | raw_part->sid; + pc98_data->boot = GET_BIT(raw_part->mid, 7); + pc98_data->hidden = !GET_BIT(raw_part->sid, 7); + + ped_partition_set_name (part, raw_part->name); + + pc98_data->ipl_sector = chs_to_sector ( + disk->dev, + PED_LE16_TO_CPU(raw_part->ipl_cyl), + raw_part->ipl_head, + raw_part->ipl_sect); + + /* hack */ + if (pc98_data->ipl_sector == part->geom.start) + pc98_data->ipl_sector = 0; + + part->num = i + 1; + + if (!ped_disk_add_partition (disk, part, constraint_any)) + goto error; + + if (part->geom.start != part_start + || part->geom.end != part_end) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Partition %d isn't aligned to cylinder " + "boundaries. This is still unsupported."), + part->num); + goto error; + } + + part->fs_type = ped_file_system_probe (&part->geom); + } + + ped_constraint_destroy (constraint_any); + return 1; + +error: + ped_disk_delete_all (disk); + ped_constraint_destroy (constraint_any); + return 0; +} + +static int +pc98_read (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + + ped_disk_delete_all (disk); + return read_table (disk); +} + +#ifndef DISCOVER_ONLY +static int +fill_raw_part (PC98RawPartition* raw_part, const PedPartition* part) +{ + PC98PartitionData* pc98_data; + int c, h, s; + const char* name; + + PED_ASSERT (raw_part != NULL); + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + pc98_data = part->disk_specific; + raw_part->mid = (pc98_data->system >> 8) & 0xFF; + raw_part->sid = pc98_data->system & 0xFF; + + SET_BIT(raw_part->mid, 7, pc98_data->boot); + SET_BIT(raw_part->sid, 7, !pc98_data->hidden); + + memset (raw_part->name, ' ', sizeof(raw_part->name)); + name = ped_partition_get_name (part); + PED_ASSERT (name != NULL); + PED_ASSERT (strlen (name) <= 16); + if (!strlen (name) && part->fs_type) + name = part->fs_type->name; + memcpy (raw_part->name, name, strlen (name)); + + sector_to_chs (part->disk->dev, part->geom.start, &c, &h, &s); + raw_part->cyl = PED_CPU_TO_LE16(c); + raw_part->head = h; + raw_part->sector = s; + + if (pc98_data->ipl_sector) { + sector_to_chs (part->disk->dev, pc98_data->ipl_sector, + &c, &h, &s); + raw_part->ipl_cyl = PED_CPU_TO_LE16(c); + raw_part->ipl_head = h; + raw_part->ipl_sect = s; + } else { + raw_part->ipl_cyl = raw_part->cyl; + raw_part->ipl_head = raw_part->head; + raw_part->ipl_sect = raw_part->sector; + } + + sector_to_chs (part->disk->dev, part->geom.end, &c, &h, &s); + if (h != part->disk->dev->hw_geom.heads - 1 + || s != part->disk->dev->hw_geom.sectors - 1) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Partition %d isn't aligned to cylinder " + "boundaries. This is still unsupported."), + part->num); + return 0; + } + raw_part->end_cyl = PED_CPU_TO_LE16(c); + raw_part->end_head = 0; + raw_part->end_sector = 0; + + return 1; +} + +static int +pc98_write (const PedDisk* disk) +{ + PedPartition* part; + int i; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + + void *s0; + if (!ptt_read_sectors (disk->dev, 0, 2, &s0)) + return 0; + PC98RawTable *table = s0; + + if (!pc98_check_ipl_signature (table)) { + memset (table->boot_code, 0, sizeof(table->boot_code)); + memcpy (table->boot_code, MBR_BOOT_CODE, sizeof(MBR_BOOT_CODE)); + } + + memset (table->partitions, 0, sizeof (table->partitions)); + table->magic = PED_CPU_TO_LE16(PC9800_EXTFMT_MAGIC); + + for (i = 1; i <= MAX_PART_COUNT; i++) { + part = ped_disk_get_partition (disk, i); + if (!part) + continue; + + if (!fill_raw_part (&table->partitions [i - 1], part)) + return 0; + } + + int write_ok = ped_device_write (disk->dev, table, 0, 2); + free (s0); + if (!write_ok) + return 0; + return ped_device_sync (disk->dev); +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +pc98_partition_new ( + const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, PedSector start, PedSector end) +{ + PedPartition* part; + PC98PartitionData* pc98_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (ped_partition_is_active (part)) { + part->disk_specific + = pc98_data = ped_malloc (sizeof (PC98PartitionData)); + if (!pc98_data) + goto error_free_part; + pc98_data->ipl_sector = 0; + pc98_data->hidden = 0; + pc98_data->boot = 0; + strcpy (pc98_data->name, ""); + } else { + part->disk_specific = NULL; + } + return part; + +error_free_part: + free (part); +error: + return 0; +} + +static PedPartition* +pc98_partition_duplicate (const PedPartition* part) +{ + PedPartition* new_part; + PC98PartitionData* new_pc98_data; + PC98PartitionData* old_pc98_data; + + new_part = ped_partition_new (part->disk, part->type, + part->fs_type, part->geom.start, + part->geom.end); + if (!new_part) + return NULL; + new_part->num = part->num; + + old_pc98_data = (PC98PartitionData*) part->disk_specific; + new_pc98_data = (PC98PartitionData*) new_part->disk_specific; + + /* ugly, but C is ugly :p */ + memcpy (new_pc98_data, old_pc98_data, sizeof (PC98PartitionData)); + return new_part; +} + +static void +pc98_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL); + + if (ped_partition_is_active (part)) + free (part->disk_specific); + free (part); +} + +static int +pc98_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + PC98PartitionData* pc98_data = part->disk_specific; + + part->fs_type = fs_type; + + pc98_data->system = 0x2062; + if (fs_type) { + if (!strcmp (fs_type->name, "fat16")) { + if (part->geom.length * 512 >= 32 * 1024 * 1024) + pc98_data->system = 0x2021; + else + pc98_data->system = 0x2011; + } else if (!strcmp (fs_type->name, "fat32")) { + pc98_data->system = 0x2061; + } else if (!strcmp (fs_type->name, "ntfs")) { + pc98_data->system = 0x2031; + } else if (!strncmp (fs_type->name, "ufs", 3)) { + pc98_data->system = 0x2044; + } else { /* ext2, reiser, xfs, etc. */ + /* ext2 partitions must be marked boot */ + pc98_data->boot = 1; + pc98_data->system = 0xa062; + } + } + + if (pc98_data->boot) + pc98_data->system |= 0x8000; + if (!pc98_data->hidden) + pc98_data->system |= 0x0080; + return 1; +} + +static int +pc98_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + PC98PartitionData* pc98_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + pc98_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_HIDDEN: + pc98_data->hidden = state; + return ped_partition_set_system (part, part->fs_type); + + case PED_PARTITION_BOOT: + pc98_data->boot = state; + return ped_partition_set_system (part, part->fs_type); + + default: + return 0; + } +} + +static int _GL_ATTRIBUTE_PURE +pc98_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + PC98PartitionData* pc98_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + pc98_data = part->disk_specific; + switch (flag) { + case PED_PARTITION_HIDDEN: + return pc98_data->hidden; + + case PED_PARTITION_BOOT: + return pc98_data->boot; + + default: + return 0; + } +} + +static int +pc98_partition_is_flag_available ( + const PedPartition* part, PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_HIDDEN: + case PED_PARTITION_BOOT: + return 1; + + default: + return 0; + } +} + +static void +pc98_partition_set_name (PedPartition* part, const char* name) +{ + PC98PartitionData* pc98_data; + int i; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + pc98_data = part->disk_specific; + + strncpy (pc98_data->name, name, 16); + pc98_data->name [16] = 0; + for (i = strlen (pc98_data->name) - 1; pc98_data->name[i] == ' '; i--) + pc98_data->name [i] = 0; +} + +static const char* _GL_ATTRIBUTE_PURE +pc98_partition_get_name (const PedPartition* part) +{ + PC98PartitionData* pc98_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + pc98_data = part->disk_specific; + + return pc98_data->name; +} + +static PedAlignment* +pc98_get_partition_alignment(const PedDisk *disk) +{ + PedSector cylinder_size = + disk->dev->hw_geom.sectors * disk->dev->hw_geom.heads; + + return ped_alignment_new(0, cylinder_size); +} + +static PedConstraint* +_primary_constraint (PedDisk* disk) +{ + PedDevice* dev = disk->dev; + PedAlignment start_align; + PedAlignment end_align; + PedGeometry max_geom; + PedSector cylinder_size; + + cylinder_size = dev->hw_geom.sectors * dev->hw_geom.heads; + + if (!ped_alignment_init (&start_align, 0, cylinder_size)) + return NULL; + if (!ped_alignment_init (&end_align, -1, cylinder_size)) + return NULL; + if (!ped_geometry_init (&max_geom, dev, cylinder_size, + dev->length - cylinder_size)) + return NULL; + + return ped_constraint_new (&start_align, &end_align, &max_geom, + &max_geom, 1, dev->length); +} + +static int +pc98_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL); + + if (_ped_partition_attempt_align (part, constraint, + _primary_constraint (part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +next_primary (PedDisk* disk) +{ + int i; + for (i=1; i<=MAX_PART_COUNT; i++) { + if (!ped_disk_get_partition (disk, i)) + return i; + } + return 0; +} + +static int +pc98_partition_enumerate (PedPartition* part) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + + /* don't re-number a partition */ + if (part->num != -1) + return 1; + + PED_ASSERT (ped_partition_is_active (part)); + + part->num = next_primary (part->disk); + if (!part->num) { + ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't add another partition.")); + return 0; + } + + return 1; +} + +static int +pc98_alloc_metadata (PedDisk* disk) +{ + PedPartition* new_part; + PedConstraint* constraint_any = NULL; + PedSector cyl_size; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + + constraint_any = ped_constraint_any (disk->dev); + + cyl_size = disk->dev->hw_geom.sectors * disk->dev->hw_geom.heads; + new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, + 0, cyl_size - 1); + if (!new_part) + goto error; + + if (!ped_disk_add_partition (disk, new_part, constraint_any)) { + ped_partition_destroy (new_part); + goto error; + } + + ped_constraint_destroy (constraint_any); + return 1; + +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +static int +pc98_get_max_primary_partition_count (const PedDisk* disk) +{ + return MAX_PART_COUNT; +} + +static bool +pc98_get_max_supported_partition_count (const PedDisk* disk, int *max_n) +{ + *max_n = MAX_PART_COUNT; + return true; +} + +#include "pt-common.h" +PT_define_limit_functions (pc98) + +static PedDiskOps pc98_disk_ops = { + clobber: NULL, + write: NULL_IF_DISCOVER_ONLY (pc98_write), + + partition_set_name: pc98_partition_set_name, + partition_get_name: pc98_partition_get_name, + + get_partition_alignment: pc98_get_partition_alignment, + + PT_op_function_initializers (pc98) +}; + +static PedDiskType pc98_disk_type = { + next: NULL, + name: "pc98", + ops: &pc98_disk_ops, + features: PED_DISK_TYPE_PARTITION_NAME +}; + +void +ped_disk_pc98_init () +{ + PED_ASSERT (sizeof (PC98RawTable) == 512 * 2); + ped_disk_type_register (&pc98_disk_type); +} + +void +ped_disk_pc98_done () +{ + ped_disk_type_unregister (&pc98_disk_type); +} diff --git a/libparted/labels/pt-common.h b/libparted/labels/pt-common.h new file mode 100644 index 0000000..76f06e3 --- /dev/null +++ b/libparted/labels/pt-common.h @@ -0,0 +1,55 @@ +/* Factor some of the duplication out of *.c. */ + +#ifdef DISCOVER_ONLY +# define NULL_IF_DISCOVER_ONLY(val) NULL +#else +# define NULL_IF_DISCOVER_ONLY(val) val +#endif + +#define PT_define_limit_functions(PT_type) \ + \ +static bool \ +PT_type##_partition_check (const PedPartition *part) \ +{ \ + return ptt_partition_max_start_len (#PT_type, part); \ +} \ + \ +static PedSector \ +PT_type##_partition_max_start_sector (void) \ +{ \ + PedSector max; \ + int err = ptt_partition_max_start_sector (#PT_type, &max); \ + PED_ASSERT (err == 0); \ + return max; \ +} \ + \ +static PedSector \ +PT_type##_partition_max_length (void) \ +{ \ + PedSector max; \ + int err = ptt_partition_max_length (#PT_type, &max); \ + PED_ASSERT (err == 0); \ + return max; \ +} + +#define PT_op_function_initializers(PT_type) \ +probe: PT_type##_probe, \ +alloc: PT_type##_alloc, \ +duplicate: PT_type##_duplicate, \ +free: PT_type##_free, \ +read: PT_type##_read, \ +partition_new: PT_type##_partition_new, \ +partition_duplicate: PT_type##_partition_duplicate, \ +partition_set_flag: PT_type##_partition_set_flag, \ +partition_get_flag: PT_type##_partition_get_flag, \ +partition_set_system: PT_type##_partition_set_system, \ +partition_is_flag_available: PT_type##_partition_is_flag_available, \ +partition_align: PT_type##_partition_align, \ +partition_destroy: PT_type##_partition_destroy, \ +partition_enumerate: PT_type##_partition_enumerate, \ +alloc_metadata: PT_type##_alloc_metadata, \ +get_max_primary_partition_count: PT_type##_get_max_primary_partition_count, \ +get_max_supported_partition_count:PT_type##_get_max_supported_partition_count,\ +partition_check: PT_type##_partition_check, \ +max_length: PT_type##_partition_max_length, \ +max_start_sector: PT_type##_partition_max_start_sector diff --git a/libparted/labels/pt-limit.c b/libparted/labels/pt-limit.c new file mode 100644 index 0000000..f1c7a09 --- /dev/null +++ b/libparted/labels/pt-limit.c @@ -0,0 +1,161 @@ +/* ANSI-C code produced by gperf version 3.1 */ +/* Command-line: gperf -C -N pt_limit_lookup -n -t -s 6 -k '*' --language=ANSI-C pt-limit.gperf */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>." +#endif + +#line 1 "pt-limit.gperf" +struct partition_limit +{ + char const *name; + uint64_t max_start_sector; + uint64_t max_length; +}; + +#define TOTAL_KEYWORDS 11 +#define MIN_WORD_LENGTH 3 +#define MAX_WORD_LENGTH 5 +#define MIN_HASH_VALUE 5 +#define MAX_HASH_VALUE 55 +/* maximum key range = 51, duplicates = 0 */ + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +hash (register const char *str, register size_t len) +{ + static const unsigned char asso_values[] = + { + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 10, 5, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 5, 40, 40, + 5, 56, 56, 5, 20, 0, 56, 56, 15, 0, + 15, 10, 0, 56, 0, 5, 0, 10, 15, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56 + }; + register unsigned int hval = 0; + + switch (len) + { + default: + hval += asso_values[(unsigned char)str[4]]; + /*FALLTHROUGH*/ + case 4: + hval += asso_values[(unsigned char)str[3]]; + /*FALLTHROUGH*/ + case 3: + hval += asso_values[(unsigned char)str[2]]; + /*FALLTHROUGH*/ + case 2: + hval += asso_values[(unsigned char)str[1]]; + /*FALLTHROUGH*/ + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval; +} + +const struct partition_limit * +pt_limit_lookup (register const char *str, register size_t len) +{ + static const struct partition_limit wordlist[] = + { + {""}, {""}, {""}, {""}, {""}, +#line 10 "pt-limit.gperf" + {"gpt",UINT64_MAX,UINT64_MAX}, + {""}, {""}, {""}, {""}, +#line 28 "pt-limit.gperf" + {"atari",INT32_MAX,INT32_MAX}, + {""}, {""}, {""}, {""}, +#line 27 "pt-limit.gperf" + {"amiga",UINT32_MAX,UINT32_MAX}, + {""}, {""}, {""}, {""}, +#line 8 "pt-limit.gperf" + {"dasd",UINT32_MAX,UINT32_MAX}, + {""}, {""}, {""}, {""}, +#line 12 "pt-limit.gperf" + {"msdos",UINT32_MAX,UINT32_MAX}, + {""}, {""}, {""}, {""}, +#line 18 "pt-limit.gperf" + {"sun",128ULL*UINT32_MAX,UINT32_MAX}, + {""}, {""}, {""}, {""}, +#line 23 "pt-limit.gperf" + {"loop",UINT64_MAX,UINT64_MAX}, + {""}, {""}, {""}, {""}, +#line 9 "pt-limit.gperf" + {"dvh",UINT32_MAX,UINT32_MAX}, + {""}, {""}, {""}, {""}, +#line 11 "pt-limit.gperf" + {"mac",UINT32_MAX,UINT32_MAX}, + {""}, {""}, {""}, {""}, +#line 20 "pt-limit.gperf" + {"bsd",UINT32_MAX,UINT32_MAX}, + {""}, {""}, {""}, {""}, +#line 24 "pt-limit.gperf" + {"pc98",UINT32_MAX,UINT32_MAX} + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register unsigned int key = hash (str, len); + + if (key <= MAX_HASH_VALUE) + { + register const char *s = wordlist[key].name; + + if (*str == *s && !strcmp (str + 1, s + 1)) + return &wordlist[key]; + } + } + return 0; +} diff --git a/libparted/labels/pt-limit.gperf b/libparted/labels/pt-limit.gperf new file mode 100644 index 0000000..d5a580d --- /dev/null +++ b/libparted/labels/pt-limit.gperf @@ -0,0 +1,28 @@ +struct partition_limit +{ + char const *name; + uint64_t max_start_sector; + uint64_t max_length; +}; +%% +dasd,UINT32_MAX,UINT32_MAX +dvh,UINT32_MAX,UINT32_MAX +gpt,UINT64_MAX,UINT64_MAX +mac,UINT32_MAX,UINT32_MAX +msdos,UINT32_MAX,UINT32_MAX +# +# Sun partitions are cylinder-aligned, and it looks like there are 128 sectors +# in a cylinder. FIXME verify. Possibly compute sectors-per-cylinder, given +# u_int16_t ntrks; /* Tracks per cylinder */ +# u_int16_t nsect; /* Sectors per track */ +sun,128ULL*UINT32_MAX,UINT32_MAX +# +bsd,UINT32_MAX,UINT32_MAX +# aix,UINT32_MAX,UINT32_MAX +# In reality, loop labels have no particular limit. +loop,UINT64_MAX,UINT64_MAX +pc98,UINT32_MAX,UINT32_MAX +# +# FIXME: not verified. looks like these are cylinder aligned, too +amiga,UINT32_MAX,UINT32_MAX +atari,INT32_MAX,INT32_MAX diff --git a/libparted/labels/pt-tools.c b/libparted/labels/pt-tools.c new file mode 100644 index 0000000..add4c45 --- /dev/null +++ b/libparted/labels/pt-tools.c @@ -0,0 +1,186 @@ +/* partition table tools + Copyright (C) 2008-2014, 2019-2023 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/>. */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> + +#include <parted/parted.h> +#include <parted/debug.h> + +#include "pt-tools.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +static char zero[16 * 1024]; + +/* Write a single sector to DISK, filling the first BUFLEN + bytes of that sector with data from BUF, and NUL-filling + any remaining bytes. Return nonzero to indicate success, + zero otherwise. */ +int +ptt_write_sector (PedDisk const *disk, void const *buf, size_t buflen) +{ + PED_ASSERT (buflen <= disk->dev->sector_size); + /* Allocate a big enough buffer for ped_device_write. */ + char *s0 = ped_malloc (disk->dev->sector_size); + if (s0 == NULL) + return 0; + /* Copy boot_code into the first part. */ + memcpy (s0, buf, buflen); + char *p = s0 + buflen; + /* Fill the rest with zeros. */ + memset (p, 0, disk->dev->sector_size - buflen); + int write_ok = ped_device_write (disk->dev, s0, 0, 1); + free (s0); + + return write_ok; +} + +/* Read N sectors, starting with sector SECTOR_NUM (which has length + DEV->sector_size) into malloc'd storage. If the read fails, free + the memory and return zero without modifying *BUF. Otherwise, set + *BUF to the new buffer and return 1. */ +int +ptt_read_sectors (PedDevice const *dev, PedSector start_sector, + PedSector n_sectors, void **buf) +{ + char *b = ped_malloc (n_sectors * dev->sector_size); + PED_ASSERT (b != NULL); + if (!ped_device_read (dev, b, start_sector, n_sectors)) { + free (b); + return 0; + } + *buf = b; + return 1; +} + +/* Read sector, SECTOR_NUM (which has length DEV->sector_size) into malloc'd + storage. If the read fails, free the memory and return zero without + modifying *BUF. Otherwise, set *BUF to the new buffer and return 1. */ +int +ptt_read_sector (PedDevice const *dev, PedSector sector_num, void **buf) +{ + return ptt_read_sectors (dev, sector_num, 1, buf); +} + +/* Zero N sectors of DEV, starting with START. + Return nonzero to indicate success, zero otherwise. */ +int +ptt_clear_sectors (PedDevice *dev, PedSector start, PedSector n) +{ + PED_ASSERT (dev->sector_size <= sizeof zero); + PedSector n_z_sectors = sizeof zero / dev->sector_size; + PedSector n_full = n / n_z_sectors; + PedSector i; + for (i = 0; i < n_full; i++) + { + if (!ped_device_write (dev, zero, start + n_z_sectors * i, n_z_sectors)) + return 0; + } + + PedSector rem = n - n_z_sectors * i; + return (rem == 0 + ? 1 : ped_device_write (dev, zero, start + n_z_sectors * i, rem)); +} + +/* Zero N sectors of GEOM->dev, starting with GEOM->start + START. + Return nonzero to indicate success, zero otherwise. */ +int +ptt_geom_clear_sectors (PedGeometry *geom, PedSector start, PedSector n) +{ + return ptt_clear_sectors (geom->dev, geom->start + start, n); +} + +#define pt_limit_lookup _GL_ATTRIBUTE_PURE __pt_limit_lookup +#include "pt-limit.c" + +/* Throw an exception and return 0 if PART's starting sector number or + its length is greater than the maximum allowed value for LABEL_TYPE. + Otherwise, return 1. */ +int +ptt_partition_max_start_len (char const *pt_type, const PedPartition *part) +{ + struct partition_limit const *pt_lim + = __pt_limit_lookup (pt_type, strlen (pt_type)); + + /* If we don't have info on the type, return "true". */ + if (pt_lim == NULL) + return 1; + + /* If the length in sectors exceeds the limit, you lose. */ + if (part->geom.length > pt_lim->max_length) + { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("partition length of %jd sectors exceeds" + " the %s-partition-table-imposed maximum" + " of %jd"), + part->geom.length, + pt_type, + pt_lim->max_length); + return 0; + } + + /* If the starting sector exceeds the limit, you lose. */ + if (part->geom.start > pt_lim->max_start_sector) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("starting sector number, %jd exceeds" + " the %s-partition-table-imposed maximum" + " of %jd"), + part->geom.start, + pt_type, + pt_lim->max_start_sector); + return 0; + } + + return 1; +} + +/* Set *MAX to the largest representation-imposed starting sector number + of a partition of type PT_TYPE and return 0. If PT_TYPE is not + recognized, return -1. */ +int +ptt_partition_max_start_sector (char const *pt_type, PedSector *max) +{ + struct partition_limit const *pt_lim + = __pt_limit_lookup (pt_type, strlen (pt_type)); + if (pt_lim == NULL) + return -1; + + *max = pt_lim->max_start_sector; + return 0; +} + +/* Set *MAX to the maximum representable length of a partition of type + PT_TYPE and return 0. If PT_TYPE is not recognized, return -1. */ +int +ptt_partition_max_length (char const *pt_type, PedSector *max) +{ + struct partition_limit const *pt_lim + = __pt_limit_lookup (pt_type, strlen (pt_type)); + if (pt_lim == NULL) + return -1; + + *max = pt_lim->max_length; + return 0; +} diff --git a/libparted/labels/pt-tools.h b/libparted/labels/pt-tools.h new file mode 100644 index 0000000..34562f7 --- /dev/null +++ b/libparted/labels/pt-tools.h @@ -0,0 +1,31 @@ +/* libparted - a library for manipulating disk partitions + Copyright (C) 2008-2014, 2019-2023 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/>. */ + +#include <stddef.h> +#include <parted/disk.h> + +int ptt_write_sector (PedDisk const *disk, void const *buf, size_t buflen); +int ptt_read_sector (PedDevice const *dev, PedSector sector_num, void **buf); +int ptt_read_sectors (PedDevice const *dev, PedSector start_sector, + PedSector n_sectors, void **buf); +int ptt_clear_sectors (PedDevice *dev, PedSector start, PedSector count); +int ptt_geom_clear_sectors (PedGeometry *geom, PedSector start, + PedSector count); +int ptt_partition_max_start_len (char const *label_type, + const PedPartition *part); + +int ptt_partition_max_start_sector (char const *pt_type, PedSector *max); +int ptt_partition_max_length (char const *pt_type, PedSector *max); diff --git a/libparted/labels/rdb.c b/libparted/labels/rdb.c new file mode 100644 index 0000000..499d385 --- /dev/null +++ b/libparted/labels/rdb.c @@ -0,0 +1,1167 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + + libparted - a library for manipulating disk partitions + disk_amiga.c - libparted module to manipulate amiga RDB partition tables. + Copyright (C) 2000-2001, 2004, 2007-2014, 2019-2023 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/>. + + Contributor: Sven Luther <luther@debian.org> +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#include "pt-tools.h" + +#ifndef MAX +# define MAX(a,b) ((a) < (b) ? (b) : (a)) +#endif + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "misc.h" + +/* String manipulation */ +static void _amiga_set_bstr (const char *cstr, char *bstr, int maxsize) { + int size = strlen (cstr); + int i; + + if (size >= maxsize) return; + bstr[0] = size; + for (i = 0; i<size; i++) bstr[i+1] = cstr[i]; +} +static const char * _amiga_get_bstr (char * bstr) { + char * cstr = bstr + 1; + int size = bstr[0]; + + cstr[size] = '\0'; + return cstr; +} + +#define IDNAME_RIGIDDISK (uint32_t)0x5244534B /* 'RDSK' */ +#define IDNAME_BADBLOCK (uint32_t)0x42414442 /* 'BADB' */ +#define IDNAME_PARTITION (uint32_t)0x50415254 /* 'PART' */ +#define IDNAME_FILESYSHEADER (uint32_t)0x46534844 /* 'FSHD' */ +#define IDNAME_LOADSEG (uint32_t)0x4C534547 /* 'LSEG' */ +#define IDNAME_BOOT (uint32_t)0x424f4f54 /* 'BOOT' */ +#define IDNAME_FREE (uint32_t)0xffffffff + +static const char * +_amiga_block_id (uint32_t id) { + switch (id) { + case IDNAME_RIGIDDISK : + return "RDSK"; + case IDNAME_BADBLOCK : + return "BADB"; + case IDNAME_PARTITION : + return "PART"; + case IDNAME_FILESYSHEADER : + return "FSHD"; + case IDNAME_LOADSEG : + return "LSEG"; + case IDNAME_BOOT : + return "BOOT"; + case IDNAME_FREE : + return "<free>"; + default : + return "<unknown>"; + } +} + +struct AmigaIds { + uint32_t ID; + struct AmigaIds *next; +}; + +static struct AmigaIds * +_amiga_add_id (uint32_t id, struct AmigaIds *ids) { + struct AmigaIds *newid; + + if ((newid=ped_malloc(sizeof (struct AmigaIds)))==NULL) + return 0; + newid->ID = id; + newid->next = ids; + return newid; +} + +static void +_amiga_free_ids (struct AmigaIds *ids) { + struct AmigaIds *current, *next; + + for (current = ids; current != NULL; current = next) { + next = current->next; + free (current); + } +} +static int _GL_ATTRIBUTE_PURE +_amiga_id_in_list (uint32_t id, struct AmigaIds *ids) { + struct AmigaIds *current; + + for (current = ids; current != NULL; current = current->next) { + if (id == current->ID) + return 1; + } + return 0; +} + +struct AmigaBlock { + uint32_t amiga_ID; /* Identifier 32 bit word */ + uint32_t amiga_SummedLongss; /* Size of the structure for checksums */ + int32_t amiga_ChkSum; /* Checksum of the structure */ +}; +#define AMIGA(pos) ((struct AmigaBlock *)(pos)) + +static int +_amiga_checksum (struct AmigaBlock *blk) { + uint32_t *rdb = (uint32_t *) blk; + uint32_t sum; + int i, end; + + sum = PED_BE32_TO_CPU (rdb[0]); + end = PED_BE32_TO_CPU (rdb[1]); + + if (end > PED_SECTOR_SIZE_DEFAULT) end = PED_SECTOR_SIZE_DEFAULT; + + for (i = 1; i < end; i++) sum += PED_BE32_TO_CPU (rdb[i]); + + return sum; +} + +static void +_amiga_calculate_checksum (struct AmigaBlock *blk) { + blk->amiga_ChkSum = PED_CPU_TO_BE32( + PED_BE32_TO_CPU(blk->amiga_ChkSum) - + _amiga_checksum((struct AmigaBlock *) blk)); + return; +} + +static struct AmigaBlock * +_amiga_read_block (const PedDevice *dev, struct AmigaBlock *blk, + PedSector block, struct AmigaIds *ids) +{ + if (!ped_device_read (dev, blk, block, 1)) + return NULL; + if (ids && !_amiga_id_in_list(PED_BE32_TO_CPU(blk->amiga_ID), ids)) + return NULL; + if (_amiga_checksum (blk) != 0) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE | PED_EXCEPTION_CANCEL, + _("%s : Bad checksum on block %llu of type %s."), + __func__, block, _amiga_block_id(PED_BE32_TO_CPU(blk->amiga_ID)))) + { + case PED_EXCEPTION_CANCEL : + return NULL; + case PED_EXCEPTION_FIX : + _amiga_calculate_checksum(AMIGA(blk)); + if (!ped_device_write ((PedDevice*)dev, blk, block, 1)) + return NULL; + /* FALLTHROUGH */ + case PED_EXCEPTION_IGNORE : + case PED_EXCEPTION_UNHANDLED : + default : + return blk; + } + } + return blk; +} + +struct RigidDiskBlock { + uint32_t rdb_ID; /* Identifier 32 bit word : 'RDSK' */ + uint32_t rdb_SummedLongs; /* Size of the structure for checksums */ + int32_t rdb_ChkSum; /* Checksum of the structure */ + uint32_t rdb_HostID; /* SCSI Target ID of host, not really used */ + uint32_t rdb_BlockBytes; /* Size of disk blocks */ + uint32_t rdb_Flags; /* RDB Flags */ + /* block list heads */ + uint32_t rdb_BadBlockList; /* Bad block list */ + uint32_t rdb_PartitionList; /* Partition list */ + uint32_t rdb_FileSysHeaderList; /* File system header list */ + uint32_t rdb_DriveInit; /* Drive specific init code */ + uint32_t rdb_BootBlockList; /* Amiga OS 4 Boot Blocks */ + uint32_t rdb_Reserved1[5]; /* Unused word, need to be set to $ffffffff */ + /* physical drive characteristics */ + uint32_t rdb_Cylinders; /* Number of the cylinders of the drive */ + uint32_t rdb_Sectors; /* Number of sectors of the drive */ + uint32_t rdb_Heads; /* Number of heads of the drive */ + uint32_t rdb_Interleave; /* Interleave */ + uint32_t rdb_Park; /* Head parking cylinder */ + uint32_t rdb_Reserved2[3]; /* Unused word, need to be set to $ffffffff */ + uint32_t rdb_WritePreComp; /* Starting cylinder of write precompensation */ + uint32_t rdb_ReducedWrite; /* Starting cylinder of reduced write current */ + uint32_t rdb_StepRate; /* Step rate of the drive */ + uint32_t rdb_Reserved3[5]; /* Unused word, need to be set to $ffffffff */ + /* logical drive characteristics */ + uint32_t rdb_RDBBlocksLo; /* low block of range reserved for hardblocks */ + uint32_t rdb_RDBBlocksHi; /* high block of range for these hardblocks */ + uint32_t rdb_LoCylinder; /* low cylinder of partitionable disk area */ + uint32_t rdb_HiCylinder; /* high cylinder of partitionable data area */ + uint32_t rdb_CylBlocks; /* number of blocks available per cylinder */ + uint32_t rdb_AutoParkSeconds; /* zero for no auto park */ + uint32_t rdb_HighRDSKBlock; /* highest block used by RDSK */ + /* (not including replacement bad blocks) */ + uint32_t rdb_Reserved4; + /* drive identification */ + char rdb_DiskVendor[8]; + char rdb_DiskProduct[16]; + char rdb_DiskRevision[4]; + char rdb_ControllerVendor[8]; + char rdb_ControllerProduct[16]; + char rdb_ControllerRevision[4]; + uint32_t rdb_Reserved5[10]; +}; + +#define RDSK(pos) ((struct RigidDiskBlock *)(pos)) + +#define AMIGA_RDB_NOT_FOUND ((uint32_t)0xffffffff) +#define RDB_LOCATION_LIMIT 16 +#define AMIGA_MAX_PARTITIONS 128 +#define MAX_RDB_BLOCK (RDB_LOCATION_LIMIT + 2 * AMIGA_MAX_PARTITIONS + 2) + +static uint32_t +_amiga_find_rdb (const PedDevice *dev, struct RigidDiskBlock *rdb) { + int i; + struct AmigaIds *ids; + + ids = _amiga_add_id (IDNAME_RIGIDDISK, NULL); + + for (i = 0; i<RDB_LOCATION_LIMIT; i++) { + if (!_amiga_read_block (dev, AMIGA(rdb), i, ids)) { + continue; + } + if (PED_BE32_TO_CPU (rdb->rdb_ID) == IDNAME_RIGIDDISK) { + _amiga_free_ids (ids); + return i; + } + } + _amiga_free_ids (ids); + return AMIGA_RDB_NOT_FOUND; +} + +struct PartitionBlock { + uint32_t pb_ID; /* Identifier 32 bit word : 'PART' */ + uint32_t pb_SummedLongs; /* Size of the structure for checksums */ + int32_t pb_ChkSum; /* Checksum of the structure */ + uint32_t pb_HostID; /* SCSI Target ID of host, not really used */ + uint32_t pb_Next; /* Block number of the next PartitionBlock */ + uint32_t pb_Flags; /* Part Flags (NOMOUNT and BOOTABLE) */ + uint32_t pb_Reserved1[2]; + uint32_t pb_DevFlags; /* Preferred flags for OpenDevice */ + char pb_DriveName[32]; /* Preferred DOS device name: BSTR form */ + uint32_t pb_Reserved2[15]; + uint32_t de_TableSize; /* Size of Environment vector */ + /* Size of the blocks in 32 bit words, usually 128 */ + uint32_t de_SizeBlock; + uint32_t de_SecOrg; /* Not used; must be 0 */ + uint32_t de_Surfaces; /* Number of heads (surfaces) */ + /* Disk sectors per block, used with SizeBlock, usually 1 */ + uint32_t de_SectorPerBlock; + uint32_t de_BlocksPerTrack; /* Blocks per track. drive specific */ + uint32_t de_Reserved; /* DOS reserved blocks at start of partition. */ + uint32_t de_PreAlloc; /* DOS reserved blocks at end of partition */ + uint32_t de_Interleave; /* Not used, usually 0 */ + uint32_t de_LowCyl; /* First cylinder of the partition */ + uint32_t de_HighCyl; /* Last cylinder of the partition */ + uint32_t de_NumBuffers; /* Initial # DOS of buffers. */ + uint32_t de_BufMemType; /* Type of mem to allocate for buffers */ + uint32_t de_MaxTransfer; /* Max number of bytes to transfer at a time */ + uint32_t de_Mask; /* Address Mask to block out certain memory */ + int32_t de_BootPri; /* Boot priority for autoboot */ + uint32_t de_DosType; /* Dostype of the file system */ + uint32_t de_Baud; /* Baud rate for serial handler */ + uint32_t de_Control; /* Control word for handler/filesystem */ + uint32_t de_BootBlocks; /* Number of blocks containing boot code */ + uint32_t pb_EReserved[12]; +}; + +#define PART(pos) ((struct PartitionBlock *)(pos)) + +#define PBFB_BOOTABLE 0 /* this partition is intended to be bootable */ +#define PBFF_BOOTABLE 1L /* (expected directories and files exist) */ +#define PBFB_NOMOUNT 1 /* do not mount this partition (e.g. manually */ +#define PBFF_NOMOUNT 2L /* mounted, but space reserved here) */ +#define PBFB_RAID 2 /* this partition is intended to be part of */ +#define PBFF_RAID 4L /* a RAID array */ +#define PBFB_LVM 3 /* this partition is intended to be part of */ +#define PBFF_LVM 8L /* a LVM volume group */ + + +struct LinkedBlock { + uint32_t lk_ID; /* Identifier 32 bit word */ + uint32_t lk_SummedLongs; /* Size of the structure for checksums */ + int32_t lk_ChkSum; /* Checksum of the structure */ + uint32_t pb_HostID; /* SCSI Target ID of host, not really used */ + uint32_t lk_Next; /* Block number of the next PartitionBlock */ +}; +struct Linked2Block { + uint32_t lk2_ID; /* Identifier 32 bit word */ + uint32_t lk2_SummedLongs; /* Size of the structure for checksums */ + int32_t lk2_ChkSum; /* Checksum of the structure */ + uint32_t lk2_HostID; /* SCSI Target ID of host, not really used */ + uint32_t lk2_Next; /* Block number of the next PartitionBlock */ + uint32_t lk2_Reverved[13]; + uint32_t lk2_Linked; /* Secondary linked list */ +}; +#define LINK_END (uint32_t)0xffffffff +#define LNK(pos) ((struct LinkedBlock *)(pos)) +#define LNK2(pos) ((struct Linked2Block *)(pos)) + + +static PedDiskType amiga_disk_type; + +static int +amiga_probe (const PedDevice *dev) +{ + struct RigidDiskBlock *rdb; + uint32_t found; + PED_ASSERT(dev != NULL); + + if ((rdb=RDSK(ped_malloc(dev->sector_size)))==NULL) + return 0; + found = _amiga_find_rdb (dev, rdb); + free (rdb); + + return (found == AMIGA_RDB_NOT_FOUND ? 0 : 1); +} + +static PedDisk* +amiga_alloc (const PedDevice* dev) +{ + PedDisk *disk; + struct RigidDiskBlock *rdb; + PedSector cyl_size; + int highest_cylinder, highest_block; + + PED_ASSERT(dev != NULL); + cyl_size = dev->hw_geom.sectors * dev->hw_geom.heads; + + if (!(disk = _ped_disk_alloc (dev, &amiga_disk_type))) + return NULL; + + if (!(disk->disk_specific = ped_malloc (disk->dev->sector_size))) { + free (disk); + return NULL; + } + rdb = disk->disk_specific; + + /* Upon failed assertion this does leak. That's fine, because + if the assertion fails, you have bigger problems than this leak. */ + PED_ASSERT(sizeof(*rdb) <= disk->dev->sector_size); + + memset(rdb, 0, disk->dev->sector_size); + + rdb->rdb_ID = PED_CPU_TO_BE32 (IDNAME_RIGIDDISK); + rdb->rdb_SummedLongs = PED_CPU_TO_BE32 (64); + rdb->rdb_HostID = PED_CPU_TO_BE32 (0); + rdb->rdb_BlockBytes = PED_CPU_TO_BE32 (disk->dev->sector_size); + rdb->rdb_Flags = PED_CPU_TO_BE32 (0); + + /* Block lists */ + rdb->rdb_BadBlockList = PED_CPU_TO_BE32 (LINK_END); + rdb->rdb_PartitionList = PED_CPU_TO_BE32 (LINK_END); + rdb->rdb_FileSysHeaderList = PED_CPU_TO_BE32 (LINK_END); + rdb->rdb_DriveInit = PED_CPU_TO_BE32 (LINK_END); + rdb->rdb_BootBlockList = PED_CPU_TO_BE32 (LINK_END); + + /* Physical drive characteristics */ + rdb->rdb_Cylinders = PED_CPU_TO_BE32 (dev->hw_geom.cylinders); + rdb->rdb_Sectors = PED_CPU_TO_BE32 (dev->hw_geom.sectors); + rdb->rdb_Heads = PED_CPU_TO_BE32 (dev->hw_geom.heads); + rdb->rdb_Interleave = PED_CPU_TO_BE32 (0); + rdb->rdb_Park = PED_CPU_TO_BE32 (dev->hw_geom.cylinders); + rdb->rdb_WritePreComp = PED_CPU_TO_BE32 (dev->hw_geom.cylinders); + rdb->rdb_ReducedWrite = PED_CPU_TO_BE32 (dev->hw_geom.cylinders); + rdb->rdb_StepRate = PED_CPU_TO_BE32 (0); + + highest_cylinder = 1 + MAX_RDB_BLOCK / cyl_size; + highest_block = highest_cylinder * cyl_size - 1; + + /* Logical driver characteristics */ + rdb->rdb_RDBBlocksLo = PED_CPU_TO_BE32 (0); + rdb->rdb_RDBBlocksHi = PED_CPU_TO_BE32 (highest_block); + rdb->rdb_LoCylinder = PED_CPU_TO_BE32 (highest_cylinder); + rdb->rdb_HiCylinder = PED_CPU_TO_BE32 (dev->hw_geom.cylinders -1); + rdb->rdb_CylBlocks = PED_CPU_TO_BE32 (cyl_size); + rdb->rdb_AutoParkSeconds = PED_CPU_TO_BE32 (0); + /* rdb_HighRDSKBlock will only be set when writing */ + rdb->rdb_HighRDSKBlock = PED_CPU_TO_BE32 (0); + + /* Driver identification */ + _amiga_set_bstr("", rdb->rdb_DiskVendor, 8); + _amiga_set_bstr(dev->model, rdb->rdb_DiskProduct, 16); + _amiga_set_bstr("", rdb->rdb_DiskRevision, 4); + _amiga_set_bstr("", rdb->rdb_ControllerVendor, 8); + _amiga_set_bstr("", rdb->rdb_ControllerProduct, 16); + _amiga_set_bstr("", rdb->rdb_ControllerRevision, 4); + + /* And calculate the checksum */ + _amiga_calculate_checksum ((struct AmigaBlock *) rdb); + + return disk; +} + +static PedDisk* +amiga_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + struct RigidDiskBlock * new_rdb; + struct RigidDiskBlock * old_rdb; + PED_ASSERT(disk != NULL); + PED_ASSERT(disk->dev != NULL); + PED_ASSERT(disk->disk_specific != NULL); + + old_rdb = (struct RigidDiskBlock *) disk->disk_specific; + + if (!(new_disk = ped_disk_new_fresh (disk->dev, &amiga_disk_type))) + return NULL; + + new_rdb = (struct RigidDiskBlock *) new_disk->disk_specific; + memcpy (new_rdb, old_rdb, 256); + return new_disk; +} + +static void +amiga_free (PedDisk* disk) +{ + PED_ASSERT(disk != NULL); + PED_ASSERT(disk->disk_specific != NULL); + + free (disk->disk_specific); + _ped_disk_free (disk); +} + +static int +_amiga_loop_check (uint32_t block, uint32_t * blocklist, uint32_t max) +{ + uint32_t i; + + for (i = 0; i < max; i++) + if (block == blocklist[i]) { + /* We are looping, let's stop. */ + return 1; + } + blocklist[max] = block; + return 0; +} + +/* We have already allocated a rdb, we are now reading it from the disk */ +static int +amiga_read (PedDisk* disk) +{ + struct RigidDiskBlock *rdb; + struct PartitionBlock *partition; + uint32_t partblock; + uint32_t partlist[AMIGA_MAX_PARTITIONS]; + PedSector cylblocks; + int i; + + PED_ASSERT(disk != NULL); + PED_ASSERT(disk->dev != NULL); + PED_ASSERT(disk->dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + PED_ASSERT(disk->disk_specific != NULL); + rdb = RDSK(disk->disk_specific); + + if (_amiga_find_rdb (disk->dev, rdb) == AMIGA_RDB_NOT_FOUND) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Didn't find rdb block, should never happen."), __func__); + return 0; + } + + /* Let's copy the rdb read geometry to the dev */ + /* FIXME: should this go into disk->dev->bios_geom instead? */ + disk->dev->hw_geom.cylinders = PED_BE32_TO_CPU (rdb->rdb_Cylinders); + disk->dev->hw_geom.heads = PED_BE32_TO_CPU (rdb->rdb_Heads); + disk->dev->hw_geom.sectors = PED_BE32_TO_CPU (rdb->rdb_Sectors); + cylblocks = (PedSector) PED_BE32_TO_CPU (rdb->rdb_Heads) * + (PedSector) PED_BE32_TO_CPU (rdb->rdb_Sectors); + + /* Remove all partitions in the former in memory table */ + ped_disk_delete_all (disk); + + /* Let's allocate a partition block */ + if (!(partition = ped_malloc (disk->dev->sector_size))) + return 0; + + /* We initialize the hardblock free list to detect loops */ + for (i = 0; i < AMIGA_MAX_PARTITIONS; i++) partlist[i] = LINK_END; + + for (i = 1, partblock = PED_BE32_TO_CPU(rdb->rdb_PartitionList); + i < AMIGA_MAX_PARTITIONS && partblock != LINK_END; + i++, partblock = PED_BE32_TO_CPU(partition->pb_Next)) + { + PedPartition *part; + PedSector start, end; + + /* Let's look for loops in the partition table */ + if (_amiga_loop_check(partblock, partlist, i)) { + break; + } + + /* Let's allocate and read a partition block to get its geometry*/ + if (!_amiga_read_block (disk->dev, AMIGA(partition), + (PedSector)partblock, NULL)) { + free(partition); + return 0; + } + + start = ((PedSector) PED_BE32_TO_CPU (partition->de_LowCyl)) + * cylblocks; + end = (((PedSector) PED_BE32_TO_CPU (partition->de_HighCyl)) + + 1) * cylblocks - 1; + + /* We can now construct a new partition */ + if (!(part = ped_partition_new (disk, PED_PARTITION_NORMAL, + NULL, start, end))) { + free(partition); + return 0; + } + /* And copy over the partition block */ + memcpy(part->disk_specific, partition, 256); + + part->num = i; + part->type = 0; + /* Let's probe what file system is present on the disk */ + part->fs_type = ped_file_system_probe (&part->geom); + + PedConstraint *constraint_exact + = ped_constraint_exact (&part->geom); + if (constraint_exact == NULL) + return 0; + bool ok = ped_disk_add_partition (disk, part, constraint_exact); + ped_constraint_destroy (constraint_exact); + if (!ok) { + ped_partition_destroy(part); + free(partition); + return 0; + } + } + free(partition); + return 1; +} + +static int +_amiga_find_free_blocks(const PedDisk *disk, uint32_t *table, + struct LinkedBlock *block, uint32_t first, uint32_t type) +{ + PedSector next; + + PED_ASSERT(disk != NULL); + PED_ASSERT(disk->dev != NULL); + + for (next = first; next != LINK_END; next = PED_BE32_TO_CPU(block->lk_Next)) { + if (table[next] != IDNAME_FREE) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE | PED_EXCEPTION_CANCEL, + _("%s : Loop detected at block %d."), __func__, next)) + { + case PED_EXCEPTION_CANCEL : + return 0; + case PED_EXCEPTION_FIX : + /* TODO : Need to add fixing code */ + case PED_EXCEPTION_IGNORE : + case PED_EXCEPTION_UNHANDLED : + default : + return 1; + } + } + + if (!_amiga_read_block (disk->dev, AMIGA(block), next, NULL)) { + return 0; + } + if (PED_BE32_TO_CPU(block->lk_ID) != type) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : The %s list seems bad at block %s."), + __func__, _amiga_block_id(PED_BE32_TO_CPU(block->lk_ID)), next)) + { + /* TODO : to more subtile things here */ + case PED_EXCEPTION_CANCEL : + case PED_EXCEPTION_UNHANDLED : + default : + return 0; + } + } + table[next] = type; + if (PED_BE32_TO_CPU(block->lk_ID) == IDNAME_FILESYSHEADER) { + if (_amiga_find_free_blocks(disk, table, block, + PED_BE32_TO_CPU(LNK2(block)->lk2_Linked), + IDNAME_LOADSEG) == 0) return 0; + } + } + return 1; +} +static uint32_t _GL_ATTRIBUTE_PURE +_amiga_next_free_block(uint32_t *table, uint32_t start, uint32_t type) { + int i; + + for (i = start; table[i] != type && table[i] != IDNAME_FREE; i++); + return i; +} +static PedPartition * _GL_ATTRIBUTE_PURE +_amiga_next_real_partition(const PedDisk *disk, PedPartition *part) { + PedPartition *next; + + for (next = ped_disk_next_partition (disk, part); + next != NULL && !ped_partition_is_active (next); + next = ped_disk_next_partition (disk, next)); + return next; +} +#ifndef DISCOVER_ONLY +static int +amiga_write (const PedDisk* disk) +{ + struct RigidDiskBlock *rdb; + struct LinkedBlock *block; + struct PartitionBlock *partition; + PedPartition *part, *next_part; + PedSector cylblocks, first_hb, last_hb; + uint32_t * table; + uint32_t i; + uint32_t rdb_num, part_num, block_num, next_num; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + PED_ASSERT (disk->disk_specific != NULL); + + if (!(rdb = ped_malloc (disk->dev->sector_size))) + return 0; + + /* Let's read the rdb */ + if ((rdb_num = _amiga_find_rdb (disk->dev, rdb)) == AMIGA_RDB_NOT_FOUND) { + rdb_num = 2; + size_t pb_size = sizeof (struct PartitionBlock); + /* Initialize only the part that won't be copied over + with a partition block in amiga_read. */ + memset ((char *)(RDSK(disk->disk_specific)) + pb_size, + 0, PED_SECTOR_SIZE_DEFAULT - pb_size); + } else { + memcpy (RDSK(disk->disk_specific), rdb, disk->dev->sector_size); + } + free (rdb); + rdb = RDSK(disk->disk_specific); + + cylblocks = (PedSector) PED_BE32_TO_CPU (rdb->rdb_Heads) * + (PedSector) PED_BE32_TO_CPU (rdb->rdb_Sectors); + first_hb = (PedSector) PED_BE32_TO_CPU (rdb->rdb_RDBBlocksLo); + last_hb = (PedSector) PED_BE32_TO_CPU (rdb->rdb_RDBBlocksHi); + + /* Allocate a free block table and initialize it. + There must be room for at least RDB_NUM + 2 entries, since + the first RDB_NUM+1 entries get IDNAME_RIGIDDISK, and the + following one must have LINK_END to serve as sentinel. */ + size_t tab_size = 2 + MAX (last_hb - first_hb, rdb_num); + if (!(table = ped_malloc (tab_size * sizeof *table))) + return 0; + + for (i = 0; i <= rdb_num; i++) + table[i] = IDNAME_RIGIDDISK; + for ( ; i < tab_size; i++) + table[i] = LINK_END; + + /* Let's allocate a partition block */ + if (!(block = ped_malloc (disk->dev->sector_size))) { + free (table); + return 0; + } + + /* And fill the free block table */ + if (_amiga_find_free_blocks(disk, table, block, + PED_BE32_TO_CPU (rdb->rdb_BadBlockList), IDNAME_BADBLOCK) == 0) + { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Failed to list bad blocks."), __func__); + goto error_free_table; + } + if (_amiga_find_free_blocks(disk, table, block, + PED_BE32_TO_CPU (rdb->rdb_PartitionList), IDNAME_PARTITION) == 0) + { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Failed to list partition blocks."), __func__); + goto error_free_table; + } + if (_amiga_find_free_blocks(disk, table, block, + PED_BE32_TO_CPU (rdb->rdb_FileSysHeaderList), IDNAME_FILESYSHEADER) == 0) + { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Failed to list file system blocks."), __func__); + goto error_free_table; + } + if (_amiga_find_free_blocks(disk, table, block, + PED_BE32_TO_CPU (rdb->rdb_BootBlockList), IDNAME_BOOT) == 0) + { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Failed to list boot blocks."), __func__); + goto error_free_table; + } + + block_num = part_num = _amiga_next_free_block(table, rdb_num+1, + IDNAME_PARTITION); + part = _amiga_next_real_partition(disk, NULL); + rdb->rdb_PartitionList = PED_CPU_TO_BE32(part ? part_num : LINK_END); + for (; part != NULL; part = next_part, block_num = next_num) { + PED_ASSERT(part->disk_specific != NULL); + PED_ASSERT(part->geom.start % cylblocks == 0); + PED_ASSERT((part->geom.end + 1) % cylblocks == 0); + + next_part = _amiga_next_real_partition(disk, part); + next_num = _amiga_next_free_block(table, block_num+1, IDNAME_PARTITION); + + partition = PART(part->disk_specific); + if (next_part == NULL) + partition->pb_Next = PED_CPU_TO_BE32(LINK_END); + else + partition->pb_Next = PED_CPU_TO_BE32(next_num); + partition->de_LowCyl = PED_CPU_TO_BE32(part->geom.start/cylblocks); + partition->de_HighCyl = PED_CPU_TO_BE32((part->geom.end+1)/cylblocks-1); + _amiga_calculate_checksum(AMIGA(partition)); + if (!ped_device_write (disk->dev, (void*) partition, block_num, 1)) { + ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Failed to write partition block at %d."), + block_num); + goto error_free_table; + /* WARNING : If we fail here, we stop everything, + * and the partition table is lost. A better + * solution should be found, using the second + * half of the hardblocks to not overwrite the + * old partition table. It becomes problematic + * if we use more than half of the hardblocks. */ + } + } + + if (block_num > PED_BE32_TO_CPU (rdb->rdb_HighRDSKBlock)) + rdb->rdb_HighRDSKBlock = PED_CPU_TO_BE32(block_num); + + _amiga_calculate_checksum(AMIGA(rdb)); + if (!ped_device_write (disk->dev, (void*) disk->disk_specific, rdb_num, 1)) + goto error_free_table; + + free (table); + free (block); + return ped_device_sync (disk->dev); + +error_free_table: + free (table); + free (block); + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +amiga_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition *part; + PedDevice *dev; + PedSector cyl; + struct PartitionBlock *partition; + struct RigidDiskBlock *rdb; + + PED_ASSERT(disk != NULL); + PED_ASSERT(disk->dev != NULL); + PED_ASSERT(disk->disk_specific != NULL); + dev = disk->dev; + cyl = (PedSector) (dev->hw_geom.sectors * dev->hw_geom.heads); + rdb = RDSK(disk->disk_specific); + + if (!(part = _ped_partition_alloc (disk, part_type, fs_type, start, end))) + return NULL; + + if (ped_partition_is_active (part)) { + if (!(part->disk_specific = ped_malloc (disk->dev->sector_size))) { + free (part); + return NULL; + } + partition = PART(part->disk_specific); + memset(partition, 0, sizeof(struct PartitionBlock)); + + partition->pb_ID = PED_CPU_TO_BE32(IDNAME_PARTITION); + partition->pb_SummedLongs = PED_CPU_TO_BE32(64); + partition->pb_HostID = rdb->rdb_HostID; + partition->pb_Flags = PED_CPU_TO_BE32(0); + /* TODO : use a scheme including the device name and the + * partition number, if it is possible */ + _amiga_set_bstr("dhx", partition->pb_DriveName, 32); + + partition->de_TableSize = PED_CPU_TO_BE32(19); + partition->de_SizeBlock = PED_CPU_TO_BE32(128); + partition->de_SecOrg = PED_CPU_TO_BE32(0); + partition->de_Surfaces = PED_CPU_TO_BE32(dev->hw_geom.heads); + partition->de_SectorPerBlock = PED_CPU_TO_BE32(1); + partition->de_BlocksPerTrack + = PED_CPU_TO_BE32(dev->hw_geom.sectors); + partition->de_Reserved = PED_CPU_TO_BE32(2); + partition->de_PreAlloc = PED_CPU_TO_BE32(0); + partition->de_Interleave = PED_CPU_TO_BE32(0); + partition->de_LowCyl = PED_CPU_TO_BE32(start/cyl); + partition->de_HighCyl = PED_CPU_TO_BE32((end+1)/cyl-1); + partition->de_NumBuffers = PED_CPU_TO_BE32(30); + partition->de_BufMemType = PED_CPU_TO_BE32(0); + partition->de_MaxTransfer = PED_CPU_TO_BE32(0x7fffffff); + partition->de_Mask = PED_CPU_TO_BE32(0xffffffff); + partition->de_BootPri = PED_CPU_TO_BE32(0); + partition->de_DosType = PED_CPU_TO_BE32(0x4c4e5800); + partition->de_Baud = PED_CPU_TO_BE32(0); + partition->de_Control = PED_CPU_TO_BE32(0); + partition->de_BootBlocks = PED_CPU_TO_BE32(0); + + } else { + part->disk_specific = NULL; + } + return part; +} + +static PedPartition* +amiga_partition_duplicate (const PedPartition* part) +{ + PedPartition *new_part; + struct PartitionBlock *new_amiga_part; + struct PartitionBlock *old_amiga_part; + + PED_ASSERT(part != NULL); + PED_ASSERT(part->disk != NULL); + PED_ASSERT(part->disk_specific != NULL); + old_amiga_part = (struct PartitionBlock *) part->disk_specific; + + new_part = ped_partition_new (part->disk, part->type, + part->fs_type, part->geom.start, + part->geom.end); + if (!new_part) + return NULL; + + new_amiga_part = (struct PartitionBlock *) new_part->disk_specific; + memcpy (new_amiga_part, old_amiga_part, 256); + + return new_part; +} + +static void +amiga_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL); + + if (ped_partition_is_active (part)) { + PED_ASSERT (part->disk_specific != NULL); + free (part->disk_specific); + } + _ped_partition_free (part); +} + +static int +amiga_partition_set_system (PedPartition* part, + const PedFileSystemType* fs_type) +{ + struct PartitionBlock *partition; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + partition = PART(part->disk_specific); + + part->fs_type = fs_type; + + if (!fs_type) + partition->de_DosType = PED_CPU_TO_BE32(0x4c4e5800); /* 'LNX\0' */ + else if (!strcmp (fs_type->name, "ext2")) + partition->de_DosType = PED_CPU_TO_BE32(0x4c4e5800); /* 'LNX\0' */ + else if (!strcmp (fs_type->name, "ext3")) + partition->de_DosType = PED_CPU_TO_BE32(0x45585403); /* 'EXT\3' */ + else if (is_linux_swap (fs_type->name)) + partition->de_DosType = PED_CPU_TO_BE32(0x53575000); /* 'SWP\0' */ + else if (!strcmp (fs_type->name, "fat16")) + partition->de_DosType = PED_CPU_TO_BE32(0x46415400); /* 'FAT\0' */ + else if (!strcmp (fs_type->name, "fat32")) + partition->de_DosType = PED_CPU_TO_BE32(0x46415401); /* 'FAT\1'*/ + else if (!strcmp (fs_type->name, "hfs")) + partition->de_DosType = PED_CPU_TO_BE32(0x48465300); /* 'HFS\0' */ + else if (!strcmp (fs_type->name, "jfs")) + partition->de_DosType = PED_CPU_TO_BE32(0x4a465300); /* 'JFS\0' */ + else if (!strcmp (fs_type->name, "ntfs")) + partition->de_DosType = PED_CPU_TO_BE32(0x4e544653); /* 'NTFS' */ + else if (!strcmp (fs_type->name, "reiserfs")) + partition->de_DosType = PED_CPU_TO_BE32(0x52465300); /* 'RFS\0' */ + else if (!strcmp (fs_type->name, "sun-ufs")) + partition->de_DosType = PED_CPU_TO_BE32(0x53554653); /* 'SUFS' */ + else if (!strcmp (fs_type->name, "hp-ufs")) + partition->de_DosType = PED_CPU_TO_BE32(0x48554653); /* 'HUFS' */ + else if (!strcmp (fs_type->name, "xfs")) + partition->de_DosType = PED_CPU_TO_BE32(0x58465300); /* 'XFS\0' */ + else + partition->de_DosType = PED_CPU_TO_BE32(0x00000000); /* unknown */ + return 1; +} + +static int +amiga_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + struct PartitionBlock *partition; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + partition = PART(part->disk_specific); + + switch (flag) { + case PED_PARTITION_BOOT: + if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_BOOTABLE); + else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_BOOTABLE)); + return 1; + case PED_PARTITION_HIDDEN: + if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_NOMOUNT); + else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_NOMOUNT)); + return 1; + case PED_PARTITION_RAID: + if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_RAID); + else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_RAID)); + return 1; + case PED_PARTITION_LVM: + if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_LVM); + else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_LVM)); + return 1; + default: + return 0; + } +} + +static int _GL_ATTRIBUTE_PURE +amiga_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + struct PartitionBlock *partition; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + partition = PART(part->disk_specific); + + switch (flag) { + case PED_PARTITION_BOOT: + return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_BOOTABLE)); + case PED_PARTITION_HIDDEN: + return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_NOMOUNT)); + case PED_PARTITION_RAID: + return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_RAID)); + case PED_PARTITION_LVM: + return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_LVM)); + default: + return 0; + } +} + +static int +amiga_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_BOOT: + case PED_PARTITION_HIDDEN: + case PED_PARTITION_RAID: + case PED_PARTITION_LVM: + return 1; + default: + return 0; + } +} + +static void +amiga_partition_set_name (PedPartition* part, const char* name) +{ + struct PartitionBlock *partition; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + partition = PART(part->disk_specific); + _amiga_set_bstr(name, partition->pb_DriveName, 32); +} +static const char* +amiga_partition_get_name (const PedPartition* part) +{ + struct PartitionBlock *partition; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + partition = PART(part->disk_specific); + + return _amiga_get_bstr(partition->pb_DriveName); +} + +static PedAlignment* +amiga_get_partition_alignment(const PedDisk *disk) +{ + PedSector cylinder_size = + disk->dev->hw_geom.sectors * disk->dev->hw_geom.heads; + + return ped_alignment_new(0, cylinder_size); +} + +static PedConstraint* +_amiga_get_constraint (const PedDisk *disk) +{ + PedDevice *dev = disk->dev; + PedAlignment start_align, end_align; + PedGeometry max_geom; + PedSector cyl_size = dev->hw_geom.sectors * dev->hw_geom.heads; + + if (!ped_alignment_init(&start_align, 0, cyl_size)) + return NULL; + if (!ped_alignment_init(&end_align, -1, cyl_size)) + return NULL; + if (!ped_geometry_init(&max_geom, dev, MAX_RDB_BLOCK + 1, + dev->length - MAX_RDB_BLOCK - 1)) + return NULL; + + return ped_constraint_new (&start_align, &end_align, + &max_geom, &max_geom, 1, dev->length); +} + +static int +amiga_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + + if (_ped_partition_attempt_align (part, constraint, + _amiga_get_constraint (part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +amiga_partition_enumerate (PedPartition* part) +{ + int i; + PedPartition* p; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + + /* never change the partition numbers */ + if (part->num != -1) + return 1; + for (i = 1; i <= AMIGA_MAX_PARTITIONS; i++) { + p = ped_disk_get_partition (part->disk, i); + if (!p) { + part->num = i; + return 1; + } + } + + /* failed to allocate a number */ +#ifndef DISCOVER_ONLY + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unable to allocate a partition number.")); +#endif + return 0; +} + +static int +amiga_alloc_metadata (PedDisk* disk) +{ + PedPartition* new_part; + PedConstraint* constraint_any = NULL; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + + constraint_any = ped_constraint_any (disk->dev); + + /* Allocate space for the RDB */ + new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, + 0, MAX_RDB_BLOCK); + if (!new_part) + goto error; + + if (!ped_disk_add_partition (disk, new_part, constraint_any)) { + ped_partition_destroy (new_part); + goto error; + } + + ped_constraint_destroy (constraint_any); + return 1; +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +static int +amiga_get_max_primary_partition_count (const PedDisk* disk) +{ + return AMIGA_MAX_PARTITIONS; +} + +static bool +amiga_get_max_supported_partition_count (const PedDisk* disk, int *max_n) +{ + *max_n = AMIGA_MAX_PARTITIONS; + return true; +} + +#include "pt-common.h" +PT_define_limit_functions (amiga) + +static PedDiskOps amiga_disk_ops = { + clobber: NULL, + write: NULL_IF_DISCOVER_ONLY (amiga_write), + + partition_set_name: amiga_partition_set_name, + partition_get_name: amiga_partition_get_name, + + get_partition_alignment: amiga_get_partition_alignment, + + PT_op_function_initializers (amiga) +}; + +static PedDiskType amiga_disk_type = { + next: NULL, + name: "amiga", + ops: &amiga_disk_ops, + features: PED_DISK_TYPE_PARTITION_NAME +}; + +void +ped_disk_amiga_init () +{ + PED_ASSERT (sizeof (struct AmigaBlock) != 3); + PED_ASSERT (sizeof (struct RigidDiskBlock) != 64); + PED_ASSERT (sizeof (struct PartitionBlock) != 64); + PED_ASSERT (sizeof (struct LinkedBlock) != 5); + PED_ASSERT (sizeof (struct Linked2Block) != 18); + + ped_disk_type_register (&amiga_disk_type); +} + +void +ped_disk_amiga_done () +{ + ped_disk_type_unregister (&amiga_disk_type); +} diff --git a/libparted/labels/sun.c b/libparted/labels/sun.c new file mode 100644 index 0000000..5ed2886 --- /dev/null +++ b/libparted/labels/sun.c @@ -0,0 +1,910 @@ +/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + + libparted - a library for manipulating disk partitions + Copyright (C) 2000-2001, 2005, 2007-2014, 2019-2023 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/>. + + Contributor: Ben Collins <bcollins@debian.org> +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <stdbool.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "misc.h" +#include "pt-tools.h" +#include "verify.h" + +/* Most of this came from util-linux's sun support, which was mostly done + by Jakub Jelinek. */ + +#define SUN_DISK_MAGIC 0xDABE /* Disk magic number */ +#define SUN_DISK_MAXPARTITIONS 8 + +#define SUN_VTOC_VERSION 1 +#define SUN_VTOC_SANITY 0x600DDEEE + +#define WHOLE_DISK_ID 0x05 +#define WHOLE_DISK_PART 2 /* as in 0, 1, 2 (3rd partition) */ +#define LINUX_SWAP_ID 0x82 + +typedef struct _SunRawPartition SunRawPartition; +typedef struct _SunPartitionInfo SunPartitionInfo; +typedef struct _SunRawLabel SunRawLabel; +typedef struct _SunPartitionData SunPartitionData; +typedef struct _SunDiskData SunDiskData; + +struct __attribute__ ((packed)) _SunRawPartition { + u_int32_t start_cylinder; /* where the part starts... */ + u_int32_t num_sectors; /* ...and it's length */ +}; + +struct __attribute__ ((packed)) _SunPartitionInfo { + u_int8_t spare1; + u_int8_t id; /* Partition type */ + u_int8_t spare2; + u_int8_t flags; /* Partition flags */ +}; + +struct __attribute__ ((packed, aligned(2))) _SunRawLabel { + char info[128]; /* Informative text string */ + u_int32_t version; /* Layout version */ + u_int8_t volume[8]; /* Volume name */ + u_int16_t nparts; /* Number of partitions */ + SunPartitionInfo infos[SUN_DISK_MAXPARTITIONS]; + u_int16_t padding; /* Alignment padding */ + u_int32_t bootinfo[3]; /* Info needed by mboot */ + u_int32_t sanity; /* To verify vtoc sanity */ + u_int32_t reserved[10]; /* Free space */ + u_int32_t timestamp[8]; /* Partition timestamp */ + u_int32_t write_reinstruct; /* sectors to skip, writes */ + u_int32_t read_reinstruct; /* sectors to skip, reads */ + u_int8_t spare1[148]; /* Padding */ + u_int16_t rspeed; /* Disk rotational speed */ + u_int16_t pcylcount; /* Physical cylinder count */ + u_int16_t sparecyl; /* extra sects per cylinder */ + u_int8_t spare2[4]; /* More magic... */ + u_int16_t ilfact; /* Interleave factor */ + u_int16_t ncyl; /* Data cylinder count */ + u_int16_t nacyl; /* Alt. cylinder count */ + u_int16_t ntrks; /* Tracks per cylinder */ + u_int16_t nsect; /* Sectors per track */ + u_int8_t spare3[4]; /* Even more magic... */ + SunRawPartition partitions[SUN_DISK_MAXPARTITIONS]; + u_int16_t magic; /* Magic number */ + u_int16_t csum; /* Label xor'd checksum */ +}; + +struct _SunPartitionData { + u_int8_t type; + int is_boot; + int is_root; + int is_lvm; + int is_raid; +}; + +struct _SunDiskData { + PedSector length; /* This is based on cyl - alt-cyl */ + SunRawLabel raw_label; +}; + +static PedDiskType sun_disk_type; + +/* Checksum computation */ +static void +sun_compute_checksum (SunRawLabel *label) +{ + u_int16_t *ush = (u_int16_t *)label; + u_int16_t csum = 0; + + while(ush < (u_int16_t *)(&label->csum)) + csum ^= *ush++; + label->csum = csum; +} + +/* Checksum Verification */ +static int +sun_verify_checksum (SunRawLabel const *label) +{ + u_int16_t *ush = ((u_int16_t *)(label + 1)) - 1; + u_int16_t csum = 0; + + while (ush >= (u_int16_t *)label) + csum ^= *ush--; + + return !csum; +} + +static int +sun_probe (const PedDevice *dev) +{ + PED_ASSERT (dev != NULL); + + void *s0; + if (!ptt_read_sector (dev, 0, &s0)) + return 0; + SunRawLabel const *label = (void const *) s0; + + int ok = 1; + /* check magic */ + if (PED_BE16_TO_CPU (label->magic) != SUN_DISK_MAGIC) { + ok = 0; + } else { +#ifndef DISCOVER_ONLY + if (!sun_verify_checksum(label)) { + ok = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Corrupted Sun disk label detected.")); + } + } +#endif + + free (s0); + return ok; +} + +static PedDisk* +sun_alloc (const PedDevice* dev) +{ + PedDisk* disk; + SunRawLabel* label; + SunDiskData* sun_specific; + const PedCHSGeometry* bios_geom = &dev->bios_geom; + PedSector cyl_size = bios_geom->sectors * bios_geom->heads; + PED_ASSERT (cyl_size != 0); + + disk = _ped_disk_alloc (dev, &sun_disk_type); + if (!disk) + goto error; + + disk->disk_specific = (SunDiskData*) ped_malloc (sizeof (SunDiskData)); + if (!disk->disk_specific) + goto error_free_disk; + sun_specific = (SunDiskData*) disk->disk_specific; + + PED_ASSERT (bios_geom->cylinders == (PedSector) (dev->length / cyl_size)); + sun_specific->length = ped_round_down_to (dev->length, cyl_size); + + label = &sun_specific->raw_label; + memset(label, 0, sizeof(SunRawLabel)); + + /* #gentoo-sparc people agree that nacyl = 0 is the best option */ + label->magic = PED_CPU_TO_BE16 (SUN_DISK_MAGIC); + label->nacyl = 0; + label->pcylcount = PED_CPU_TO_BE16 (bios_geom->cylinders); + label->rspeed = PED_CPU_TO_BE16 (5400); + label->ilfact = PED_CPU_TO_BE16 (1); + label->sparecyl = 0; + label->ntrks = PED_CPU_TO_BE16 (bios_geom->heads); + label->nsect = PED_CPU_TO_BE16 (bios_geom->sectors); + label->ncyl = PED_CPU_TO_BE16 (dev->length / cyl_size); + + label->sanity = PED_CPU_TO_BE32 (SUN_VTOC_SANITY); + label->version = PED_CPU_TO_BE32 (SUN_VTOC_VERSION); + label->nparts = PED_CPU_TO_BE16 (SUN_DISK_MAXPARTITIONS); + + /* Add a whole disk partition at a minimum */ + label->infos[WHOLE_DISK_PART].id = WHOLE_DISK_ID; + label->partitions[WHOLE_DISK_PART].start_cylinder = 0; + label->partitions[WHOLE_DISK_PART].num_sectors = + PED_CPU_TO_BE32(sun_specific->length); + + /* Now a neato string to describe this label */ + snprintf(label->info, sizeof(label->info) - 1, + "GNU Parted Custom cyl %d alt %d hd %d sec %d", + PED_BE16_TO_CPU(label->ncyl), + PED_BE16_TO_CPU(label->nacyl), + PED_BE16_TO_CPU(label->ntrks), + PED_BE16_TO_CPU(label->nsect)); + + sun_compute_checksum(label); + return disk; + +error_free_disk: + _ped_disk_free (disk); +error: + return NULL; +} + +static PedDisk* +sun_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + SunDiskData* new_sun_data; + SunDiskData* old_sun_data = (SunDiskData*) disk->disk_specific; + + new_disk = ped_disk_new_fresh (disk->dev, &sun_disk_type); + if (!new_disk) + return NULL; + + new_sun_data = (SunDiskData*) new_disk->disk_specific; + memcpy (new_sun_data, old_sun_data, sizeof (SunDiskData)); + return new_disk; +} + +static void +sun_free (PedDisk *disk) +{ + free (disk->disk_specific); + _ped_disk_free (disk); +} + +static int +_check_geometry_sanity (PedDisk* disk, SunRawLabel* label) +{ + PedDevice* dev = disk->dev; + + if (PED_BE16_TO_CPU(label->nsect) == dev->hw_geom.sectors && + PED_BE16_TO_CPU(label->ntrks) == dev->hw_geom.heads) + dev->bios_geom = dev->hw_geom; + + if (!!PED_BE16_TO_CPU(label->pcylcount) + * !!PED_BE16_TO_CPU(label->ntrks) + * !!PED_BE16_TO_CPU(label->nsect) == 0) + return 0; + + if (PED_BE16_TO_CPU(label->nsect) != dev->bios_geom.sectors || + PED_BE16_TO_CPU(label->ntrks) != dev->bios_geom.heads) { +#ifndef DISCOVER_ONLY + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The disk CHS geometry (%d,%d,%d) reported " + "by the operating system does not match " + "the geometry stored on the disk label " + "(%d,%d,%d)."), + dev->bios_geom.cylinders, + dev->bios_geom.heads, + dev->bios_geom.sectors, + PED_BE16_TO_CPU(label->pcylcount), + PED_BE16_TO_CPU(label->ntrks), + PED_BE16_TO_CPU(label->nsect)) + == PED_EXCEPTION_CANCEL) + return 0; +#endif + dev->bios_geom.sectors = PED_BE16_TO_CPU(label->nsect); + dev->bios_geom.heads = PED_BE16_TO_CPU(label->ntrks); + dev->bios_geom.cylinders = PED_BE16_TO_CPU(label->pcylcount); + + if (dev->bios_geom.sectors * dev->bios_geom.heads + * dev->bios_geom.cylinders > dev->length) { + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The disk label describes a disk bigger than " + "%s."), + dev->path) + != PED_EXCEPTION_IGNORE) + return 0; + } + } + return 1; +} + +static int +sun_read (PedDisk* disk) +{ + SunPartitionData* sun_data; + SunDiskData* disk_data; + int i; + PedPartition* part; + PedSector end, start, block; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + PED_ASSERT (disk->disk_specific != NULL); + + disk_data = (SunDiskData*) disk->disk_specific; + + ped_disk_delete_all (disk); + + void *s0; + if (!ptt_read_sector (disk->dev, 0, &s0)) + goto error; + + SunRawLabel *label = &disk_data->raw_label; + verify (sizeof (*label) == 512); + memcpy (label, s0, sizeof (*label)); + free (s0); + + if (!_check_geometry_sanity (disk, label)) + goto error; + + block = disk->dev->bios_geom.sectors * disk->dev->bios_geom.heads; + disk_data->length = block * disk->dev->bios_geom.cylinders; + + for (i = 0; i < SUN_DISK_MAXPARTITIONS; i++) { + if (!PED_BE32_TO_CPU(label->partitions[i].num_sectors)) + continue; + if (!label->infos[i].id) + continue; + if (label->infos[i].id == WHOLE_DISK_ID) + continue; + + start = PED_BE32_TO_CPU(label->partitions[i].start_cylinder) + * block; + end = start + + PED_BE32_TO_CPU(label->partitions[i].num_sectors) - 1; + + part = ped_partition_new (disk, PED_PARTITION_NORMAL, NULL, + start, end); + if (!part) + goto error; + + sun_data = part->disk_specific; + sun_data->type = label->infos[i].id; + sun_data->is_boot = sun_data->type == 0x1; + sun_data->is_root = sun_data->type == 0x2; + sun_data->is_lvm = sun_data->type == 0x8e; + sun_data->is_raid = sun_data->type == 0xfd; + + part->num = i + 1; + part->fs_type = ped_file_system_probe (&part->geom); + + PedConstraint *constraint_exact + = ped_constraint_exact (&part->geom); + if (constraint_exact == NULL) + goto error; + bool ok = ped_disk_add_partition (disk, part, constraint_exact); + ped_constraint_destroy (constraint_exact); + if (!ok) + goto error; + } + + return 1; + + error: + return 0; +} + +#ifndef DISCOVER_ONLY +static int +_use_old_info (const PedDisk* disk, const void *sector_0) +{ + SunRawLabel const *old_label = sector_0; + + if (old_label->info[0] + && PED_BE16_TO_CPU (old_label->magic) == SUN_DISK_MAGIC) { + SunDiskData *sun_specific = disk->disk_specific; + memcpy (&sun_specific->raw_label, sector_0, + sizeof (sun_specific->raw_label)); + verify (sizeof (sun_specific->raw_label) == 512); + } + + return 1; +} + +static int +sun_write (const PedDisk* disk) +{ + SunRawLabel* label; + SunPartitionData* sun_data; + SunDiskData* disk_data; + PedPartition* part; + int i; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->dev != NULL); + + void *s0; + if (!ptt_read_sector (disk->dev, 0, &s0)) + return 0; + + /* Calling _use_old_info here in sun_write + above seems wrong, because it modifies *DISK. + FIXME: maybe later. */ + if (!_use_old_info (disk, s0)) { + free (s0); + return 0; + } + + disk_data = (SunDiskData*) disk->disk_specific; + label = &disk_data->raw_label; + + memset (label->partitions, 0, + sizeof (SunRawPartition) * SUN_DISK_MAXPARTITIONS); + memset (label->infos, 0, + sizeof (SunPartitionInfo) * SUN_DISK_MAXPARTITIONS); + + for (i = 0; i < SUN_DISK_MAXPARTITIONS; i++) { + part = ped_disk_get_partition (disk, i + 1); + + if (!part && i == WHOLE_DISK_PART) { + /* Ok, nothing explicitly in the whole disk + partition, so let's put it there for safety + sake. */ + + label->infos[i].id = WHOLE_DISK_ID; + label->partitions[i].start_cylinder = 0; + label->partitions[i].num_sectors = + PED_CPU_TO_BE32(disk_data->length); + continue; + } + if (!part) + continue; + + sun_data = part->disk_specific; + label->infos[i].id = sun_data->type; + label->partitions[i].start_cylinder + = PED_CPU_TO_BE32 (part->geom.start + / (disk->dev->bios_geom.sectors + * disk->dev->bios_geom.heads)); + label->partitions[i].num_sectors + = PED_CPU_TO_BE32 (part->geom.end + - part->geom.start + 1); + } + + /* We assume the harddrive is always right, and that the label may + be wrong. I don't think this will cause any problems, since the + cylinder count is always enforced by our alignment, and we + sanity checked the sectors/heads when we detected the device. The + worst that could happen here is that the drive seems bigger or + smaller than it really is, but we'll have that problem even if we + don't do this. */ + + if (disk->dev->bios_geom.cylinders > 65536) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("The disk has %d cylinders, which is greater than " + "the maximum of 65536."), + disk->dev->bios_geom.cylinders); + } + + label->pcylcount = PED_CPU_TO_BE16 (disk->dev->bios_geom.cylinders); + label->ncyl = PED_CPU_TO_BE16 (disk->dev->bios_geom.cylinders + - PED_BE16_TO_CPU (label->nacyl)); + + sun_compute_checksum (label); + + verify (sizeof *label == 512); + memcpy (s0, label, sizeof *label); + int write_ok = ped_device_write (disk->dev, s0, 0, 1); + free (s0); + + if (write_ok) + return ped_device_sync (disk->dev); + + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +sun_partition_new (const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + SunPartitionData* sun_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (ped_partition_is_active (part)) { + part->disk_specific + = sun_data = ped_malloc (sizeof (SunPartitionData)); + if (!sun_data) + goto error_free_part; + sun_data->type = 0; + sun_data->is_boot = 0; + sun_data->is_root = 0; + sun_data->is_lvm = 0; + sun_data->is_raid = 0; + } else { + part->disk_specific = NULL; + } + + return part; + +error_free_part: + free (part); +error: + return NULL; +} + +static PedPartition* +sun_partition_duplicate (const PedPartition* part) +{ + PedPartition* new_part; + SunPartitionData* new_sun_data; + SunPartitionData* old_sun_data; + + new_part = ped_partition_new (part->disk, part->type, + part->fs_type, part->geom.start, + part->geom.end); + if (!new_part) + return NULL; + new_part->num = part->num; + + old_sun_data = (SunPartitionData*) part->disk_specific; + new_sun_data = (SunPartitionData*) new_part->disk_specific; + new_sun_data->type = old_sun_data->type; + new_sun_data->is_boot = old_sun_data->is_boot; + new_sun_data->is_root = old_sun_data->is_root; + new_sun_data->is_lvm = old_sun_data->is_lvm; + new_sun_data->is_raid = old_sun_data->is_raid; + return new_part; +} + +static void +sun_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL); + + if (ped_partition_is_active (part)) + free (part->disk_specific); + free (part); +} + +static int +sun_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + SunPartitionData* sun_data = part->disk_specific; + + part->fs_type = fs_type; + + if (sun_data->is_boot) { + sun_data->type = 0x1; + return 1; + } + if (sun_data->is_root) { + sun_data->type = 0x2; + return 1; + } + if (sun_data->is_lvm) { + sun_data->type = 0x8e; + return 1; + } + if (sun_data->is_raid) { + sun_data->type = 0xfd; + return 1; + } + + sun_data->type = 0x83; + if (fs_type) { + if (is_linux_swap (fs_type->name)) + sun_data->type = 0x82; + else if (!strcmp (fs_type->name, "ufs")) + sun_data->type = 0x6; + } + + return 1; +} + +static int +sun_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + SunPartitionData* sun_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + PED_ASSERT (ped_partition_is_flag_available (part, flag)); + + sun_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_BOOT: + sun_data->is_boot = state; + if (state) { + sun_data->is_lvm = 0; + sun_data->is_raid = 0; + sun_data->is_root = 0; + } + return ped_partition_set_system (part, part->fs_type); + + case PED_PARTITION_ROOT: + sun_data->is_root = state; + if (state) { + sun_data->is_boot = 0; + sun_data->is_lvm = 0; + sun_data->is_raid = 0; + } + return ped_partition_set_system (part, part->fs_type); + + case PED_PARTITION_LVM: + sun_data->is_lvm = state; + if (state) { + sun_data->is_boot = 0; + sun_data->is_raid = 0; + sun_data->is_root = 0; + } + return ped_partition_set_system (part, part->fs_type); + + case PED_PARTITION_RAID: + sun_data->is_raid = state; + if (state) { + sun_data->is_boot = 0; + sun_data->is_lvm = 0; + sun_data->is_root = 0; + } + return ped_partition_set_system (part, part->fs_type); + + default: + return 0; + } +} + + +static int _GL_ATTRIBUTE_PURE +sun_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + SunPartitionData* sun_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + sun_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_BOOT: + return sun_data->is_boot; + case PED_PARTITION_ROOT: + return sun_data->is_root; + case PED_PARTITION_LVM: + return sun_data->is_lvm; + case PED_PARTITION_RAID: + return sun_data->is_raid; + + default: + return 0; + } +} + + +static int +sun_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_BOOT: + case PED_PARTITION_ROOT: + case PED_PARTITION_LVM: + case PED_PARTITION_RAID: + return 1; + + default: + return 0; + } +} + +static bool +sun_get_max_supported_partition_count (const PedDisk* disk, int *max_n) +{ + *max_n = SUN_DISK_MAXPARTITIONS; + return true; +} + +static int +sun_get_max_primary_partition_count (const PedDisk* disk) +{ + return SUN_DISK_MAXPARTITIONS; +} + +static PedAlignment* +sun_get_partition_alignment(const PedDisk *disk) +{ + PedSector block = + disk->dev->hw_geom.sectors * disk->dev->hw_geom.heads; + + return ped_alignment_new(0, block); +} + +static PedConstraint* +_get_strict_constraint (PedDisk* disk) +{ + PedDevice* dev = disk->dev; + PedAlignment start_align; + PedAlignment end_align; + PedGeometry max_geom; + SunDiskData* disk_data = disk->disk_specific; + PedSector block = dev->bios_geom.sectors * dev->bios_geom.heads; + + if (!ped_alignment_init (&start_align, 0, block)) + return NULL; + if (!ped_alignment_init (&end_align, -1, block)) + return NULL; + if (!ped_geometry_init (&max_geom, dev, 0, disk_data->length)) + return NULL; + + return ped_constraint_new (&start_align, &end_align, &max_geom, + &max_geom, 1, dev->length); +} + +static PedConstraint* +_get_lax_constraint (PedDisk* disk) +{ + PedDevice* dev = disk->dev; + PedAlignment start_align; + PedGeometry max_geom; + SunDiskData* disk_data = disk->disk_specific; + PedSector block = dev->bios_geom.sectors * dev->bios_geom.heads; + + if (!ped_alignment_init (&start_align, 0, block)) + return NULL; + if (!ped_geometry_init (&max_geom, dev, 0, disk_data->length)) + return NULL; + + return ped_constraint_new (&start_align, ped_alignment_any, &max_geom, + &max_geom, 1, dev->length); +} + +/* _get_strict_constraint() will align the partition to the end of the cylinder. + * This isn't required, but since partitions must start at the start of the + * cylinder, space between the end of a partition and the end of a cylinder + * is unusable, so there's no point wasting space! + * However, if they really insist (via constraint)... which they will + * if they're reading a weird table of the disk... then we allow the end to + * be anywhere, with _get_lax_constraint() + */ +static int +sun_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL); + + if (_ped_partition_attempt_align (part, constraint, + _get_strict_constraint (part->disk))) + return 1; + if (_ped_partition_attempt_align (part, constraint, + _get_lax_constraint (part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +sun_partition_enumerate (PedPartition* part) +{ + int i; + PedPartition* p; + + /* never change the partition numbers */ + if (part->num != -1) + return 1; + for (i = 1; i <= SUN_DISK_MAXPARTITIONS; i++) { + /* skip the Whole Disk partition for now */ + if (i == WHOLE_DISK_PART + 1) + continue; + p = ped_disk_get_partition (part->disk, i); + if (!p) { + part->num = i; + return 1; + } + } + +#ifndef DISCOVER_ONLY + /* Ok, now allocate the Whole disk if it isn't already */ + p = ped_disk_get_partition (part->disk, WHOLE_DISK_PART + 1); + if (!p) { + int j = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The Whole Disk partition is the only " + "available one left. Generally, it is not a " + "good idea to overwrite this partition with " + "a real one. Solaris may not be able to " + "boot without it, and SILO (the sparc boot " + "loader) appreciates it as well.")); + if (j == PED_EXCEPTION_IGNORE) { + /* bad bad bad...you will suffer your own fate */ + part->num = WHOLE_DISK_PART + 1; + return 1; + } + } + + /* failed to allocate a number, this means we are full */ + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Sun disk label is full.")); +#endif + return 0; +} + +static int +sun_alloc_metadata (PedDisk* disk) +{ + PedPartition* new_part; + SunDiskData* disk_data; + PedConstraint* constraint_any; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->disk_specific != NULL); + PED_ASSERT (disk->dev != NULL); + + constraint_any = ped_constraint_any (disk->dev); + + /* Sun disk label does not need to allocate a sector. The disk + label is contained within the first 512 bytes, which should not + be overwritten by any boot loader or superblock. It is safe for + most partitions to start at sector 0. We do however, allocate + the space used by alt-cyl's, since we cannot use those. Put them + at the end of the disk. */ + + disk_data = disk->disk_specific; + + if (disk->dev->length <= 0 || + disk_data->length <= 0 || + disk->dev->length == disk_data->length) + goto error; + + new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, + disk_data->length, disk->dev->length - 1); + if (!new_part) + goto error; + + if (!ped_disk_add_partition (disk, new_part, constraint_any)) { + ped_partition_destroy (new_part); + goto error; + } + + ped_constraint_destroy (constraint_any); + return 1; +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +#include "pt-common.h" +PT_define_limit_functions (sun) + +static PedDiskOps sun_disk_ops = { + clobber: NULL, + write: NULL_IF_DISCOVER_ONLY (sun_write), + + get_partition_alignment: sun_get_partition_alignment, + + partition_set_name: NULL, + partition_get_name: NULL, + + PT_op_function_initializers (sun) +}; + +static PedDiskType sun_disk_type = { + next: NULL, + name: "sun", + ops: &sun_disk_ops, + features: 0 +}; + +void +ped_disk_sun_init () +{ + PED_ASSERT (sizeof (SunRawLabel) == 512); + ped_disk_type_register (&sun_disk_type); +} + +void +ped_disk_sun_done () +{ + ped_disk_type_unregister (&sun_disk_type); +} diff --git a/libparted/labels/vtoc.c b/libparted/labels/vtoc.c new file mode 100644 index 0000000..d467164 --- /dev/null +++ b/libparted/labels/vtoc.c @@ -0,0 +1,1329 @@ +#include <config.h> +#include <parted/vtoc.h> + +#ifdef DEBUG_DASD +#define PDEBUG fprintf(stderr, "%s:%d:%s\n", \ + __FILE__, \ + __LINE__, \ + __PRETTY_FUNCTION__); +#else +#define PDEBUG +#endif + +#include <parted/parted.h> + +#include <libintl.h> +#if ENABLE_NLS +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +static const unsigned char EBCtoASC[256] = +{ +/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ + 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, +/* 0x08 -GE -SPS -RPT VT FF CR SO SI */ + 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC + -ENP ->LF */ + 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, +/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB + -IUS */ + 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, +/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC + -INP */ + 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, +/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL + -SW */ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, +/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ + 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, +/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ + 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, +/* 0x40 SP RSP ä ---- */ + 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, +/* 0x48 . < ( + | */ + 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, +/* 0x50 & ---- */ + 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, +/* 0x58 ß ! $ * ) ; */ + 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, +/* 0x60 - / ---- Ä ---- ---- ---- */ + 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, +/* 0x68 ---- , % _ > ? */ + 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, +/* 0x70 --- ---- ---- ---- ---- ---- ---- */ + 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, +/* 0x78 * ` : # @ ' = " */ + 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, +/* 0x80 * a b c d e f g */ + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, +/* 0x88 h i ---- ---- ---- */ + 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, +/* 0x90 ° j k l m n o p */ + 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, +/* 0x98 q r ---- ---- */ + 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, +/* 0xA0 ~ s t u v w x */ + 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, +/* 0xA8 y z ---- ---- ---- ---- */ + 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, +/* 0xB0 ^ ---- § ---- */ + 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, +/* 0xB8 ---- [ ] ---- ---- ---- ---- */ + 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, +/* 0xC0 { A B C D E F G */ + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, +/* 0xC8 H I ---- ö ---- */ + 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, +/* 0xD0 } J K L M N O P */ + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, +/* 0xD8 Q R ---- ü */ + 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, +/* 0xE0 \ S T U V W X */ + 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, +/* 0xE8 Y Z ---- Ö ---- ---- ---- */ + 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, +/* 0xF0 0 1 2 3 4 5 6 7 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, +/* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ + 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 +}; + +static const unsigned char ASCtoEBC[256] = +{ + /*00 NL SH SX EX ET NQ AK BL */ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /*08 BS HT LF VT FF CR SO SI */ + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /*10 DL D1 D2 D3 D4 NK SN EB */ + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x15, 0x32, 0x26, + /*18 CN EM SB EC FS GS RS US */ + 0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F, + /*20 SP ! " # $ % & ' */ + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /*28 ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /*30 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /*38 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /*40 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /*48 H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /*50 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /*58 X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D, + /*60 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /*68 h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /*70 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /*78 x y z { | } ~ DL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xFF +}; + +enum failure { + unable_to_open, + unable_to_seek, + unable_to_write, + unable_to_read +}; + +static char buffer[93]; + +static void +vtoc_error (enum failure why, char const *s1, char const *s2) +{ + PDEBUG + char error[8192]; + + switch (why) { + case unable_to_open: + sprintf(error, "VTOC: %s -- %s\n%s\n", + _("opening of device failed"), s1, s2); + break; + case unable_to_seek: + sprintf(error, "VTOC: %s -- %s\n%s\n", + _("seeking on device failed"), s1, s2); + break; + case unable_to_write: + sprintf(error, "VTOC: %s -- %s\n%s\n", + _("writing to device failed"), s1, s2); + break; + case unable_to_read: + sprintf(error, "VTOC: %s -- %s\n%s\n", + _("reading from device failed"), s1, s2); + break; + default: + sprintf(error, "VTOC: %s\n", _("Fatal error")); + } + + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, error); +} + +char * +vtoc_ebcdic_enc (char const *source, char *target, int l) +{ + PDEBUG + int i; + + for (i = 0; i < l; i++) + target[i]=ASCtoEBC[(unsigned char)(source[i])]; + + return target; +} + +char * +vtoc_ebcdic_dec (char const *source, char *target, int l) +{ + PDEBUG + int i; + + for (i = 0; i < l; i++) + target[i]=EBCtoASC[(unsigned char)(source[i])]; + + return target; +} + +void +vtoc_set_extent (extent_t *ext, u_int8_t typeind, u_int8_t seqno, + cchh_t *lower, cchh_t *upper) +{ + PDEBUG + ext->typeind = typeind; + ext->seqno = seqno; + memcpy(&ext->llimit,lower,sizeof(cchh_t)); + memcpy(&ext->ulimit,upper,sizeof(cchh_t)); +} + +void +vtoc_set_cchh (cchh_t *addr, u_int32_t cc, u_int16_t hh) +{ + PDEBUG + addr->cc = (u_int16_t) cc; + addr->hh = cc >> 16; + addr->hh <<= 4; + addr->hh |= hh; +} + +u_int32_t +vtoc_get_cyl_from_cchh (cchh_t *addr) +{ + u_int32_t cyl; + + /*decode cylinder for large volumes */ + cyl = addr->hh & 0xFFF0; + cyl <<= 12; + cyl |= addr->cc; + return cyl; +} + +u_int16_t +vtoc_get_head_from_cchh (cchh_t *addr) +{ + /* decode heads for large volumes */ + return addr->hh & 0x000F; +} + +static void +vtoc_set_ttr (ttr_t *addr, u_int16_t tt, u_int8_t r) +{ + PDEBUG + addr->tt = tt; + addr->r = r; +} + +void +vtoc_set_cchhb (cchhb_t *addr, u_int32_t cc, u_int16_t hh, u_int8_t b) +{ + PDEBUG + addr->cc = (u_int16_t) cc; + addr->hh = cc >> 16; + addr->hh <<= 4; + addr->hh |= hh; + addr->b = b; +} + +u_int32_t +vtoc_get_cyl_from_cchhb(cchhb_t *addr) +{ + u_int32_t cyl; + + /* decode cylinder for large volumes */ + cyl = addr->hh & 0xFFF0; + cyl <<= 12; + cyl |= addr->cc; + return cyl; +} + +u_int16_t +vtoc_get_head_from_cchhb(cchhb_t *addr) +{ + /* decode heads for large volumes */ + return addr->hh & 0x000F; +} + +/* + * some functions to convert cyl-cyl-head-head addresses to + * block or track numbers + * Note: Record zero is special, so first block on a track is + * in record 1! + */ +u_int64_t +cchhb2blk (cchhb_t *p, struct fdasd_hd_geometry *geo) +{ + return (u_int64_t) vtoc_get_cyl_from_cchhb(p) * + geo->heads * geo->sectors + + vtoc_get_head_from_cchhb(p) * geo->sectors + + p->b; +} + +u_int64_t +cchh2blk (cchh_t *p, struct fdasd_hd_geometry *geo) +{ + return (u_int64_t) vtoc_get_cyl_from_cchh(p) * + geo->heads * geo->sectors + + vtoc_get_head_from_cchh(p) * geo->sectors; +} + +u_int32_t +cchh2trk (cchh_t *p, struct fdasd_hd_geometry *geo) +{ + return vtoc_get_cyl_from_cchh(p) * geo->heads + + vtoc_get_head_from_cchh(p); +} + +void +vtoc_set_date (labeldate_t * d, u_int8_t year, u_int16_t day) +{ + PDEBUG + d->year = year; + d->day = day; +} + +/* + * initializes the volume label with EBCDIC spaces + */ +void +vtoc_volume_label_init (volume_label_t *vlabel) +{ + PDEBUG + sprintf(buffer, "%92s", " "); + vtoc_ebcdic_enc(buffer, buffer, sizeof *vlabel); + memcpy(vlabel, buffer, sizeof *vlabel); +} + +/* + * reads the volume label from dasd + */ +int +vtoc_read_volume_label (int f, unsigned long vlabel_start, + volume_label_t *vlabel) +{ + + char str[5]; + unsigned long block_zero; + typedef struct bogus_label bogus_label_t; + typedef union vollabel vollabel_t; + + union __attribute__((packed)) vollabel { + /* cdl and ldl have the same data struct */ + volume_label_t cdl; + cms_volume_label_t cms; + }; + + struct __attribute__((packed)) bogus_label { + char overhead[512]; + vollabel_t actual_label; + }; + + bogus_label_t mybogus; + bogus_label_t *bogus_ptr = &mybogus; + vollabel_t *union_ptr = &bogus_ptr->actual_label; + volume_label_t *cdl_ptr = &union_ptr->cdl; + + PDEBUG + int rc; + + if (lseek(f, vlabel_start, SEEK_SET) == -1) { + vtoc_error(unable_to_seek, "vtoc_read_volume_label", + _("Could not read volume label.")); + return 1; + } + + rc = read(f, vlabel, sizeof(volume_label_t)); + if (rc != sizeof(volume_label_t)) { + vtoc_error(unable_to_read, "vtoc_read_volume_label", + _("Could not read volume label.")); + return 1; + } + + if (strncmp(vlabel->volkey, vtoc_ebcdic_enc("VOL1", str, 4), 4) == 0 + || strncmp(vlabel->volkey, vtoc_ebcdic_enc("LNX1", str, 4), 4) == 0 + || strncmp(vlabel->volkey, vtoc_ebcdic_enc("CMS1", str, 4), 4) == 0) + return 0; + + /* + If we didn't find a valid volume label, there is a special case + we must try before we give up. For a CMS-formatted disk on FBA + DASD using the DIAG driver and a block size greater than 512, we + must read the block at offset 0, then look for a label within + that block at offset 512. + */ + + block_zero = 0; + + if (lseek(f, block_zero, SEEK_SET) == -1) { + vtoc_error(unable_to_seek, "vtoc_read_volume_label", + _("Could not read volume label.")); + return 1; + } + + rc = read(f, bogus_ptr, sizeof(bogus_label_t)); + if (rc != sizeof(bogus_label_t)) { + vtoc_error(unable_to_read, "vtoc_read_volume_label", + _("Could not read volume label.")); + return 1; + } + + memcpy(vlabel, cdl_ptr, sizeof *vlabel); + return 0; +} + +/* + * writes the volume label to dasd + */ +int +vtoc_write_volume_label (int f, unsigned long vlabel_start, + volume_label_t const *vlabel) +{ + PDEBUG + int rc; + + if (lseek(f, vlabel_start, SEEK_SET) == -1) + vtoc_error(unable_to_seek, "vtoc_write_volume_label", + _("Could not write volume label.")); + + rc = write(f, vlabel, sizeof(volume_label_t)); + if (rc != sizeof(volume_label_t)) + vtoc_error(unable_to_write, "vtoc_write_volume_label", + _("Could not write volume label.")); + + return 0; +} + +/* + * takes a string as input, converts it to uppercase, translates + * it to EBCDIC and fills it up with spaces before it copies it + * as volume serial to the volume label + */ +void +vtoc_volume_label_set_volser (volume_label_t *vlabel, char const *volser) +{ + PDEBUG + int j, i = strlen(volser); + char s[VOLSER_LENGTH + 1]; + + strcpy(s, " "); + vtoc_ebcdic_enc(s, s, VOLSER_LENGTH); + strncpy(vlabel->volid, s, VOLSER_LENGTH); + + if (i > VOLSER_LENGTH) + i = VOLSER_LENGTH; + + strncpy(s, volser, i); + for (j=0; j<i; j++) + s[j] = toupper(s[j]); + + s[VOLSER_LENGTH] = 0x00; + vtoc_ebcdic_enc(s, s, i); + strncpy(vlabel->volid, s, i); + + return; +} + +/* + * returns the volume serial number right after it is translated + * to ASCII + */ +char * +vtoc_volume_label_get_volser (volume_label_t *vlabel, char *volser) +{ + PDEBUG + vtoc_ebcdic_dec(vlabel->volid, volser, VOLSER_LENGTH); + + return volser; +} + +/* + * sets the volume label key right after + * it has been translated to EBCDIC + */ +void +vtoc_volume_label_set_key (volume_label_t *vlabel, char const *key) +{ + PDEBUG + char s[4]; + + vtoc_ebcdic_enc(key, s, 4); + strncpy(vlabel->volkey, s, 4); + + return; +} + +/* + * sets the volume label identifier right + * after it has been translated to EBCDIC + */ +void +vtoc_volume_label_set_label (volume_label_t *vlabel, char const *lbl) +{ + PDEBUG + char s[4]; + + vtoc_ebcdic_enc(lbl, s, 4); + strncpy(vlabel->vollbl, s, 4); + + return; +} + +/* + * returns the volume label key = the label identifier + * right after it has been translated to ASCII + */ +char * +vtoc_volume_label_get_label (volume_label_t *vlabel, char *lbl) +{ + PDEBUG + vtoc_ebcdic_dec(vlabel->vollbl, lbl, 4); + + return lbl; +} + +/* + * reads either a format4 label or a format1 label + * from the specified position + */ +void +vtoc_read_label (int f, unsigned long position, format1_label_t *f1, + format4_label_t *f4, format5_label_t *f5, format7_label_t *f7) +{ + PDEBUG + int t; + + if (lseek(f, position, SEEK_SET) == -1) + vtoc_error(unable_to_seek, "vtoc_read_label", + _("Could not read VTOC labels.")); + + if (f1 != NULL) { + t = sizeof(format1_label_t); + if (read(f, f1, t) != t) + vtoc_error(unable_to_read, "vtoc_read_label", + _("Could not read VTOC FMT1 DSCB.")); + } + + if (f4 != NULL) { + t = sizeof(format4_label_t); + if (read(f, f4, t) != t) + vtoc_error(unable_to_read, "vtoc_read_label", + _("Could not read VTOC FMT4 DSCB.")); + } + + if (f5 != NULL) { + t = sizeof(format5_label_t); + if (read(f, f5, t) != t) + vtoc_error(unable_to_read, "vtoc_read_label", + _("Could not read VTOC FMT5 DSCB.")); + } + + if (f7 != NULL) { + t = sizeof(format7_label_t); + if (read(f, f7, t) != t) + vtoc_error(unable_to_read, "vtoc_read_label", + _("Could not read VTOC FMT7 DSCB.")); + } +} + +/* + * writes either a FMT1, FMT4 or FMT5 label + * to the specified position + */ +void +vtoc_write_label (int f, unsigned long position, + format1_label_t const *f1, + format4_label_t const *f4, + format5_label_t const *f5, + format7_label_t const *f7, + format9_label_t const *f9) +{ + PDEBUG + int t; + + if (lseek(f, position, SEEK_SET) == -1) + vtoc_error(unable_to_seek, "vtoc_write_label", + _("Could not write VTOC labels.")); + + if (f1 != NULL) { + t = sizeof(format1_label_t); + if (write(f, f1, t) != t) + vtoc_error(unable_to_write, "vtoc_write_label", + _("Could not write VTOC FMT1 DSCB.")); + } + + if (f4 != NULL) { + t = sizeof(format4_label_t); + if (write(f, f4, t) != t) + vtoc_error(unable_to_write, "vtoc_write_label", + _("Could not write VTOC FMT4 DSCB.")); + } + + if (f5 != NULL) { + t = sizeof(format5_label_t); + if (write(f, f5, t) != t) + vtoc_error(unable_to_write, "vtoc_write_label", + _("Could not write VTOC FMT5 DSCB.")); + } + + if (f7 != NULL) { + t = sizeof(format7_label_t); + if (write(f, f7, t) != t) + vtoc_error(unable_to_write, "vtoc_write_label", + _("Could not write VTOC FMT7 DSCB.")); + } + + if (f9 != NULL) + { + t = sizeof(format9_label_t); + if (write(f, f9, t) != t) + { + close(f); + vtoc_error(unable_to_write, "vtoc_write_label", + _("Could not write VTOC FMT9 DSCB.")); + } + } +} + +/* + * initializes a format4 label + */ +void +vtoc_init_format4_label (format4_label_t *f4, + unsigned int compat_cylinders, + unsigned int real_cylinders, unsigned int tracks, + unsigned int blocks, unsigned int blksize, + u_int16_t dev_type) +{ + PDEBUG + int i; + + cchh_t lower = {VTOC_START_CC, VTOC_START_HH}; + cchh_t upper = {VTOC_START_CC, VTOC_START_HH}; + + for (i=0; i<44; i++) f4->DS4KEYCD[i] = 0x04; + f4->DS4IDFMT = 0xf4; + + vtoc_set_cchhb(&f4->DS4HPCHR, 0x0000, 0x0000, 0x00); + f4->DS4DSREC = blocks - 2; + /* free space starts right behind VTOC + vtoc_set_cchh(&f4->DS4HCCHH, VTOC_START_CC, VTOC_START_HH + 1);*/ + vtoc_set_cchh(&f4->DS4HCCHH, 0x0000, 0x0000); + f4->DS4NOATK = 0x0000; + f4->DS4VTOCI = 0x00; + f4->DS4NOEXT = 0x01; + f4->DS4SMSFG = 0x00; + f4->DS4DEVAC = 0x00; + + /* -- begin f4->DS4DEVCT -- */ + f4->DS4DEVCT.DS4DSCYL = compat_cylinders; + f4->DS4DEVCT.DS4DSTRK = tracks; + + switch (dev_type) { + case DASD_3380_TYPE: + f4->DS4DEVCT.DS4DEVTK = DASD_3380_VALUE; + break; + case DASD_3390_TYPE: + f4->DS4DEVCT.DS4DEVTK = DASD_3390_VALUE; + break; + case DASD_9345_TYPE: + f4->DS4DEVCT.DS4DEVTK = DASD_9345_VALUE; + break; + default: + f4->DS4DEVCT.DS4DEVTK = blocks * blksize; + } + + f4->DS4DEVCT.DS4DEVI = 0x00; + f4->DS4DEVCT.DS4DEVL = 0x00; + f4->DS4DEVCT.DS4DEVK = 0x00; + f4->DS4DEVCT.DS4DEVFG = 0x30; + f4->DS4DEVCT.DS4DEVTL = 0x0000; + f4->DS4DEVCT.DS4DEVDT = blocks; + f4->DS4DEVCT.DS4DEVDB = 0x00; + /* -- end f4->DS4DEVCT -- */ + + bzero(f4->DS4AMTIM, sizeof(f4->DS4AMTIM)); + bzero(f4->DS4AMCAT, sizeof(f4->DS4AMCAT)); + bzero(f4->DS4R2TIM, sizeof(f4->DS4R2TIM)); + bzero(f4->res1, sizeof(f4->res1)); + bzero(f4->DS4F6PTR, sizeof(f4->DS4F6PTR)); + + /* -- begin f4lbl->DS4VTOCE -- */ + vtoc_set_extent(&f4->DS4VTOCE, 0x01, 0x00, &lower, &upper); + /* -- end f4lbl->DS4VTOCE -- */ + + bzero(f4->res2, sizeof(f4->res2)); + f4->DS4EFLVL = 0x00; + bzero(&f4->DS4EFPTR, sizeof(f4->DS4EFPTR)); + bzero(&f4->res3, sizeof(f4->res3)); + f4->DS4DCYL = real_cylinders; + bzero(f4->res4, sizeof(f4->res4)); + f4->DS4DEVF2 = 0x40; /* allow format 8 and 9 labels */ + bzero(&f4->res5, sizeof(f4->res5)); +} + +/* + * initializes a format5 label + */ +void +vtoc_init_format5_label (format5_label_t *f5) +{ + PDEBUG + int i; + + bzero(f5, sizeof(format5_label_t)); + for (i=0; i<4; i++) + f5->DS5KEYID[i] = 0x05; + f5->DS5FMTID = 0xf5; +} + +/* + * initializes a format7 label + */ +void +vtoc_init_format7_label (format7_label_t *f7) +{ + PDEBUG + int i; + + bzero(f7, sizeof(format7_label_t)); + for (i=0; i<4; i++) + f7->DS7KEYID[i] = 0x07; + f7->DS7FMTID = 0xf7; +} + +/* + * helper function that initializes a most parts of a + * format1 or format 8 label, all but the field DS1FMTID + */ +void +vtoc_init_format_1_8_label (unsigned int blksize, + extent_t *part_extent, format1_label_t *f1) +{ + PDEBUG + struct tm * creatime; + time_t t; + char str[80]; + + /* get actual date */ + t = time(NULL); + creatime = gmtime(&t); + + bzero(f1->DS1DSNAM, sizeof(f1->DS1DSNAM)); + sprintf(str, "PART .NEW "); + vtoc_ebcdic_enc(str, str, 44); + strncpy(f1->DS1DSNAM, str, 44); + strncpy(f1->DS1DSSN, " ", 6); + f1->DS1VOLSQ = 0x0001; + + vtoc_set_date(&f1->DS1CREDT, (u_int8_t) creatime->tm_year, + (u_int16_t) creatime->tm_yday); + /* expires never - 99 365 */ + vtoc_set_date(&f1->DS1EXPDT, 0x63, 0x016D); + f1->DS1NOEPV = 0x01; + f1->DS1NOBDB = 0x00; + f1->DS1FLAG1 = 0x00; + vtoc_ebcdic_enc("IBM LINUX ", str, 13); + strncpy(f1->DS1SYSCD, str, 13); + vtoc_set_date(&f1->DS1REFD, (u_int8_t) creatime->tm_year, + (u_int16_t) creatime->tm_yday); + f1->DS1SMSFG = 0x00; + f1->DS1SCXTF = 0x00; + f1->DS1SCXTV = 0x0000; + f1->DS1DSRG1 = 0x00; + f1->DS1DSRG2 = 0x00; + f1->DS1RECFM = 0x88; + f1->DS1OPTCD = 0x00; + f1->DS1BLKL = blksize; + f1->DS1LRECL = blksize; + f1->DS1KEYL = 0x00; + f1->DS1RKP = 0x0000; + f1->DS1DSIND = 0x80; /* last volume for this dataset */ + f1->DS1SCAL1 = 0x80; + bzero(&f1->DS1SCAL3, sizeof(f1->DS1SCAL3)); + vtoc_set_ttr(&f1->DS1LSTAR, 0x0000, 0x00); + f1->DS1TRBAL = 0x00; + bzero(&f1->res1, sizeof(f1->res1)); + memcpy(&f1->DS1EXT1, part_extent, sizeof(extent_t)); + bzero(&f1->DS1EXT2, sizeof(extent_t)); + bzero(&f1->DS1EXT3, sizeof(extent_t)); + vtoc_set_cchhb(&f1->DS1PTRDS, 0x0000, 0x0000, 0x00); +} + +void +vtoc_init_format1_label (unsigned int blksize, + extent_t *part_extent, format1_label_t *f1) +{ + vtoc_init_format_1_8_label(blksize, part_extent, f1); + f1->DS1FMTID = 0xf1; +} + +void +vtoc_init_format8_label (unsigned int blksize, + extent_t *part_extent, format1_label_t *f8) +{ + vtoc_init_format_1_8_label(blksize, part_extent, f8); + f8->DS1FMTID = 0xf8; +} + +void +vtoc_update_format8_label (cchhb_t *associated_f9, format1_label_t *f8) +{ + memcpy(&f8->DS1PTRDS, associated_f9, sizeof(*associated_f9)); +} + +void +vtoc_init_format9_label (format9_label_t *f9) +{ + f9->DS9KEYID = 0x09; + f9->DS9SUBTY = 0x01; + f9->DS9NUMF9 = 1; + f9->DS9FMTID = 0xf9; +} + +/* + * do some updates to the VTOC format4 label + */ +void +vtoc_update_format4_label (format4_label_t *f4, cchhb_t *highest_f1, + u_int16_t unused_update) +{ + PDEBUG + /* update highest address of a format 1 label */ + memcpy(&f4->DS4HPCHR, highest_f1, sizeof(cchhb_t)); + + /* update unused DSCB count */ + f4->DS4DSREC = unused_update; +} + +/* + * reorganizes all extents within a FMT5 label + */ +static void +vtoc_reorganize_FMT5_extents (format5_label_t *f5) +{ + PDEBUG + ds5ext_t *ext, *last, tmp; + int i, j; + + for (i=0; i<26; i++) { + if (i==0) + last = &f5->DS5AVEXT; + else if ((i > 0) && (i < 8)) + last = &f5->DS5EXTAV[i-1]; + else + last = &f5->DS5MAVET[i-8]; + + for (j=i; j<26; j++) { + if (j==0) + ext = &f5->DS5AVEXT; + else if ((j > 0) && (j < 8)) + ext = &f5->DS5EXTAV[j-1]; + else + ext = &f5->DS5MAVET[j-8]; + + if (((ext->t > 0) && (last->t == 0)) || + ((ext->t > 0) && (ext->t < last->t))) + { + tmp.t = last->t; + tmp.fc = last->fc; + tmp.ft = last->ft; + last->t = ext->t; + last->fc = ext->fc; + last->ft = ext->ft; + ext->t = tmp.t; + ext->fc = tmp.fc; + ext->ft = tmp.ft; + } + } + } +} + +/* + * add a free space extent description to the VTOC FMT5 DSCB + */ +void +vtoc_update_format5_label_add (format5_label_t *f5, int verbose, + int trk, u_int16_t a, u_int16_t b, u_int8_t c) +{ + PDEBUG + ds5ext_t *ext = NULL, *tmp = NULL; + int i; + + for (i=0; i<26; i++) { + if (i==0) + ext = &f5->DS5AVEXT; + else if ((i > 0) && (i < 8)) + ext = &f5->DS5EXTAV[i-1]; + else + ext = &f5->DS5MAVET[i-8]; + + if (((a < ext->t) && (a + b*trk + c > ext->t)) || + ((a > ext->t) && (ext->t + ext->fc*trk + ext->ft > a))) + { + puts ("BUG: overlapping free space extents " + "in FMT5 DSCB!\nexiting..."); + exit(EXIT_FAILURE); + } + + if ((ext->t + ext->fc + ext->ft) == 0x0000) { + ext->t = a; + ext->fc = b; + ext->ft = c; + tmp = ext; + if (verbose) + puts ("FMT5 add extent: add new extent"); + break; + } + } + + if (tmp == NULL) { + /* BUG: no free extent found */ + puts ("BUG: no free FMT5 DSCB extent found!\nexiting..."); + exit(EXIT_FAILURE); + } + + for (i=0; i<26; i++) { + if (i==0) + ext = &f5->DS5AVEXT; + else if ((i > 0) && (i < 8)) + ext = &f5->DS5EXTAV[i-1]; + else + ext = &f5->DS5MAVET[i-8]; + + if ((ext->t + ext->fc + ext->ft) == 0x0000) + continue; + + if ((ext->t + ext->fc*trk + ext->ft) == tmp->t) { + /* this extent precedes the new one */ + ext->fc += (tmp->fc + (tmp->ft + ext->ft)/trk); + ext->ft = (tmp->ft + ext->ft) % trk; + bzero(tmp, sizeof(ds5ext_t)); + tmp = ext; + + if (verbose) + puts ("FMT5 add extent: " + "merge with predecessor"); + + i = -1; + continue; + } + + if ((tmp->t + tmp->fc*trk + tmp->ft) == ext->t) { + /* this extent succeeds the new one */ + ext->t = tmp->t; + ext->fc += (tmp->fc + (tmp->ft + ext->ft)/trk); + ext->ft = (tmp->ft + ext->ft) % trk; + bzero(tmp, sizeof(ds5ext_t)); + tmp = ext; + + if (verbose) + puts ("FMT5 add extent: " + "merge with successor"); + + i = -1; + continue; + } + } +} + +/* + * remove a free space extent description from the VTOC FMT5 DSCB + */ +void +vtoc_update_format5_label_del (format5_label_t *f5, int verbose, + int trk, u_int16_t a, u_int16_t b, u_int8_t c) +{ + PDEBUG + ds5ext_t *ext; + int i, counter=0; + + for (i=0; i<26; i++) { + if (i==0) + ext = &f5->DS5AVEXT; + else if ((i > 0) && (i < 8)) + ext = &f5->DS5EXTAV[i-1]; + else + ext = &f5->DS5MAVET[i-8]; + + if ((a == ext->t) && (b == ext->fc) && (c == ext->ft)) { + /* fills up whole free space gap */ + bzero(ext, sizeof(ds5ext_t)); + + if (verbose) + puts ("FMT5 del extent: fills whole gap"); + + counter++; + break; + } + + if ((a == ext->t) && ((b < ext->fc) || (c < ext->ft))) { + /* left-bounded in free space gap */ + ext->t = ext->t + b*trk + c; + + if (c > ext->ft) { + ext->fc -= (b + 1); + ext->ft -= (c - trk); + } else { + ext->fc -= b; + ext->ft -= c; + } + + if (verbose) + puts ("FMT5 del extent: left bounded"); + + counter++; + break; + } + + if ((ext->t < a) + && ((ext->t + ext->fc*trk + ext->ft) == (a + b*trk + c))) + { + /* right-bounded in free space gap */ + if (c > ext->ft) { + ext->fc -= (b + 1); + ext->ft -= (c - trk); + } else { + ext->fc -= b; + ext->ft -= c; + } + + if (verbose) + puts ("FMT5 del extent: right bounded"); + + counter++; + break; + } + + if ((a > ext->t) + && ((ext->t + ext->fc*trk + ext->ft) > (a + b*trk + c))) + { + /* partition devides free space into 2 pieces */ + u_int16_t x = a + b*trk + c; + u_int16_t w,y; + u_int8_t z; + + w = (ext->t + ext->fc*trk + ext->ft) - (a + b*trk + c); + y = w / trk; + z = w % trk; + + ext->fc = (a - ext->t) / trk; + ext->ft = (a - ext->t) % trk; + + vtoc_update_format5_label_add(f5, verbose, + trk, x, y, z); + + if (verbose) + puts ("FMT5 del extent: 2 pieces"); + + counter++; + break; + } + + if ((a < ext->t) && (a + b*trk + c > ext->t) + && (a + b*trk + c < ext->t + ext->fc*trk + ext->ft)) + { + puts ("BUG: corresponding free space extent " + "doesn't match free space currently shown " + "in FMT5 DSCB!\nexiting..."); + exit(EXIT_FAILURE); + } + + if ((a > ext->t) && (a < ext->t + ext->fc*trk + ext->ft) + && (a + b*trk + c > ext->t + ext->fc*trk + ext->ft)) + { + puts ("BUG: specified free space extent for " + "deleting doesn't match free space " + "currently shown in FMT5 DSCB!\n" + "exiting..."); + exit(EXIT_FAILURE); + } + } + + if (counter > 0) + return; + + puts ("BUG: specified free space extent for " + "deleting not found in FMT5 DSCB!\n" + "exiting..."); + exit(EXIT_FAILURE); +} + +/* + * reorganizes all extents within a FMT7 label + */ +static void +vtoc_reorganize_FMT7_extents (format7_label_t *f7) +{ + PDEBUG + ds7ext_t *ext, *last, tmp; + int i, j; + + for (i=0; i<16; i++) { + if (i<5) + last = &f7->DS7EXTNT[i]; + else + last = &f7->DS7ADEXT[i-5]; + + for (j=i; j<16; j++) { + if (j<5) + ext = &f7->DS7EXTNT[j]; + else + ext = &f7->DS7ADEXT[j-5]; + + if (((ext->a > 0) && (last->a == 0)) + || ((ext->a > 0) && (ext->a < last->a))) + { + tmp.a = last->a; + tmp.b = last->b; + last->a = ext->a; + last->b = ext->b; + ext->a = tmp.a; + ext->b = tmp.b; + } + } + } +} + +/* + * add a free space extent description to the VTOC FMT7 DSCB + */ +void +vtoc_update_format7_label_add (format7_label_t *f7, int verbose, + u_int32_t a, u_int32_t b) +{ + PDEBUG + ds7ext_t *ext = NULL, *tmp = NULL; + int i; + + for (i=0; i<16; i++) { + if (i<5) + ext = &f7->DS7EXTNT[i]; + else + ext = &f7->DS7ADEXT[i-5]; + + if (((a < ext->a) && (b > ext->a) && (b < ext->b)) + || ((a > ext->a) && (a < ext->b) && (b > ext->b))) + { + puts ("BUG: overlapping free space extents " + "in FMT7 DSCB!\nexiting..."); + exit(EXIT_FAILURE); + } + + if ((ext->a + ext->b) == 0x00000000) { + ext->a = a; + ext->b = b; + tmp = ext; + + if (verbose) + puts ("FMT7 add extent: add new extent"); + + break; + } + } + + if (tmp == NULL) { + /* BUG: no free extent found */ + puts ("BUG: no free FMT7 DSCB extent found!\nexiting..."); + exit(EXIT_FAILURE); + } + + for (i=0; i<16; i++) { + if (i<5) + ext = &f7->DS7EXTNT[i]; + else + ext = &f7->DS7ADEXT[i-5]; + + if ((ext->a + ext->b) == 0x00000000) + continue; + + if (ext->b == tmp->a) { + /* this extent precedes the new one */ + ext->b = tmp->b; + bzero(tmp, sizeof(ds7ext_t)); + tmp = ext; + + if (verbose) + puts ("FMT7 add extent: " + "merge with predecessor"); + + i = -1; + continue; + } + + if (ext->a == tmp->b) { + /* this extent succeeds the new one */ + ext->a = tmp->a; + bzero(tmp, sizeof(ds7ext_t)); + tmp = ext; + + if (verbose) + puts ("FMT7 add extent: merge with successor"); + + i = -1; + continue; + } + } +} + +/* + * remove a free space extent description from the VTOC FMT7 DSCB + */ +void +vtoc_update_format7_label_del (format7_label_t *f7, int verbose, + u_int32_t a, u_int32_t b) +{ + PDEBUG + ds7ext_t *ext; + int i, counter=0; + + for (i=0; i<16; i++) { + if (i<5) + ext = &f7->DS7EXTNT[i]; + else + ext = &f7->DS7ADEXT[i-5]; + + if ((a == ext->a) && (b == ext->b)) { + /* fills up whole free space gap */ + bzero(ext, sizeof(ds7ext_t)); + + if (verbose) + puts ("FMT7 del extent: fills whole gap"); + + counter++; + break; + } + + if ((a == ext->a) && (b < ext->b)) { + /* left-bounded in free space gap */ + ext->a = b; + + if (verbose) + puts ("FMT7 add extent: left-bounded"); + + counter++; + break; + } + + if ((a > ext->a) && (b == ext->b)) { + /* right-bounded in free space gap */ + ext->b = a; + + if (verbose) + puts ("FMT7 add extent: right-bounded"); + + counter++; + break; + } + + if ((a > ext->a) && (b < ext->b)) { + /* partition devides free space into 2 pieces */ + vtoc_update_format7_label_add(f7, verbose, b, ext->b); + ext->b = a; + + if (verbose) + puts ("FMT7 add extent: 2 pieces"); + + counter++; + break; + } + + if (((a < ext->a) && (b > ext->a)) || ((a < ext->b) && (b > ext->b))) { + puts ("BUG: specified free space extent for deleting " + "doesn't match free space currently shown in " + "FMT7 DSCB!\nexiting..."); + printf ("%d %d %d %d\n", a, b, ext->a, ext->b); + exit(EXIT_FAILURE); + } + } + + if (counter > 0) + return; + + puts ("BUG: specified free space extent for " + "deleting not found in FMT7 DSCB!\n" + "exiting..."); + exit(EXIT_FAILURE); +} + +void +vtoc_set_freespace(format4_label_t *f4, format5_label_t *f5, + format7_label_t *f7, char ch, int verbose, + u_int32_t start, u_int32_t stop, u_int32_t cyl, + u_int32_t trk) +{ + PDEBUG + if ((cyl * trk) > BIG_DISK_SIZE) { + if (ch == '+') + vtoc_update_format7_label_add(f7, verbose, start, + /* ds7ext RTA + 1 */ + stop + 1); + else if (ch == '-') + vtoc_update_format7_label_del(f7, verbose, start, + /* ds7ext RTA + 1 */ + stop + 1); + else + puts ("BUG: syntax error in vtoc_set_freespace call"); + + vtoc_reorganize_FMT7_extents (f7); + + f4->DS4VTOCI = 0xa0; + f4->DS4EFLVL = 0x07; + vtoc_set_cchhb(&f4->DS4EFPTR, 0x0000, 0x0001, 0x03); + } else { + u_int16_t x,y; + u_int8_t z; + + x = (u_int16_t) start; + y = (u_int16_t) ((stop - start + 1) / trk); + z = (u_int8_t) ((stop - start + 1) % trk); + + if (ch == '+') + vtoc_update_format5_label_add(f5, verbose, trk, x, y, z); + else if (ch == '-') + vtoc_update_format5_label_del(f5, verbose, trk, x, y, z); + else + puts ("BUG: syntax error in vtoc_set_freespace call"); + + vtoc_reorganize_FMT5_extents (f5); + } +} |