diff options
Diffstat (limited to '')
146 files changed, 58486 insertions, 0 deletions
diff --git a/libparted-fs-resize.pc.in b/libparted-fs-resize.pc.in new file mode 100644 index 0000000..ed9b3d6 --- /dev/null +++ b/libparted-fs-resize.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libparted-fs-resize +Description: The GNU Parted filesystem resize shared library +Version: @VERSION@ +Libs: -L${libdir} -lparted-fs-resize +Cflags: -I${includedir} diff --git a/libparted.pc.in b/libparted.pc.in new file mode 100644 index 0000000..3603926 --- /dev/null +++ b/libparted.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libparted +Description: The GNU Parted disk partitioning shared library +Version: @VERSION@ +Libs: -L${libdir} -lparted +Cflags: -I${includedir} diff --git a/libparted/Makefile.am b/libparted/Makefile.am new file mode 100644 index 0000000..db29a18 --- /dev/null +++ b/libparted/Makefile.am @@ -0,0 +1,65 @@ +# This file is part of GNU Parted +# Copyright (C) 1999-2001, 2007, 2009-2014, 2019-2023 Free Software Foundation, +# Inc. +# +# This file may be modified and/or distributed without restriction. + +SUBDIRS_CHECK = +if HAVE_CHECK +SUBDIRS_CHECK += tests +endif + +ARCH_SOURCE = arch/$(OS).c + +AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS) + +SUBDIRS = labels fs . $(SUBDIRS_CHECK) + + +partedincludedir = \ + -I$(top_srcdir)/lib -I$(top_builddir)/include -I$(top_srcdir)/include +lib_LTLIBRARIES = libparted.la + +# Set the shared library version, per Libtool's guidelines. +# For details, see the "Updating library version information" section of +# "info libtool". +CURRENT = 2 +REVISION = 5 +AGE = 0 + +libparted_la_LDFLAGS = -version-info $(CURRENT):$(REVISION):$(AGE) + +libparted_la_SOURCES = debug.c \ + architecture.c \ + architecture.h \ + device.c \ + exception.c \ + filesys.c \ + libparted.c \ + timer.c \ + unit.c \ + disk.c \ + cs/geom.c \ + cs/constraint.c \ + cs/natmath.c \ + $(ARCH_SOURCE) + + +EXTRA_libparted_la_SOURCES = arch/linux.c \ + arch/linux.h \ + arch/gnu.c \ + arch/beos.c + +libparted_la_LIBADD = \ + fs/libfs.la \ + labels/liblabels.la \ + $(top_builddir)/lib/libgnulib.la \ + $(OS_LIBS) \ + $(DM_LIBS) \ + $(LIB_BLKID) \ + $(UUID_LIBS) \ + $(INTLLIBS) + +EXTRA_DIST = mbr.s + +AM_CPPFLAGS = $(partedincludedir) $(INTLINCS) diff --git a/libparted/Makefile.in b/libparted/Makefile.in new file mode 100644 index 0000000..3708494 --- /dev/null +++ b/libparted/Makefile.in @@ -0,0 +1,2265 @@ +# 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, 2009-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@ +@HAVE_CHECK_TRUE@am__append_1 = tests +subdir = libparted +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 = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libparted_la_DEPENDENCIES = fs/libfs.la labels/liblabels.la \ + $(top_builddir)/lib/libgnulib.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am__dirstamp = $(am__leading_dot)dirstamp +am__objects_1 = arch/$(OS).lo +am_libparted_la_OBJECTS = debug.lo architecture.lo device.lo \ + exception.lo filesys.lo libparted.lo timer.lo unit.lo disk.lo \ + cs/geom.lo cs/constraint.lo cs/natmath.lo $(am__objects_1) +libparted_la_OBJECTS = $(am_libparted_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 = +libparted_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libparted_la_LDFLAGS) $(LDFLAGS) -o $@ +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)/architecture.Plo \ + ./$(DEPDIR)/debug.Plo ./$(DEPDIR)/device.Plo \ + ./$(DEPDIR)/disk.Plo ./$(DEPDIR)/exception.Plo \ + ./$(DEPDIR)/filesys.Plo ./$(DEPDIR)/libparted.Plo \ + ./$(DEPDIR)/timer.Plo ./$(DEPDIR)/unit.Plo \ + arch/$(DEPDIR)/$(OS).Plo arch/$(DEPDIR)/beos.Plo \ + arch/$(DEPDIR)/gnu.Plo arch/$(DEPDIR)/linux.Plo \ + cs/$(DEPDIR)/constraint.Plo cs/$(DEPDIR)/geom.Plo \ + cs/$(DEPDIR)/natmath.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 = $(libparted_la_SOURCES) $(EXTRA_libparted_la_SOURCES) +DIST_SOURCES = $(libparted_la_SOURCES) $(EXTRA_libparted_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +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)` +DIST_SUBDIRS = labels fs . tests +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build-aux/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +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@ +SUBDIRS_CHECK = $(am__append_1) +ARCH_SOURCE = arch/$(OS).c +AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS) +SUBDIRS = labels fs . $(SUBDIRS_CHECK) +partedincludedir = \ + -I$(top_srcdir)/lib -I$(top_builddir)/include -I$(top_srcdir)/include + +lib_LTLIBRARIES = libparted.la + +# Set the shared library version, per Libtool's guidelines. +# For details, see the "Updating library version information" section of +# "info libtool". +CURRENT = 2 +REVISION = 5 +AGE = 0 +libparted_la_LDFLAGS = -version-info $(CURRENT):$(REVISION):$(AGE) +libparted_la_SOURCES = debug.c \ + architecture.c \ + architecture.h \ + device.c \ + exception.c \ + filesys.c \ + libparted.c \ + timer.c \ + unit.c \ + disk.c \ + cs/geom.c \ + cs/constraint.c \ + cs/natmath.c \ + $(ARCH_SOURCE) + +EXTRA_libparted_la_SOURCES = arch/linux.c \ + arch/linux.h \ + arch/gnu.c \ + arch/beos.c + +libparted_la_LIBADD = \ + fs/libfs.la \ + labels/liblabels.la \ + $(top_builddir)/lib/libgnulib.la \ + $(OS_LIBS) \ + $(DM_LIBS) \ + $(LIB_BLKID) \ + $(UUID_LIBS) \ + $(INTLLIBS) + +EXTRA_DIST = mbr.s +AM_CPPFLAGS = $(partedincludedir) $(INTLINCS) +all: all-recursive + +.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/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu libparted/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): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_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}; \ + } +cs/$(am__dirstamp): + @$(MKDIR_P) cs + @: > cs/$(am__dirstamp) +cs/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) cs/$(DEPDIR) + @: > cs/$(DEPDIR)/$(am__dirstamp) +cs/geom.lo: cs/$(am__dirstamp) cs/$(DEPDIR)/$(am__dirstamp) +cs/constraint.lo: cs/$(am__dirstamp) cs/$(DEPDIR)/$(am__dirstamp) +cs/natmath.lo: cs/$(am__dirstamp) cs/$(DEPDIR)/$(am__dirstamp) +arch/$(am__dirstamp): + @$(MKDIR_P) arch + @: > arch/$(am__dirstamp) +arch/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) arch/$(DEPDIR) + @: > arch/$(DEPDIR)/$(am__dirstamp) +arch/$(OS).lo: arch/$(am__dirstamp) arch/$(DEPDIR)/$(am__dirstamp) +arch/linux.lo: arch/$(am__dirstamp) arch/$(DEPDIR)/$(am__dirstamp) +arch/gnu.lo: arch/$(am__dirstamp) arch/$(DEPDIR)/$(am__dirstamp) +arch/beos.lo: arch/$(am__dirstamp) arch/$(DEPDIR)/$(am__dirstamp) + +libparted.la: $(libparted_la_OBJECTS) $(libparted_la_DEPENDENCIES) $(EXTRA_libparted_la_DEPENDENCIES) + $(AM_V_CCLD)$(libparted_la_LINK) -rpath $(libdir) $(libparted_la_OBJECTS) $(libparted_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f arch/*.$(OBJEXT) + -rm -f arch/*.lo + -rm -f cs/*.$(OBJEXT) + -rm -f cs/*.lo + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/architecture.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/device.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disk.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exception.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesys.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libparted.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unit.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@arch/$(DEPDIR)/$(OS).Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@arch/$(DEPDIR)/beos.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@arch/$(DEPDIR)/gnu.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@arch/$(DEPDIR)/linux.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@cs/$(DEPDIR)/constraint.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@cs/$(DEPDIR)/geom.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@cs/$(DEPDIR)/natmath.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 + -rm -rf arch/.libs arch/_libs + -rm -rf cs/.libs cs/_libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(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-recursive + +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-recursive + +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 + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +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) + -rm -f arch/$(DEPDIR)/$(am__dirstamp) + -rm -f arch/$(am__dirstamp) + -rm -f cs/$(DEPDIR)/$(am__dirstamp) + -rm -f cs/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/architecture.Plo + -rm -f ./$(DEPDIR)/debug.Plo + -rm -f ./$(DEPDIR)/device.Plo + -rm -f ./$(DEPDIR)/disk.Plo + -rm -f ./$(DEPDIR)/exception.Plo + -rm -f ./$(DEPDIR)/filesys.Plo + -rm -f ./$(DEPDIR)/libparted.Plo + -rm -f ./$(DEPDIR)/timer.Plo + -rm -f ./$(DEPDIR)/unit.Plo + -rm -f arch/$(DEPDIR)/$(OS).Plo + -rm -f arch/$(DEPDIR)/beos.Plo + -rm -f arch/$(DEPDIR)/gnu.Plo + -rm -f arch/$(DEPDIR)/linux.Plo + -rm -f cs/$(DEPDIR)/constraint.Plo + -rm -f cs/$(DEPDIR)/geom.Plo + -rm -f cs/$(DEPDIR)/natmath.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/architecture.Plo + -rm -f ./$(DEPDIR)/debug.Plo + -rm -f ./$(DEPDIR)/device.Plo + -rm -f ./$(DEPDIR)/disk.Plo + -rm -f ./$(DEPDIR)/exception.Plo + -rm -f ./$(DEPDIR)/filesys.Plo + -rm -f ./$(DEPDIR)/libparted.Plo + -rm -f ./$(DEPDIR)/timer.Plo + -rm -f ./$(DEPDIR)/unit.Plo + -rm -f arch/$(DEPDIR)/$(OS).Plo + -rm -f arch/$(DEPDIR)/beos.Plo + -rm -f arch/$(DEPDIR)/gnu.Plo + -rm -f arch/$(DEPDIR)/linux.Plo + -rm -f cs/$(DEPDIR)/constraint.Plo + -rm -f cs/$(DEPDIR)/geom.Plo + -rm -f cs/$(DEPDIR)/natmath.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool 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-libLTLIBRARIES install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-libLTLIBRARIES + +.PRECIOUS: Makefile + + +# 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/arch/beos.c b/libparted/arch/beos.c new file mode 100644 index 0000000..df14b99 --- /dev/null +++ b/libparted/arch/beos.c @@ -0,0 +1,649 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2006-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 <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> + +/* POSIX headers */ +#include <sys/stat.h> +#include <dirent.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +/* BeOS APIs */ +#include <drivers/Drivers.h> + +/* ZETA R1+ APIs */ +#if B_ZETA_VERSION >= B_ZETA_VERSION_1_0_0 +# include <device/ata_info.h> +#endif + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "../architecture.h" + +#define BEOS_SPECIFIC(dev) ((BEOSSpecific*) (dev)->arch_specific) + +typedef struct _BEOSSpecific BEOSSpecific; + +struct _BEOSSpecific { + int fd; +}; + +static void +_scan_for_disks(const char* path) +{ + char subdir[PATH_MAX]; + dirent_t* entp; + size_t pos; + DIR* dirp; + + if ((dirp=opendir(path)) != NULL) + { + /* Build first part of path */ + strcpy(subdir, path); + strcat(subdir, "/"); + pos = strlen(subdir); + + /* Check all directory entries.. */ + while((entp=readdir(dirp)) != NULL) + { + /* If they start with '.' just skip */ + if (entp->d_name[0] == '.') + continue; + + strcpy(subdir+pos, entp->d_name); + + /* /dev/disk/.../raw are the complete disks + we're interested in */ + if (strcmp(entp->d_name, "raw") == 0) + _ped_device_probe(subdir); + else /* If not 'raw', it most often will + be another subdir */ + _scan_for_disks(subdir); + } + + closedir(dirp); + } +} + +static void +_flush_cache(PedDevice* dev) +{ + int fd; + if ((fd=open(dev->path, O_RDONLY)) < 0) + { + ioctl(fd, B_FLUSH_DRIVE_CACHE); + close(fd); + } +} + +#if B_ZETA_VERSION >= B_ZETA_VERSION_1_0_0 +static int +_device_init_ata(PedDevice* dev) +{ + ata_device_infoblock ide_info; + int maxlen, len, idx, fd; + char buf[256]; + + /* Try and get information about device from ATA(PI) driver */ + if ((fd=open(dev->path, O_RDONLY)) < 0) + goto ata_error; + + if (ioctl(fd, B_ATA_GET_DEVICE_INFO, &ide_info, sizeof(ide_info)) < 0) + goto ata_error_close; + + close(fd); + + /* Copy 'logical' dimensions */ + dev->bios_geom.cylinders = ide_info.cylinders; + dev->bios_geom.heads = ide_info.heads; + dev->bios_geom.sectors = ide_info.sectors; + + /* Copy used dimensions */ + dev->hw_geom.cylinders = ide_info.current_cylinders; + dev->hw_geom.heads = ide_info.current_heads; + dev->hw_geom.sectors = ide_info.current_sectors; + + /* Copy total number of sectors */ + if (ide_info._48_bit_addresses_supported) + dev->length = ide_info.LBA48_total_sectors; + else if (ide_info.LBA_supported) + dev->length = ide_info.LBA_total_sectors; + else + dev->length = ide_info.cylinders * + ide_info.heads * + ide_info.sectors; + + dev->sector_size = + dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT; + + /* Use sensible model */ + maxlen=sizeof(ide_info.model_number); + strncpy(buf, ide_info.model_number, maxlen); + buf[maxlen] = '\0'; + + for (len=-1, idx=maxlen-1; idx > 0 && len < 0; idx--) + if (buf[idx] > 0x20) + len = idx; + + buf[(len == -1) ? 0 : len+1] = '\0'; + + dev->model = strdup(buf); + + return PED_DEVICE_IDE; + +ata_error_close: + close(fd); + +ata_error: + return 0; +} +#endif + +static int +_device_init_generic_blkdev(PedDevice* dev) +{ + device_geometry bios, os; + int got_bios_info = 0; + int fd; + + /* Try and get information about device from ATA(PI) driver */ + if ((fd=open(dev->path, O_RDONLY)) < 0) + goto blkdev_error; + + /* B_GET_GEOMETRY is mandatory */ + if (ioctl(fd, B_GET_GEOMETRY, &os, sizeof(os)) < 0) + goto blkdev_error_close; + + /* B_GET_BIOS_GEOMETRY is optional */ + if (!ioctl(fd, B_GET_BIOS_GEOMETRY, &bios, sizeof(bios))) + got_bios_info = 1; + + close(fd); + + dev->hw_geom.cylinders = os.cylinder_count; + dev->hw_geom.heads = os.head_count; + dev->hw_geom.sectors = os.sectors_per_track; + + dev->sector_size = + dev->phys_sector_size = os.bytes_per_sector; + + if (got_bios_info) + { + dev->bios_geom.cylinders = bios.cylinder_count; + dev->bios_geom.heads = bios.head_count; + dev->bios_geom.sectors = bios.sectors_per_track; + } + else + dev->bios_geom = dev->hw_geom; + + dev->model = strdup(""); + + return PED_DEVICE_IDE; + +blkdev_error_close: + close(fd); + +blkdev_error: + return 0; +} + +static int +_device_init_blkdev(PedDevice* dev) +{ + int type; + +#if B_ZETA_VERSION >= B_ZETA_VERSION_1_0_0 + if (!(type=_device_init_ata(dev))) +#endif + type = _device_init_generic_blkdev(dev); + + return type; +} + +static int +_device_init_file(PedDevice* dev, struct stat* dev_statp) +{ + if (!dev_statp->st_size) + return 0; + + dev->sector_size = + dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT; + + dev->length = dev_statp->st_size / PED_SECTOR_SIZE_DEFAULT; + + dev->bios_geom.cylinders = dev->length / (4 * 32); + dev->bios_geom.heads = 4; + dev->bios_geom.sectors = 32; + dev->hw_geom = dev->bios_geom; + + dev->model = strdup(_("Disk Image")); + + return PED_DEVICE_FILE; +} + +static int +_device_init(PedDevice* dev) +{ + struct stat dev_stat; + int type = 0; + + /* Check if we're a regular file */ + if (stat(dev->path, &dev_stat) < 0) + goto done; + + if (S_ISBLK(dev_stat.st_mode) || S_ISCHR(dev_stat.st_mode)) + type = _device_init_blkdev(dev); + else if (S_ISREG(dev_stat.st_mode)) + type = _device_init_file(dev,&dev_stat); + +done: + return type; +} + + +static PedDevice* +beos_new (const char* path) +{ + struct stat stat_info; + PedDevice* dev; + + PED_ASSERT(path != NULL); + + dev = (PedDevice*) ped_malloc (sizeof (PedDevice)); + if (!dev) + goto error; + + dev->path = strdup(path); + if (!dev->path) + goto error_free_dev; + + dev->arch_specific + = (BEOSSpecific*) ped_malloc(sizeof(BEOSSpecific)); + if (dev->arch_specific == NULL) + goto error_free_path; + + dev->open_count = 0; + dev->read_only = 0; + dev->external_mode = 0; + dev->dirty = 0; + dev->boot_dirty = 0; + + if ((dev->type=_device_init(dev)) <= 0) + goto error_free_arch_specific; + + /* All OK! */ + return dev; + +error_free_arch_specific: + free (dev->arch_specific); + +error_free_path: + free (dev->path); + +error_free_dev: + free (dev); + +error: + return NULL; +} + +static void +beos_destroy (PedDevice* dev) +{ + free (dev->arch_specific); + free (dev->path); + free (dev->model); + free (dev); +} + +static int +beos_is_busy (PedDevice* dev) +{ + return 1; +} + +static int +beos_open (PedDevice* dev) +{ + BEOSSpecific* arch_specific = BEOS_SPECIFIC(dev); + +retry: + arch_specific->fd = open(dev->path, O_RDWR); + if (arch_specific->fd == -1) { + char* rw_error_msg = strerror(errno); + + arch_specific->fd = open (dev->path, O_RDONLY); + if (arch_specific->fd == -1) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_CANCEL, + _("Error opening %s: %s"), + dev->path, strerror (errno)) + != PED_EXCEPTION_RETRY) { + return 0; + } else { + goto retry; + } + } else { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("Unable to open %s read-write (%s). %s has " + "been opened read-only."), + dev->path, rw_error_msg, dev->path); + dev->read_only = 1; + } + } else { + dev->read_only = 0; + } + + _flush_cache (dev); + + return 1; +} + +static int +beos_refresh_open (PedDevice* dev) +{ + return 1; +} + +static int +beos_close (PedDevice* dev) +{ + BEOSSpecific* arch_specific = BEOS_SPECIFIC(dev); + + if (dev->dirty) + _flush_cache (dev); + + close (arch_specific->fd); + + return 1; +} + +static int +beos_refresh_close (PedDevice* dev) +{ + if (dev->dirty) + _flush_cache (dev); + + return 1; +} + +static int +beos_read (const PedDevice* dev, void* buffer, PedSector start, PedSector count) +{ + BEOSSpecific* arch_specific = BEOS_SPECIFIC(dev); + int status; + PedExceptionOption ex_status; + size_t read_length = count * dev->sector_size; + + PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + + /* First, try to seek */ + while(1) + { + if (lseek(arch_specific->fd, start * dev->sector_size, SEEK_SET) + == start * dev->sector_size) + break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during seek for read on %s"), + strerror (errno), dev->path); + + switch (ex_status) + { + case PED_EXCEPTION_IGNORE: + return 1; + case PED_EXCEPTION_RETRY: + break /* out of switch */; + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + /* If we seeked ok, now is the time to read */ + while (1) + { + status = read(arch_specific->fd, buffer, read_length); + if (status == count * dev->sector_size) + break; + + if (status > 0) + { + read_length -= status; + buffer += status; + continue; + } + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during read on %s"), + strerror (errno), + dev->path); + + switch (ex_status) + { + case PED_EXCEPTION_IGNORE: + return 1; + case PED_EXCEPTION_RETRY: + break; + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + return 1; +} + +static int +beos_write (PedDevice* dev, const void* buffer, PedSector start, + PedSector count) +{ + BEOSSpecific* arch_specific = BEOS_SPECIFIC(dev); + int status; + PedExceptionOption ex_status; + size_t write_length = count * dev->sector_size; + + PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + + if (dev->read_only) + { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Can't write to %s, because it is opened read-only."), + dev->path) + != PED_EXCEPTION_IGNORE) + return 0; + else + return 1; + } + + while(1) + { + if (lseek(arch_specific->fd,start * dev->sector_size,SEEK_SET) + == start * dev->sector_size) + break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during seek for write on %s"), + strerror (errno), dev->path); + + switch (ex_status) + { + case PED_EXCEPTION_IGNORE: + return 1; + case PED_EXCEPTION_RETRY: + break; + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + +#ifdef READ_ONLY + printf ("ped_device_write (\"%s\", %p, %d, %d)\n", + dev->path, buffer, (int) start, (int) count); +#else + dev->dirty = 1; + while(1) + { + status = write (arch_specific->fd, buffer, write_length); + if (status == count * dev->sector_size) + break; + + if (status > 0) + { + write_length -= status; + buffer += status; + continue; + } + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (errno), dev->path); + + switch (ex_status) + { + case PED_EXCEPTION_IGNORE: + return 1; + case PED_EXCEPTION_RETRY: + break; + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } +#endif /* !READ_ONLY */ + + return 1; +} + +static PedSector +beos_check (PedDevice* dev, void* buffer, PedSector start, PedSector count) +{ + BEOSSpecific* arch_specific = BEOS_SPECIFIC(dev); + PedSector done = 0; + int status; + + PED_ASSERT(dev != NULL); + + if (lseek(arch_specific->fd, start * dev->sector_size, SEEK_SET) + != start * dev->sector_size) + return 0; + + for (done = 0; done < count; done += status / dev->sector_size) + { + status = read (arch_specific->fd, buffer, + (size_t) ((count-done) * dev->sector_size)); + if (status < 0) + break; + } + + return done; +} + +static int +beos_sync (PedDevice* dev) +{ + return 1; +} + +static int +beos_sync_fast (PedDevice* dev) +{ + return 1; +} + +/* Probe for all available disks */ +static void +beos_probe_all () +{ + /* For BeOS/ZETA/Haiku, all disks are published under /dev/disk */ + _scan_for_disks("/dev/disk"); +} + +static char* +beos_partition_get_path (const PedPartition* part) +{ + return NULL; +} + +static int +beos_partition_is_busy (const PedPartition* part) +{ + return 0; +} + +static int +beos_disk_commit (PedDisk* disk) +{ + return 0; +} + +static PedDeviceArchOps beos_dev_ops = { + _new: beos_new, + destroy: beos_destroy, + is_busy: beos_is_busy, + open: beos_open, + refresh_open: beos_refresh_open, + close: beos_close, + refresh_close: beos_refresh_close, + read: beos_read, + write: beos_write, + check: beos_check, + sync: beos_sync, + sync_fast: beos_sync_fast, + probe_all: beos_probe_all +}; + +static PedDiskArchOps beos_disk_ops = { + partition_get_path: beos_partition_get_path, + partition_is_busy: beos_partition_is_busy, + disk_commit: beos_disk_commit +}; + +PedArchitecture ped_beos_arch = { + dev_ops: &beos_dev_ops, + disk_ops: &beos_disk_ops +}; diff --git a/libparted/arch/gnu.c b/libparted/arch/gnu.c new file mode 100644 index 0000000..b2f0428 --- /dev/null +++ b/libparted/arch/gnu.c @@ -0,0 +1,930 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2001, 2005, 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 <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> + +#include <errno.h> +#include <hurd.h> +#include <hurd/fs.h> +#include <hurd/store.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "../architecture.h" + +#define GNU_SPECIFIC(dev) ((GNUSpecific*) (dev)->arch_specific) + +typedef struct _GNUSpecific GNUSpecific; + +struct _GNUSpecific { + struct store* store; + int consume; +}; + +/* Initialize a PedDevice using SOURCE. The SOURCE will NOT be destroyed; + the caller created it, it is the caller's responsilbility to free it + after it calls ped_device_destory. SOURCE is not registered in Parted's + list of devices. */ +PedDevice* ped_device_new_from_store (struct store *source); + +static int +_device_get_sector_size (PedDevice* dev) +{ + GNUSpecific* arch_specific = GNU_SPECIFIC (dev); + size_t store_block_size = arch_specific->store->block_size; + + return PED_SECTOR_SIZE_DEFAULT; +} + +static PedSector +_device_get_length (PedDevice* dev) +{ + GNUSpecific* arch_specific = GNU_SPECIFIC (dev); + size_t store_blocks = arch_specific->store->blocks; + size_t store_block_size = arch_specific->store->block_size; + + return ((long long) store_blocks * store_block_size) / PED_SECTOR_SIZE_DEFAULT; +} + +static int +_device_probe_geometry (PedDevice* dev) +{ + PedSector cyl_size; + + dev->length = _device_get_length (dev); + if (!dev->length) + return 0; + + dev->sector_size = _device_get_sector_size (dev); + if (!dev->sector_size) + return 0; + + /* XXX: We have no way to get this! */ + dev->bios_geom.sectors = 63; + dev->bios_geom.heads = 255; + cyl_size = dev->bios_geom.sectors * dev->bios_geom.heads; + dev->bios_geom.cylinders = dev->length / cyl_size + * (dev->sector_size / PED_SECTOR_SIZE_DEFAULT); + dev->hw_geom = dev->bios_geom; + + return 1; +} + +static int +init_file (PedDevice* dev) +{ + PedExceptionOption ex_status; + +retry_open: + if (!ped_device_open (dev)) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_RETRY_CANCEL, + _("Unable to open %s."), + dev->path); + switch (ex_status) { + case PED_EXCEPTION_RETRY: + goto retry_open; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + goto error; + } + + return 0; + } + +retry_probe: + if (!_device_probe_geometry (dev)) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_RETRY_CANCEL, + _("Unable to probe store.")); + switch (ex_status) { + case PED_EXCEPTION_RETRY: + goto retry_probe; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + goto error_close_dev; + } + + return 0; + } + + dev->model = ""; + + ped_device_close (dev); + return 1; + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} + +static void +_flush_cache (PedDevice* dev) +{ + GNUSpecific* arch_specific = GNU_SPECIFIC (dev); + + if (dev->read_only) + return; + + /* Wait for a complete sync to finish. */ + file_sync (arch_specific->store->source, 1, 0); +} + +/* Initialize by allocating memory and filling in a few defaults, a + PedDevice structure. */ +static PedDevice* +_init_device (const char *path) +{ + PedDevice *dev; + GNUSpecific* arch_specific; + + dev = (PedDevice*) ped_malloc (sizeof (PedDevice)); + if (!dev) + goto error; + + dev->path = strdup (path); + if (!dev->path) + goto error_free_dev; + + dev->arch_specific + = (GNUSpecific*) ped_malloc (sizeof (GNUSpecific)); + if (!dev->arch_specific) + goto error_free_path; + + dev->type = PED_DEVICE_UNKNOWN; /* It's deprecated anyway */ + dev->open_count = 0; + dev->read_only = 0; + dev->external_mode = 0; + dev->dirty = 0; + dev->boot_dirty = 0; + + return dev; + +error_free_arch_specific: + free (dev->arch_specific); +error_free_path: + free (dev->path); +error_free_dev: + free (dev); +error: + return NULL; +} + +/* Ask the kernel and translators to reload the partition table. + XXX: Will probably be replaced by some RPC to partfs when it's finished. In + the meantime, gnumach's glue layer will pass BLKRRPART to the Linux drivers. + */ +#define BLKRRPART 0x125F +static int +_reread_part_table (PedDevice* dev) +{ + struct store *store = GNU_SPECIFIC (dev)->store; + int retry_count = 9; + int len = strlen (dev->path); + char path[len + 3 + 1]; + int i; + int done = 1; + + sync (); + + if(strcmp (store->class->name, "device") == 0) { + while (device_set_status (store->port, BLKRRPART, NULL, 0)) { + retry_count--; + sync (); + if (retry_count == 3) + sleep (1); /* Pause to allow system to settle */ + + if (!retry_count) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("WARNING: the kernel failed to re-read the " + "partition table on %s (%s). As a result, " + "it may not reflect all of your changes " + "until after reboot."), + dev->path, strerror (errno)); + return 0; + } + } + } + + i = 1; + while (1) { + file_t node; + error_t err; + + /* Throw away all active parted-based translators */ + snprintf (path, sizeof (path), "%ss%u", dev->path, i); + node = file_name_lookup (path, O_NOTRANS, 0666); + if (node == MACH_PORT_NULL) { + if (errno == ENOENT) + /* Finished looping over them */ + break; + + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("Warning: unable to open %s (%s). As a " + "result, it may not reflect all of your " + "changes until after reboot."), + path, strerror (errno)); + done = 0; + } + + err = file_set_translator (node, 0, FS_TRANS_SET, + 0, 0, 0, MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND); + if (err) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("Warning: failed to make translator go away " + "on %s (%s). As a result, it may not reflect " + "all of your changes until after reboot."), + dev->path, strerror (errno)); + done = 0; + } + i++; + } + + return done; +} + +/* Free the memory associated with a PedDevice structure. */ +static void +_done_device (PedDevice *dev) +{ + free (dev->arch_specific); + free (dev->path); + free (dev); +} + +/* Release all resources that libparted owns in DEV. */ +static void +gnu_destroy (PedDevice* dev) +{ + GNUSpecific* arch_specific = GNU_SPECIFIC (dev); + + if (arch_specific->consume) + store_free (arch_specific->store); + + _done_device (dev); +} + +static PedDevice* +gnu_new (const char* path) +{ + PedDevice* dev; + GNUSpecific* arch_specific; + error_t ro_err, rw_err; + int ispath; + + PED_ASSERT (path != NULL); + + dev = _init_device (path); + if (!dev) + return NULL; + + arch_specific = GNU_SPECIFIC (dev); + arch_specific->consume = 1; + + retry_open: + /* Try read-write. */ + if (strchr (dev->path, '/') != NULL) { + /* We set this to prevent having to use strchr more then once. */ + ispath = 1; + + rw_err = store_open (dev->path, 0, NULL, &arch_specific->store); + } else { + rw_err = store_typed_open (dev->path, 0, NULL, &arch_specific->store); + } + + /* Try readonly. */ + if (rw_err) { + if (ispath) { + ro_err = store_open (dev->path, STORE_READONLY, NULL, + &arch_specific->store); + } else { + ro_err = store_typed_open (dev->path, STORE_READONLY, NULL, + &arch_specific->store); + } + + if (ro_err) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_CANCEL, + _("Error opening %s: %s"), + dev->path, strerror (ro_err)) + != PED_EXCEPTION_RETRY) { + return NULL; + } else + goto retry_open; + } else { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("Unable to open %s read-write (%s). %s has " + "been opened read-only."), + dev->path, strerror (rw_err), dev->path); + dev->read_only = 1; + } + } else { + dev->read_only = 0; + } + + _flush_cache (dev); + + if (!init_file (dev)) { + gnu_destroy(dev); + return NULL; + } + + return dev; +} + +PedDevice* +ped_device_new_from_store (struct store *source) +{ + PedDevice* dev; + GNUSpecific* arch_specific; + + PED_ASSERT (source != NULL); + + dev = _init_device (source->name ?: "(unknown)"); + if (!dev) + return NULL; + + arch_specific = GNU_SPECIFIC (dev); + arch_specific->store = source; + arch_specific->consume = 0; + + dev->read_only = source->flags & (STORE_READONLY|STORE_HARD_READONLY); + + if (!init_file (dev)) { + _done_device (dev); + return NULL; + } + + return dev; +} + +static int +gnu_is_busy (PedDevice* dev) +{ + return 0; +} + +static int +gnu_open (PedDevice* dev) +{ + return 1; +} + +static int +gnu_refresh_open (PedDevice* dev) +{ + return 1; +} + +static int +gnu_close (PedDevice* dev) +{ + GNUSpecific* arch_specific = GNU_SPECIFIC (dev); + + _flush_cache (dev); + + if (dev->dirty && dev->type != PED_DEVICE_FILE) { + if (_reread_part_table (dev)) + dev->dirty = 0; + } + + return 1; +} + +static int +gnu_refresh_close (PedDevice* dev) +{ + _flush_cache (dev); + return 1; +} + +static int +gnu_read (const PedDevice* dev, void* user_buffer, PedSector device_start, + PedSector count) +{ + GNUSpecific* arch_specific = GNU_SPECIFIC (dev); + error_t err; + PedExceptionOption ex_status; + size_t start; + size_t store_start_block; + /* In bytes. This can be larger than COUNT when store pages are + larger than PED_SECTOR_SIZE_DEFAULT. */ + size_t store_read_length; + char local_buffer[PED_SECTOR_SIZE_DEFAULT]; + void * store_read_buffer; + size_t have_read; + size_t read_offset; + size_t device_read_length = count * PED_SECTOR_SIZE_DEFAULT; + + start = device_start * PED_SECTOR_SIZE_DEFAULT; + if (PED_SECTOR_SIZE_DEFAULT != arch_specific->store->block_size) { + store_start_block = start / arch_specific->store->block_size; + store_read_length = (device_read_length + + arch_specific->store->block_size - 1) + / arch_specific->store->block_size; + } else { + store_start_block = device_start; + store_read_length = device_read_length; + } + + read_offset = start + - store_start_block * arch_specific->store->block_size; + + if (store_read_length % arch_specific->store->block_size != 0) + store_read_length = store_read_length + + arch_specific->store->block_size + - store_read_length % arch_specific->store->block_size; + +retry: + have_read = 0; + while (1) { + size_t did_read; + size_t offset; + + store_read_buffer = local_buffer; + did_read = sizeof (local_buffer); + + err = store_read (arch_specific->store, store_start_block, + store_read_length - have_read, + &store_read_buffer, &did_read); + if (err) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during read on %s"), + strerror (err), + dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + return 1; + + case PED_EXCEPTION_RETRY: + goto retry; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + memcpy (user_buffer + have_read - read_offset, + store_read_buffer + + (have_read >= read_offset + ? 0 : read_offset - have_read), + have_read + did_read > device_read_length + read_offset + ? device_read_length + read_offset - have_read + : did_read); + + if (store_read_buffer != local_buffer) + vm_deallocate (mach_task_self (), + (long) store_read_buffer, did_read); + + have_read += did_read; + store_start_block += did_read + / arch_specific->store->block_size; + + if (have_read >= device_read_length) + break; + } + + return 1; +} + +static int +gnu_write (PedDevice* dev, const void* buffer, PedSector start, PedSector count) +{ + GNUSpecific* arch_specific = GNU_SPECIFIC (dev); + error_t err; + PedExceptionOption ex_status; + void * temp; + char local_buffer[PED_SECTOR_SIZE_DEFAULT]; + size_t did_read; + size_t did_write; + + /* Map a disk sector to a store sector. */ + #define PED_TO_STORE(store, sector) (((sector) * PED_SECTOR_SIZE_DEFAULT) \ + / (store)->block_size) + + if (dev->read_only) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Can't write to %s, because it is opened read-only."), + dev->path) + != PED_EXCEPTION_IGNORE) + return 0; + else + return 1; + } + +#ifdef READ_ONLY + printf ("ped_device_write (\"%s\", %p, %d, %d)\n", + dev->path, buffer, (int) start, (int) count); +#else + dev->dirty = 1; + + /* If the first ``device'' block (PedSector) is not aligned on a + store block, then we need to fetch the old block, copy in the + overlaping area and finally, write the modified data out to the + store. */ + if ((PED_SECTOR_SIZE_DEFAULT * start) % arch_specific->store->block_size + != 0) { + size_t write_offset; + size_t flushing; + +doggy_first_block_read: + /* We do not bother looping as we are only reading a + single block. */ + temp = local_buffer; + did_read = sizeof (local_buffer); + err = store_read (arch_specific->store, + PED_TO_STORE (arch_specific->store, start), + arch_specific->store->block_size, &temp, + &did_read); + if (! err && did_read != arch_specific->store->block_size) + err = EIO; + + if (err) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during read on %s"), + strerror (err), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + break; + + case PED_EXCEPTION_RETRY: + goto doggy_first_block_read; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + write_offset = (start * PED_SECTOR_SIZE_DEFAULT) + % arch_specific->store->block_size; + flushing = arch_specific->store->block_size - write_offset; + if (flushing > count * PED_SECTOR_SIZE_DEFAULT) + flushing = count * PED_SECTOR_SIZE_DEFAULT; + + memcpy (temp + write_offset, buffer, flushing); + +doggy_first_block_write: + err = store_write (arch_specific->store, + PED_TO_STORE (arch_specific->store, start), + temp, arch_specific->store->block_size, + &did_write); + if (! err && did_write != arch_specific->store->block_size) + err = EIO; + + if (err) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (err), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + break; + + case PED_EXCEPTION_RETRY: + goto doggy_first_block_write; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + if (temp != local_buffer) + vm_deallocate ( + mach_task_self (), + (long) temp, + did_read); + return 0; + } + } + + start += flushing / PED_SECTOR_SIZE_DEFAULT; + count -= flushing / PED_SECTOR_SIZE_DEFAULT; + buffer += write_offset; + + if (temp != local_buffer) + vm_deallocate (mach_task_self (), (long) temp, + did_read); + + if (count == 0) + return 1; + } + + while (count > 0 + && count >= arch_specific->store->block_size / PED_SECTOR_SIZE_DEFAULT) { + err = store_write (arch_specific->store, + PED_TO_STORE (arch_specific->store, start), + buffer, count * PED_SECTOR_SIZE_DEFAULT, + &did_write); + + if (err) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (err), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + break; + + case PED_EXCEPTION_RETRY: + continue; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + start += did_write / PED_SECTOR_SIZE_DEFAULT; + count -= did_write / PED_SECTOR_SIZE_DEFAULT; + buffer += did_write; + } + + if (count == 0) + return 1; + + /* We are now left with (strictly) less then a store block to write + to disk. Thus, we read the block, overlay the buffer and flush. */ + PED_ASSERT (count * PED_SECTOR_SIZE_DEFAULT + < arch_specific->store->block_size); + +doggy_last_block_read: + /* We do not bother looping as we are only reading a + single block. */ + temp = local_buffer; + did_read = sizeof (local_buffer); + err = store_read (arch_specific->store, + PED_TO_STORE (arch_specific->store, start), + arch_specific->store->block_size, &temp, + &did_read); + if (! err && did_read != arch_specific->store->block_size) + err = EIO; + + if (err) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during read on %s"), + strerror (err), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + break; + + case PED_EXCEPTION_RETRY: + goto doggy_last_block_read; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + memcpy (temp, buffer, count * PED_SECTOR_SIZE_DEFAULT); + +doggy_last_block_write: + err = store_write (arch_specific->store, + PED_TO_STORE (arch_specific->store, start), + temp, arch_specific->store->block_size, + &did_write); + if (! err && did_write != arch_specific->store->block_size) + err = EIO; + + if (err) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (err), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + break; + + case PED_EXCEPTION_RETRY: + goto doggy_last_block_write; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + if (temp != local_buffer) + vm_deallocate (mach_task_self (), + (long) temp, + did_read); + return 0; + } + } + +#endif /* !READ_ONLY */ + return 1; +} + +/* TODO: returns the number of sectors that are ok. + */ +static PedSector +gnu_check (PedDevice* dev, void* buffer, PedSector start, PedSector count) +{ + int status; + int done = 0; + + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + PED_ASSERT (buffer != NULL); + + return count; +} + +static int +gnu_sync (PedDevice* dev) +{ + GNUSpecific* arch_specific; + error_t err; + PedExceptionOption ex_status; + static char *last_failure = NULL; + + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + + arch_specific = GNU_SPECIFIC (dev); + + if (dev->read_only || ! dev->dirty) + return 1; + + while (1) { + err = file_sync (arch_specific->store->source, 1, 0); + if (! err || err == EOPNOTSUPP || err == EPERM + || (last_failure && strcmp (last_failure, dev->path) == 0)) + break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s trying to sync %s to disk"), + strerror (errno), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + free (last_failure); + last_failure = strdup (dev->path); + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + case PED_EXCEPTION_CANCEL: + return 0; + } + } + + return 1; +} + +static int +probe_standard_devices () +{ + _ped_device_probe ("/dev/sd0"); + _ped_device_probe ("/dev/sd1"); + _ped_device_probe ("/dev/sd2"); + _ped_device_probe ("/dev/sd3"); + _ped_device_probe ("/dev/sd4"); + _ped_device_probe ("/dev/sd5"); + + _ped_device_probe ("/dev/hd0"); + _ped_device_probe ("/dev/hd1"); + _ped_device_probe ("/dev/hd2"); + _ped_device_probe ("/dev/hd3"); + _ped_device_probe ("/dev/hd4"); + _ped_device_probe ("/dev/hd5"); + _ped_device_probe ("/dev/hd6"); + _ped_device_probe ("/dev/hd7"); + + _ped_device_probe ("/dev/wd0"); + _ped_device_probe ("/dev/wd1"); + _ped_device_probe ("/dev/wd2"); + _ped_device_probe ("/dev/wd3"); + _ped_device_probe ("/dev/wd4"); + _ped_device_probe ("/dev/wd5"); + _ped_device_probe ("/dev/wd6"); + _ped_device_probe ("/dev/wd7"); + + return 1; +} + +static void +gnu_probe_all () +{ + probe_standard_devices (); +} + +static char* +gnu_partition_get_path (const PedPartition* part) +{ + const char* dev_path = part->disk->dev->path; + int result_len = strlen (dev_path) + 16; + char* result; + + result = (char*) ped_malloc (result_len); + if (!result) + return NULL; + snprintf (result, result_len, "%ss%d", dev_path, part->num); + return result; +} + +static int +gnu_partition_is_busy (const PedPartition* part) +{ + return 0; +} + +static int +gnu_disk_commit (PedDisk* disk) +{ + return _reread_part_table (disk->dev); +} + +static PedDeviceArchOps gnu_dev_ops = { + _new: gnu_new, + destroy: gnu_destroy, + is_busy: gnu_is_busy, + open: gnu_open, + refresh_open: gnu_refresh_open, + close: gnu_close, + refresh_close: gnu_refresh_close, + read: gnu_read, + write: gnu_write, + check: gnu_check, + sync: gnu_sync, + sync_fast: gnu_sync, + probe_all: gnu_probe_all +}; + +static PedDiskArchOps gnu_disk_ops = { + partition_get_path: gnu_partition_get_path, + partition_is_busy: gnu_partition_is_busy, + disk_commit: gnu_disk_commit +}; + +PedArchitecture ped_gnu_arch = { + dev_ops: &gnu_dev_ops, + disk_ops: &gnu_disk_ops +}; diff --git a/libparted/arch/linux.c b/libparted/arch/linux.c new file mode 100644 index 0000000..ccbba86 --- /dev/null +++ b/libparted/arch/linux.c @@ -0,0 +1,3413 @@ +/* libparted - a library for manipulating disk partitions + Copyright (C) 1999-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/>. +*/ + +#define PROC_DEVICES_BUFSIZ 16384 + +#include <config.h> +#include <arch/linux.h> +#include <linux/blkpg.h> +#include <parted/parted.h> +#include <parted/debug.h> +#if defined __s390__ || defined __s390x__ +#include <parted/fdasd.h> +#endif + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <stdio.h> +#include <syscall.h> +#include <unistd.h> +#include <stdbool.h> +#include <dirent.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> /* for uname() */ +#include <scsi/scsi.h> +#include <assert.h> +#include <sys/sysmacros.h> +#ifdef ENABLE_DEVICE_MAPPER +#include <libdevmapper.h> +#endif + +#include "../architecture.h" +#include "dirname.h" +#include "xstrtol.h" +#include "xalloc.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/* The __attribute__ feature is available in gcc versions 2.5 and later. + The __-protected variants of the attributes 'format' and 'printf' are + accepted by gcc versions 2.6.4 (effectively 2.7) and later. */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) +# define _GL_ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec)) +#else +# define _GL_ATTRIBUTE_FORMAT(spec) /* empty */ +#endif + +#define STRPREFIX(a, b) (strncmp (a, b, strlen (b)) == 0) + +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +#ifndef __NR__llseek +#define __NR__llseek 140 +#endif + +#ifndef SCSI_IOCTL_SEND_COMMAND +#define SCSI_IOCTL_SEND_COMMAND 1 +#endif + +/* from <linux/hdreg.h> */ +#define HDIO_GETGEO 0x0301 /* get device geometry */ +#define HDIO_GET_IDENTITY 0x030d /* get IDE identification info */ + +#define RD_MODE (O_RDONLY) +#define WR_MODE (O_WRONLY) +#define RW_MODE (O_RDWR) + +struct hd_geometry { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + unsigned long start; +}; + +struct ata7_sectinfo { + int valid1:1; + int valid2:1; + int rsv:26; + int multiplier:4; +}; + +/* structure returned by HDIO_GET_IDENTITY, as per ANSI ATA2 rev.2f spec */ +struct hd_driveid { + unsigned short config; /* lots of obsolete bit flags */ + unsigned short cyls; /* "physical" cyls */ + unsigned short reserved2; /* reserved (word 2) */ + unsigned short heads; /* "physical" heads */ + unsigned short track_bytes; /* unformatted bytes per track */ + unsigned short sector_bytes; /* unformatted bytes per sector */ + unsigned short sectors; /* "physical" sectors per track */ + unsigned short vendor0; /* vendor unique */ + unsigned short vendor1; /* vendor unique */ + unsigned short vendor2; /* vendor unique */ + unsigned char serial_no[20]; /* 0 = not_specified */ + unsigned short buf_type; + unsigned short buf_size; /* 512 byte increments; + 0 = not_specified */ + unsigned short ecc_bytes; /* for r/w long cmds; + 0 = not_specified */ + unsigned char fw_rev[8]; /* 0 = not_specified */ + char model[40]; /* 0 = not_specified */ + unsigned char max_multsect; /* 0=not_implemented */ + unsigned char vendor3; /* vendor unique */ + unsigned short dword_io; /* 0=not_implemented; 1=implemented */ + unsigned char vendor4; /* vendor unique */ + unsigned char capability; /* bits 0:DMA 1:LBA 2:IORDYsw + 3:IORDYsup*/ + unsigned short reserved50; /* reserved (word 50) */ + unsigned char vendor5; /* vendor unique */ + unsigned char tPIO; /* 0=slow, 1=medium, 2=fast */ + unsigned char vendor6; /* vendor unique */ + unsigned char tDMA; /* 0=slow, 1=medium, 2=fast */ + unsigned short field_valid; /* bits 0:cur_ok 1:eide_ok */ + unsigned short cur_cyls; /* logical cylinders */ + unsigned short cur_heads; /* logical heads */ + unsigned short cur_sectors; /* logical sectors per track */ + unsigned short cur_capacity0; /* logical total sectors on drive */ + unsigned short cur_capacity1; /* (2 words, misaligned int) */ + unsigned char multsect; /* current multiple sector count */ + unsigned char multsect_valid; /* when (bit0==1) multsect is ok */ + unsigned int lba_capacity; /* total number of sectors */ + unsigned short dma_1word; /* single-word dma info */ + unsigned short dma_mword; /* multiple-word dma info */ + unsigned short eide_pio_modes; /* bits 0:mode3 1:mode4 */ + unsigned short eide_dma_min; /* min mword dma cycle time (ns) */ + unsigned short eide_dma_time; /* recommended mword dma cycle + time (ns) */ + unsigned short eide_pio; /* min cycle time (ns), no IORDY */ + unsigned short eide_pio_iordy; /* min cycle time (ns), with IORDY */ + unsigned short words69_70[2]; /* reserved words 69-70 */ + /* HDIO_GET_IDENTITY currently returns only words 0 through 70 */ + unsigned short words71_74[4]; /* reserved words 71-74 */ + unsigned short queue_depth; /* */ + unsigned short words76_79[4]; /* reserved words 76-79 */ + unsigned short major_rev_num; /* */ + unsigned short minor_rev_num; /* */ + unsigned short command_set_1; /* bits 0:Smart 1:Security 2:Removable + 3:PM */ + unsigned short command_set_2; /* bits 14:Smart Enabled 13:0 zero */ + unsigned short cfsse; /* command set-feature supported + extensions */ + unsigned short cfs_enable_1; /* command set-feature enabled */ + unsigned short cfs_enable_2; /* command set-feature enabled */ + unsigned short csf_default; /* command set-feature default */ + unsigned short dma_ultra; /* */ + unsigned short word89; /* reserved (word 89) */ + unsigned short word90; /* reserved (word 90) */ + unsigned short CurAPMvalues; /* current APM values */ + unsigned short word92; /* reserved (word 92) */ + unsigned short hw_config; /* hardware config */ + unsigned short words94_105[12];/* reserved words 94-105 */ + struct ata7_sectinfo ata7_sectinfo; /* ATAPI/ATA7 physical and logical + sector size */ + unsigned short words107_116[10];/* reserved words 107-116 */ + unsigned int logical_sectsize;/* ATAPI/ATA7 logical sector size */ + unsigned short words119_125[7];/* reserved words 119-125 */ + unsigned short last_lun; /* reserved (word 126) */ + unsigned short word127; /* reserved (word 127) */ + unsigned short dlf; /* device lock function + * 15:9 reserved + * 8 security level 1:max 0:high + * 7:6 reserved + * 5 enhanced erase + * 4 expire + * 3 frozen + * 2 locked + * 1 en/disabled + * 0 capability + */ + unsigned short csfo; /* current set features options + * 15:4 reserved + * 3 auto reassign + * 2 reverting + * 1 read-look-ahead + * 0 write cache + */ + unsigned short words130_155[26];/* reserved vendor words 130-155 */ + unsigned short word156; + unsigned short words157_159[3]; /* reserved vendor words 157-159 */ + unsigned short words160_255[95];/* reserved words 160-255 */ +}; + +/* from <linux/fs.h> */ +#define BLKRRPART _IO(0x12,95) /* re-read partition table */ +#define BLKGETSIZE _IO(0x12,96) /* return device size */ +#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ +#define BLKSSZGET _IO(0x12,104) /* get block device sector size */ +#define BLKGETLASTSECT _IO(0x12,108) /* get last sector of block device */ +#define BLKSETLASTSECT _IO(0x12,109) /* set last sector of block device */ + +/* return device size in bytes (u64 *arg) */ +#define BLKGETSIZE64 _IOR(0x12,114,size_t) + +struct blkdev_ioctl_param { + unsigned int block; + size_t content_length; + char * block_contents; +}; + +/* from <linux/major.h> */ +#define IDE0_MAJOR 3 +#define IDE1_MAJOR 22 +#define IDE2_MAJOR 33 +#define IDE3_MAJOR 34 +#define IDE4_MAJOR 56 +#define IDE5_MAJOR 57 +#define SCSI_CDROM_MAJOR 11 +#define SCSI_DISK0_MAJOR 8 +#define SCSI_DISK1_MAJOR 65 +#define SCSI_DISK2_MAJOR 66 +#define SCSI_DISK3_MAJOR 67 +#define SCSI_DISK4_MAJOR 68 +#define SCSI_DISK5_MAJOR 69 +#define SCSI_DISK6_MAJOR 70 +#define SCSI_DISK7_MAJOR 71 +#define SCSI_DISK8_MAJOR 128 +#define SCSI_DISK9_MAJOR 129 +#define SCSI_DISK10_MAJOR 130 +#define SCSI_DISK11_MAJOR 131 +#define SCSI_DISK12_MAJOR 132 +#define SCSI_DISK13_MAJOR 133 +#define SCSI_DISK14_MAJOR 134 +#define SCSI_DISK15_MAJOR 135 +#define COMPAQ_SMART2_MAJOR 72 +#define COMPAQ_SMART2_MAJOR1 73 +#define COMPAQ_SMART2_MAJOR2 74 +#define COMPAQ_SMART2_MAJOR3 75 +#define COMPAQ_SMART2_MAJOR4 76 +#define COMPAQ_SMART2_MAJOR5 77 +#define COMPAQ_SMART2_MAJOR6 78 +#define COMPAQ_SMART2_MAJOR7 79 +#define COMPAQ_SMART_MAJOR 104 +#define COMPAQ_SMART_MAJOR1 105 +#define COMPAQ_SMART_MAJOR2 106 +#define COMPAQ_SMART_MAJOR3 107 +#define COMPAQ_SMART_MAJOR4 108 +#define COMPAQ_SMART_MAJOR5 109 +#define COMPAQ_SMART_MAJOR6 110 +#define COMPAQ_SMART_MAJOR7 111 +#define DAC960_MAJOR 48 +#define ATARAID_MAJOR 114 +#define I2O_MAJOR1 80 +#define I2O_MAJOR2 81 +#define I2O_MAJOR3 82 +#define I2O_MAJOR4 83 +#define I2O_MAJOR5 84 +#define I2O_MAJOR6 85 +#define I2O_MAJOR7 86 +#define I2O_MAJOR8 87 +#define UBD_MAJOR 98 +#define DASD_MAJOR 94 +#define VIODASD_MAJOR 112 +#define AOE_MAJOR 152 +#define SX8_MAJOR1 160 +#define SX8_MAJOR2 161 +#define XVD_MAJOR 202 +#define SDMMC_MAJOR 179 +#define LOOP_MAJOR 7 +#define MD_MAJOR 9 +#define BLKEXT_MAJOR 259 +#define RAM_MAJOR 1 + +#define SCSI_BLK_MAJOR(M) ( \ + (M) == SCSI_DISK0_MAJOR \ + || (M) == SCSI_CDROM_MAJOR \ + || ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) \ + || ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR)) + +/* Maximum number of partitions supported by linux. */ +#define MAX_NUM_PARTS 64 + +static char* _device_get_part_path (PedDevice const *dev, int num); +static int _partition_is_mounted_by_path (const char* path); +static unsigned int _device_get_partition_range(PedDevice const* dev); +static int _device_open (PedDevice* dev, int flags); +static int _device_open_ro (PedDevice* dev); +static int _device_close (PedDevice* dev); + +static int +_read_fd (int fd, char **buf) +{ + char* p; + size_t size = PROC_DEVICES_BUFSIZ; + int s, filesize = 0; + + *buf = malloc (size * sizeof (char)); + if (*buf == 0) { + return -1; + } + + do { + p = &(*buf) [filesize]; + s = read (fd, p, PROC_DEVICES_BUFSIZ); + /* exit if there is an error or EOF is reached */ + if (s <= 0) + break; + filesize += s; + size += s; + char *new_buf = realloc (*buf, size); + if (new_buf == NULL) { + int saved_errno = errno; + free (*buf); + errno = saved_errno; + return -1; + } + *buf = new_buf; + } while (1); + + if (filesize == 0 && s < 0) { + free (*buf); + *buf = NULL; + return -1; + } else { + char *new_buf = realloc (*buf, filesize + 1); + if (new_buf == NULL) { + int saved_errno = errno; + free (*buf); + errno = saved_errno; + return -1; + } + *buf = new_buf; + (*buf)[filesize] = '\0'; + } + + return filesize; +} + +static int +_major_type_in_devices (int major, const char* type) +{ + int fd; + char* buf = NULL; + char* line; + char* end; + int bd = 0; + char c; + + fd = open ("/proc/devices", O_RDONLY); + if (fd < 0) + return 0; + + if (_read_fd(fd, &buf) < 0) { + close(fd); + return 0; + } + + line = buf; + end = strchr(line, '\n'); + while (end) { + char *name; + int maj; + + c = *end; + *end = '\0'; + + if (!bd) { + if (!strncmp(line, "Block devices:", 14)) + bd = 1; + goto next; + } + + name = strrchr(line, ' '); + if (!name || strcmp(name+1, type)) + goto next; + + maj = strtol(line, &name, 10); + if (maj == major) { + free(buf); + close(fd); + return 1; + } + +next: + *end = c; + line = end+1; + end = strchr(line, '\n'); + } + free(buf); + close(fd); + return 0; +} + +static int +_is_ide_major (int major) +{ + switch (major) { + case IDE0_MAJOR: + case IDE1_MAJOR: + case IDE2_MAJOR: + case IDE3_MAJOR: + case IDE4_MAJOR: + case IDE5_MAJOR: + return 1; + + default: + return 0; + } +} + +static int +_is_cpqarray_major (int major) +{ + return ((COMPAQ_SMART2_MAJOR <= major && major <= COMPAQ_SMART2_MAJOR7) + || (COMPAQ_SMART_MAJOR <= major && major <= COMPAQ_SMART_MAJOR7)); +} + +static int +_is_i2o_major (int major) +{ + return (I2O_MAJOR1 <= major && major <= I2O_MAJOR8); +} + +static int +_is_sx8_major (int major) +{ + return (SX8_MAJOR1 <= major && major <= SX8_MAJOR2); +} + +static int +_is_virtblk_major (int major) +{ + return _major_type_in_devices (major, "virtblk"); +} + +static int +_is_blkext_major (int major) +{ + return _major_type_in_devices (major, "blkext"); +} + +#ifdef ENABLE_DEVICE_MAPPER +static int +_dm_task_run_wait (struct dm_task *task, uint32_t cookie) +{ + int rc = 0; + + rc = dm_task_run (task); + dm_udev_wait (cookie); + + return rc; +} + +static int +_is_dm_major (int major) +{ + return _major_type_in_devices (major, "device-mapper"); +} + +static int +_dm_maptype (PedDevice *dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + struct dm_task *dmt; + uint64_t start, length; + char *target_type = NULL; + char *params; + int r = -1; + const char* dev_dir = getenv ("DM_DEV_DIR"); + + if (dev_dir && *dev_dir && !dm_set_dev_dir(dev_dir)) + return r; + + if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) + return r; + + if (!dm_task_set_major_minor(dmt, arch_specific->major, + arch_specific->minor, 0)) + goto bad; + + dm_task_no_open_count(dmt); + + if (!dm_task_run(dmt)) + goto bad; + + dm_get_next_target(dmt, NULL, &start, &length, &target_type, ¶ms); + + arch_specific->dmtype = strdup(target_type ? target_type : "NO-TARGET"); + if (arch_specific->dmtype == NULL) + goto bad; + r = 0; +bad: + dm_task_destroy(dmt); + return r; +} + +/* Return nonzero if device-mapper device, DEVPATH, is part of a dmraid + array. Use the heuristic of checking for the string "DMRAID-" at the + start of its UUID. */ +static int +_is_dmraid_device (const char *devpath) +{ + int rc = 0; + + char const *dm_name = strrchr (devpath, '/'); + char const *dm_basename = dm_name && *(++dm_name) ? dm_name : devpath; + struct dm_task *task = dm_task_create (DM_DEVICE_DEPS); + if (!task) + return 0; + + dm_task_set_name (task, dm_basename); + if (!dm_task_run (task)) + goto err; + + const char *dmraid_uuid = dm_task_get_uuid (task); + if (STRPREFIX (dmraid_uuid, "DMRAID-")) + rc = 1; + +err: + dm_task_destroy (task); + return rc; +} + +/* We consider a dm device that is a linear mapping with a * + * single target that also is a dm device to be a partition */ + +static int +_dm_is_part (const char *path) +{ + int rc = 0; + struct dm_task *task = dm_task_create (DM_DEVICE_DEPS); + if (!task) + return 0; + + dm_task_set_name(task, path); + if (!dm_task_run(task)) + goto err; + + struct dm_info *info = alloca (sizeof *info); + memset(info, '\0', sizeof *info); + dm_task_get_info (task, info); + if (!info->exists) + goto err; + + struct dm_deps *deps = dm_task_get_deps (task); + if (!deps) + goto err; + + if (deps->count != 1) + goto err; + if (!_is_dm_major (major (deps->device[0]))) + goto err; + dm_task_destroy (task); + if (!(task = dm_task_create (DM_DEVICE_TABLE))) + return 0; + dm_task_set_name (task, path); + if (!dm_task_run (task)) + goto err; + + char *target_type = NULL; + char *params = NULL; + uint64_t start, length; + + dm_get_next_target (task, NULL, &start, &length, &target_type, ¶ms); + if (strcmp (target_type, "linear")) + goto err; + rc = 1; + +err: + dm_task_destroy(task); + return rc; +} + +static int +_probe_dm_devices () +{ + DIR* mapper_dir; + struct dirent* dent; + char buf [512]; /* readdir(3) claims d_name[256] */ + struct stat st; + + mapper_dir = opendir ("/dev/mapper"); + if (!mapper_dir) + return 0; + + /* Search the /dev/mapper directory for devices w/ the same major + * number that was returned from _probe_lvm_major(). + */ + while ((dent = readdir (mapper_dir))) { + if (strcmp (dent->d_name, ".") == 0 || + strcmp (dent->d_name, "..") == 0) + continue; + + snprintf (buf, sizeof (buf), "/dev/mapper/%s", dent->d_name); + + if (stat (buf, &st) != 0) + continue; + + if (_is_dm_major(major(st.st_rdev)) && _is_dmraid_device (buf) + && !_dm_is_part(buf)) + _ped_device_probe (buf); + } + closedir (mapper_dir); + + return 1; +} +#endif + +static int +_device_stat (PedDevice* dev, struct stat * dev_stat) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + + while (1) { + if (!stat (dev->path, dev_stat)) { + return 1; + } else { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_CANCEL, + _("Could not stat device %s - %s."), + dev->path, + strerror (errno)) + != PED_EXCEPTION_RETRY) + return 0; + } + } +} + +static int +_device_probe_type (PedDevice* dev) +{ + struct stat dev_stat; + int dev_major; + int dev_minor; + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + + if (!_device_stat (dev, &dev_stat)) + return 0; + + if (!S_ISBLK(dev_stat.st_mode)) { + dev->type = PED_DEVICE_FILE; + return 1; + } + + arch_specific->major = dev_major = major (dev_stat.st_rdev); + arch_specific->minor = dev_minor = minor (dev_stat.st_rdev); + + if (SCSI_BLK_MAJOR (dev_major) && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_SCSI; + } else if (_is_ide_major (dev_major) && (dev_minor % 0x40 == 0)) { + dev->type = PED_DEVICE_IDE; + } else if (dev_major == DAC960_MAJOR && (dev_minor % 0x8 == 0)) { + dev->type = PED_DEVICE_DAC960; + } else if (dev_major == ATARAID_MAJOR && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_ATARAID; + } else if (dev_major == AOE_MAJOR && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_AOE; + } else if (dev_major == DASD_MAJOR && (dev_minor % 0x4 == 0)) { + dev->type = PED_DEVICE_DASD; + } else if (dev_major == VIODASD_MAJOR && (dev_minor % 0x8 == 0)) { + dev->type = PED_DEVICE_VIODASD; + } else if (_is_sx8_major(dev_major) && (dev_minor % 0x20 == 0)) { + dev->type = PED_DEVICE_SX8; + } else if (_is_i2o_major (dev_major) && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_I2O; + } else if (_is_cpqarray_major (dev_major) && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_CPQARRAY; + } else if (dev_major == UBD_MAJOR && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_UBD; +#ifdef ENABLE_DEVICE_MAPPER + } else if (_is_dm_major(dev_major)) { + dev->type = PED_DEVICE_DM; + if (_dm_maptype(dev)) { + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Unable to determine the dm type of %s."), + dev->path); + } +#endif + } else if (dev_major == XVD_MAJOR && (dev_minor % 0x10 == 0)) { + dev->type = PED_DEVICE_XVD; + } else if (dev_major == SDMMC_MAJOR && (dev_minor % 0x08 == 0)) { + dev->type = PED_DEVICE_SDMMC; + } else if (_is_virtblk_major(dev_major)) { + dev->type = PED_DEVICE_VIRTBLK; + } else if (dev_major == LOOP_MAJOR) { + dev->type = PED_DEVICE_LOOP; + } else if (dev_major == MD_MAJOR) { + dev->type = PED_DEVICE_MD; + } else if (_is_blkext_major(dev_major) && dev->path && strstr(dev->path, "nvme")) { + dev->type = PED_DEVICE_NVME; + } else if (dev_major == RAM_MAJOR) { + dev->type = PED_DEVICE_RAM; + } else if (_is_blkext_major(dev_major) && dev->path && strstr(dev->path, "pmem")) { + dev->type = PED_DEVICE_PMEM; + } else { + dev->type = PED_DEVICE_UNKNOWN; + } + + return 1; +} + +static int +_get_linux_version () +{ + static int kver = -1; + + struct utsname uts; + unsigned int major = 0; + unsigned int minor = 0; + unsigned int teeny = 0; + + if (kver != -1) + return kver; + + if (uname (&uts)) + return kver = 0; + int n = sscanf (uts.release, "%u.%u.%u", &major, &minor, &teeny); + assert (n == 2 || n == 3); + return kver = KERNEL_VERSION (major, minor, teeny); +} + +#if USE_BLKID +static void +get_blkid_topology (LinuxSpecific *arch_specific) +{ + arch_specific->probe = blkid_new_probe (); + if (!arch_specific->probe) + return; + + if (blkid_probe_set_device(arch_specific->probe, + arch_specific->fd, 0, 0)) + return; + + arch_specific->topology = + blkid_probe_get_topology(arch_specific->probe); +} +#endif + +static void +_device_set_sector_size (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + int sector_size; + + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT; + + PED_ASSERT (dev->open_count); + + if (_get_linux_version() < KERNEL_VERSION (2,3,0)) { + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + return; + } + + if (ioctl (arch_specific->fd, BLKSSZGET, §or_size)) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("Could not determine sector size for %s: %s.\n" + "Using the default sector size (%lld)."), + dev->path, strerror (errno), PED_SECTOR_SIZE_DEFAULT); + } else { + dev->sector_size = (long long)sector_size; + dev->phys_sector_size = dev->sector_size; + } + +#if USE_BLKID + get_blkid_topology(arch_specific); + if (!arch_specific->topology) { + dev->phys_sector_size = 0; + } else { + dev->phys_sector_size = + blkid_topology_get_physical_sector_size( + arch_specific->topology); + } + if (dev->phys_sector_size == 0) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("Could not determine physical sector size for %s.\n" + "Using the logical sector size (%lld)."), + dev->path, dev->sector_size); + dev->phys_sector_size = dev->sector_size; + } +#endif + +#if defined __s390__ || defined __s390x__ + /* The real_sector_size is currently needed for DASD layouts, + * so we set it unconditionally. In the long run it should + * be considered to use the dev->phys_sector_size in label/dasd.c. + */ + arch_specific->real_sector_size = dev->sector_size; + /* Return PED_SECTOR_SIZE_DEFAULT for DASDs. */ + if (dev->type == PED_DEVICE_DASD) { + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + } +#endif +} + +static int +_kernel_has_blkgetsize64(void) +{ + int version = _get_linux_version(); + + if (version >= KERNEL_VERSION (2,5,4)) return 1; + if (version < KERNEL_VERSION (2,5,0) && + version >= KERNEL_VERSION (2,4,18)) return 1; + return 0; +} + +/* TODO: do a binary search if BLKGETSIZE doesn't work?! */ +static PedSector +_device_get_length (PedDevice* dev) +{ + unsigned long size; + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + uint64_t bytes=0; + const char* test_str; + PedSector test_size; + + + PED_ASSERT (dev->open_count > 0); + PED_ASSERT (dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + + test_str = getenv ("PARTED_TEST_DEVICE_LENGTH"); + if (test_str + && xstrtoll (test_str, NULL, 10, &test_size, NULL) == LONGINT_OK) + return test_size; + + if (_kernel_has_blkgetsize64()) { + if (ioctl(arch_specific->fd, BLKGETSIZE64, &bytes) == 0) { + return bytes / dev->sector_size; + } + } + + if (ioctl (arch_specific->fd, BLKGETSIZE, &size)) { + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Unable to determine the size of %s (%s)."), + dev->path, + strerror (errno)); + return 0; + } + + return size; +} + +static int +_device_probe_geometry (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + struct stat dev_stat; + struct hd_geometry geometry; + int geometry_is_valid = 0; + int sector_size = 0; + + if (!_device_stat (dev, &dev_stat)) + return 0; + PED_ASSERT (S_ISBLK (dev_stat.st_mode)); + + _device_set_sector_size (dev); + + dev->length = _device_get_length (dev); + if (!dev->length) + return 0; + + /* initialize the bios_geom values to something */ + dev->bios_geom.sectors = 0; + dev->bios_geom.heads = 0; + dev->bios_geom.cylinders = 0; + + geometry_is_valid = !ioctl (arch_specific->fd, HDIO_GETGEO, &geometry) + && geometry.sectors && geometry.heads; + +#if defined __s390__ || defined __s390x__ + if (geometry_is_valid) { +#else + if (!ioctl (arch_specific->fd, BLKSSZGET, §or_size)) { + /* get the sector count first */ + dev->bios_geom.sectors = 1 + (sector_size / PED_SECTOR_SIZE_DEFAULT); + dev->bios_geom.heads = 255; + } else if (geometry_is_valid) { + /* if BLKSSZGET failed, use deprecated HDIO_GETGEO result */ +#endif + dev->bios_geom.sectors = geometry.sectors; + dev->bios_geom.heads = geometry.heads; + } else { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("Could not determine sector size for %s: %s.\n" + "Using the default sector size (%lld)."), + dev->path, strerror (errno), PED_SECTOR_SIZE_DEFAULT); + dev->bios_geom.sectors = 2; + dev->bios_geom.heads = 255; + } + + dev->bios_geom.cylinders + = dev->length / (dev->bios_geom.heads + * dev->bios_geom.sectors); + dev->hw_geom = dev->bios_geom; + return 1; +} + +static char* +strip_name(char* str) +{ + int i; + int end = 0; + + for (i = 0; str[i] != 0; i++) { + if (!isspace (str[i]) + || (isspace (str[i]) && !isspace (str[i+1]) && str[i+1])) { + str [end] = str[i]; + end++; + } + } + str[end] = 0; + return strdup (str); +} + +static int +init_ide (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + struct stat dev_stat; + struct hd_driveid hdi; + PedExceptionOption ex_status; + char hdi_buf[41]; + int sector_multiplier = 0; + int r; + + if (!_device_stat (dev, &dev_stat)) + goto error; + + if (!_device_open_ro (dev)) + goto error; + + r = ioctl (arch_specific->fd, HDIO_GET_IDENTITY, &hdi); + if (r && errno == EINVAL) { + /* silently ignore unsupported ioctl */ + dev->model = strdup(_("Generic IDE")); + } else if (r) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Could not get identity of device %s - %s"), + dev->path, strerror (errno)); + switch (ex_status) { + case PED_EXCEPTION_CANCEL: + goto error_close_dev; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + /* FALLTHROUGH */ + case PED_EXCEPTION_IGNORE: + dev->model = strdup(_("Generic IDE")); + break; + default: + PED_ASSERT (0); + break; + } + } else { + /* hdi.model is not guaranteed to be NULL terminated */ + memcpy (hdi_buf, hdi.model, 40); + hdi_buf[40] = '\0'; + dev->model = strip_name (hdi_buf); + + if (!hdi.ata7_sectinfo.valid1 && hdi.ata7_sectinfo.valid2) + sector_multiplier = hdi.ata7_sectinfo.multiplier; + else + sector_multiplier = 1; + + if (sector_multiplier != 1) { + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Device %s has multiple (%d) logical sectors " + "per physical sector.\n" + "GNU Parted supports this EXPERIMENTALLY for " + "some special disk label/file system " + "combinations, e.g. GPT and ext2/3.\n" + "Please consult the web site for up-to-date " + "information."), + dev->path, sector_multiplier); + + switch (ex_status) { + case PED_EXCEPTION_CANCEL: + goto error_close_dev; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + /* FALLTHROUGH */ + case PED_EXCEPTION_IGNORE: + break; + default: + PED_ASSERT (0); + break; + } + } + + /* XXX sector_size has not been set yet! */ + /* dev->phys_sector_size = dev->sector_size + * sector_multiplier;*/ + dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT; + } + + if (!_device_probe_geometry (dev)) + goto error_close_dev; + + _device_close (dev); + return 1; + +error_close_dev: + _device_close (dev); +error: + return 0; +} + +/* This function reads the /sys entry named "file" for device "dev". */ +static char * +read_device_sysfs_file (PedDevice *dev, const char *file) +{ + FILE *f; + char name_buf[128]; + char buf[256]; + + snprintf (name_buf, 127, "/sys/block/%s/device/%s", + last_component (dev->path), file); + + if ((f = fopen (name_buf, "r")) == NULL) + return NULL; + + if (fgets (buf, 255, f) == NULL) { + fclose (f); + return NULL; + } + + fclose (f); + return strip_name (buf); +} + +/* This function sends a query to a SCSI device for vendor and product + * information. It uses the deprecated SCSI_IOCTL_SEND_COMMAND to + * issue this query. + */ +static int +scsi_query_product_info (PedDevice* dev, char **vendor, char **product) +{ + /* The following are defined by the SCSI-2 specification. */ + typedef struct _scsi_inquiry_cmd + { + uint8_t op; + uint8_t lun; /* bits 5-7 denote the LUN */ + uint8_t page_code; + uint8_t reserved; + uint8_t alloc_length; + uint8_t control; + } __attribute__((packed)) scsi_inquiry_cmd_t; + + typedef struct _scsi_inquiry_data + { + uint8_t peripheral_info; + uint8_t device_info; + uint8_t version_info; + uint8_t _field1; + uint8_t additional_length; + uint8_t _reserved1; + uint8_t _reserved2; + uint8_t _field2; + uint8_t vendor_id[8]; + uint8_t product_id[16]; + uint8_t product_revision[4]; + uint8_t vendor_specific[20]; + uint8_t _reserved3[40]; + } __attribute__((packed)) scsi_inquiry_data_t; + + struct scsi_arg + { + unsigned int inlen; + unsigned int outlen; + + union arg_data + { + scsi_inquiry_data_t out; + scsi_inquiry_cmd_t in; + } data; + } arg; + + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + char buf[32]; + + *vendor = NULL; + *product = NULL; + + memset (&arg, 0x00, sizeof(struct scsi_arg)); + arg.inlen = 0; + arg.outlen = sizeof(scsi_inquiry_data_t); + arg.data.in.op = INQUIRY; + arg.data.in.lun = dev->host << 5; + arg.data.in.alloc_length = sizeof(scsi_inquiry_data_t); + arg.data.in.page_code = 0; + arg.data.in.reserved = 0; + arg.data.in.control = 0; + + if (ioctl (arch_specific->fd, SCSI_IOCTL_SEND_COMMAND, &arg) < 0) + return 0; + + memcpy (buf, arg.data.out.vendor_id, 8); + buf[8] = '\0'; + *vendor = strip_name (buf); + + memcpy (buf, arg.data.out.product_id, 16); + buf[16] = '\0'; + *product = strip_name (buf); + + return 1; +} + +/* This function provides the vendor and product name for a SCSI device. + * It supports both the modern /sys interface and direct queries + * via the deprecated ioctl, SCSI_IOCTL_SEND_COMMAND. + */ +static int +scsi_get_product_info (PedDevice* dev, char **vendor, char **product) +{ + *vendor = read_device_sysfs_file (dev, "vendor"); + *product = read_device_sysfs_file (dev, "model"); + if (*vendor && *product) + return 1; + + return scsi_query_product_info (dev, vendor, product); +} + +static int +init_scsi (PedDevice* dev) +{ + struct scsi_idlun + { + uint32_t dev_id; + uint32_t host_unique_id; + } idlun; + + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + char* vendor; + char* product; + + if (!_device_open_ro (dev)) + goto error; + + if (ioctl (arch_specific->fd, SCSI_IOCTL_GET_IDLUN, &idlun) < 0) { + dev->host = 0; + dev->did = 0; + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL, + _("Error initialising SCSI device %s - %s"), + dev->path, strerror (errno)) + != PED_EXCEPTION_IGNORE) + goto error_close_dev; + if (!_device_probe_geometry (dev)) + goto error_close_dev; + _device_close (dev); + return 1; + } + + dev->host = idlun.host_unique_id; + dev->did = idlun.dev_id; + + dev->model = (char*) ped_malloc (8 + 16 + 2); + if (!dev->model) + goto error_close_dev; + + if (scsi_get_product_info (dev, &vendor, &product)) { + sprintf (dev->model, "%.8s %.16s", vendor, product); + free (vendor); + free (product); + } else { + strcpy (dev->model, "Generic SCSI"); + } + + if (!_device_probe_geometry (dev)) + goto error_close_dev; + + _device_close (dev); + return 1; + +error_close_dev: + _device_close (dev); +error: + return 0; +} + +static int +init_file (PedDevice* dev) +{ + struct stat dev_stat; + + if (!_device_stat (dev, &dev_stat)) + goto error; + if (!_device_open_ro (dev)) + goto error; + + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + char *p = getenv ("PARTED_SECTOR_SIZE"); + if (p) { + int s = atoi (p); + if (0 < s && s % 512 == 0) + dev->sector_size = s; + } + dev->phys_sector_size = dev->sector_size; + + if (S_ISBLK(dev_stat.st_mode)) + dev->length = _device_get_length (dev); + else + dev->length = dev_stat.st_size / dev->sector_size; + if (dev->length <= 0) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The device %s is so small that it cannot possibly " + "store a file system or partition table. Perhaps " + "you selected the wrong device?"), + dev->path); + goto error_close_dev; + } + + _device_close (dev); + + dev->bios_geom.cylinders = dev->length / 4 / 32; + dev->bios_geom.heads = 4; + dev->bios_geom.sectors = 32; + dev->hw_geom = dev->bios_geom; + dev->model = strdup (""); + + return 1; + +error_close_dev: + _device_close (dev); +error: + return 0; +} + +#if defined __s390__ || defined __s390x__ +static int +init_dasd (PedDevice* dev, const char* model_name) +{ + struct stat dev_stat; + struct hd_geometry geo; + dasd_information_t dasd_info; + + if (!_device_stat (dev, &dev_stat)) + goto error; + + if (!_device_open_ro (dev)) + goto error; + + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + + PED_ASSERT (S_ISBLK (dev_stat.st_mode)); + + _device_set_sector_size (dev); + if (!dev->sector_size) + goto error_close_dev; + + dev->length = _device_get_length (dev); + if (!dev->length) + goto error_close_dev; + + if (!ioctl (arch_specific->fd, HDIO_GETGEO, &geo)) { + dev->hw_geom.sectors = geo.sectors; + dev->hw_geom.heads = geo.heads; + dev->hw_geom.cylinders = dev->length + / (dev->hw_geom.heads * dev->hw_geom.sectors) + / (dev->sector_size / PED_SECTOR_SIZE_DEFAULT); + dev->bios_geom = dev->hw_geom; + } else { + dev->bios_geom.sectors = 12; + dev->bios_geom.heads = 15; + dev->bios_geom.cylinders = dev->length + / (dev->hw_geom.heads * dev->hw_geom.sectors) + / (dev->sector_size / PED_SECTOR_SIZE_DEFAULT); + dev->hw_geom = dev->bios_geom; + } + + if (!ioctl(arch_specific->fd, BIODASDINFO, &dasd_info)) { + arch_specific->devno = dasd_info.devno; + } else { + arch_specific->devno = arch_specific->major * 256 + + arch_specific->minor; + } + + dev->model = strdup (model_name); + + _device_close (dev); + return 1; + +error_close_dev: + _device_close (dev); +error: + return 0; +} +#endif + +static int +init_generic (PedDevice* dev, const char* model_name) +{ + struct stat dev_stat; + PedExceptionOption ex_status; + + if (!_device_stat (dev, &dev_stat)) + goto error; + + if (!_device_open_ro (dev)) + goto error; + + ped_exception_fetch_all (); + if (_device_probe_geometry (dev)) { + ped_exception_leave_all (); + } else { + if (!_device_get_length (dev)) { + ped_exception_catch (); + ped_exception_leave_all (); + goto error_close_dev; + } + + /* hack to allow use of files, for testing */ + ped_exception_catch (); + ped_exception_leave_all (); + + ex_status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Unable to determine geometry of " + "file/device %s. You should not use Parted " + "unless you REALLY know what you're doing!"), + dev->path); + switch (ex_status) { + case PED_EXCEPTION_CANCEL: + goto error_close_dev; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + /* FALLTHROUGH */ + case PED_EXCEPTION_IGNORE: + break; + default: + PED_ASSERT (0); + break; + } + + /* what should we stick in here? */ + dev->length = dev_stat.st_size / PED_SECTOR_SIZE_DEFAULT; + dev->bios_geom.cylinders = dev->length / 4 / 32; + dev->bios_geom.heads = 4; + dev->bios_geom.sectors = 32; + dev->sector_size = PED_SECTOR_SIZE_DEFAULT; + dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT; + } + + dev->model = strdup (model_name); + + _device_close (dev); + return 1; + +error_close_dev: + _device_close (dev); +error: + return 0; +} + +static int +sdmmc_get_product_info (PedDevice* dev, char **type, char **name) +{ + *type = read_device_sysfs_file (dev, "type"); + *name = read_device_sysfs_file (dev, "name"); + if (*type && *name) + return 1; + + return 0; +} + +static int +init_sdmmc (PedDevice* dev) +{ + char id[128]; + char *type = NULL; + char *name = NULL; + + if (sdmmc_get_product_info (dev, &type, &name)) { + snprintf (id, sizeof(id) - 1, "%s %s", type, name); + } else { + snprintf (id, sizeof(id) - 1, "%s", + _("Generic SD/MMC Storage Card")); + } + free (type); + free (name); + return init_generic(dev, id); +} + +static int +init_nvme (PedDevice* dev) +{ + int ret; + char *model = read_device_sysfs_file (dev, "model"); + + if (!model) + ret = init_generic (dev, _("NVMe Device")); + else { + ret = init_generic (dev, model); + free (model); + } + + return ret; +} + +static PedDevice* +linux_new (const char* path) +{ + PedDevice* dev; + LinuxSpecific* arch_specific; + + PED_ASSERT (path != NULL); + + dev = (PedDevice*) ped_malloc (sizeof (PedDevice)); + if (!dev) + goto error; + + dev->path = strdup (path); + if (!dev->path) + goto error_free_dev; + + dev->arch_specific + = (LinuxSpecific*) ped_malloc (sizeof (LinuxSpecific)); + if (!dev->arch_specific) + goto error_free_path; + arch_specific = LINUX_SPECIFIC (dev); + arch_specific->dmtype = NULL; +#if USE_BLKID + arch_specific->probe = NULL; + arch_specific->topology = NULL; +#endif + + dev->open_count = 0; + dev->read_only = 0; + dev->external_mode = 0; + dev->dirty = 0; + dev->boot_dirty = 0; + +#ifdef ENABLE_DEVICE_MAPPER + dm_udev_set_sync_support(1); +#endif + + if (!_device_probe_type (dev)) + goto error_free_arch_specific; + + switch (dev->type) { + case PED_DEVICE_IDE: + if (!init_ide (dev)) + goto error_free_arch_specific; + break; + + case PED_DEVICE_SCSI: + if (!init_scsi (dev)) + goto error_free_arch_specific; + break; + + case PED_DEVICE_DAC960: + if (!init_generic (dev, _("DAC960 RAID controller"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_SX8: + if (!init_generic (dev, _("Promise SX8 SATA Device"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_AOE: + if (!init_generic (dev, _("ATA over Ethernet Device"))) + goto error_free_arch_specific; + break; + +#if defined __s390__ || defined __s390x__ + case PED_DEVICE_DASD: + if (!init_dasd (dev, _("IBM S390 DASD drive"))) + goto error_free_arch_specific; + break; +#endif + + case PED_DEVICE_VIODASD: + if (!init_generic (dev, _("IBM iSeries Virtual DASD"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_CPQARRAY: + if (!init_generic (dev, _("Compaq Smart Array"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_NVME: + if (!init_nvme (dev)) + goto error_free_arch_specific; + break; + + case PED_DEVICE_PMEM: + if (!init_generic (dev, _("NVDIMM Device"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_ATARAID: + if (!init_generic (dev, _("ATARAID Controller"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_I2O: + if (!init_generic (dev, _("I2O Controller"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_UBD: + if (!init_generic (dev, _("User-Mode Linux UBD"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_FILE: + if (!init_file (dev)) + goto error_free_arch_specific; + break; + + case PED_DEVICE_LOOP: + if (!init_generic (dev, _("Loopback device"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_DM: + { + char* type; + if (arch_specific->dmtype == NULL + || asprintf(&type, _("Linux device-mapper (%s)"), + arch_specific->dmtype) == -1) + goto error_free_arch_specific; + bool ok = init_generic (dev, type); + free (type); + if (!ok) + goto error_free_arch_specific; + break; + } + + case PED_DEVICE_XVD: + if (!init_generic (dev, _("Xen Virtual Block Device"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_UNKNOWN: + if (!init_generic (dev, _("Unknown"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_SDMMC: + if (!init_sdmmc (dev)) + goto error_free_arch_specific; + break; + case PED_DEVICE_VIRTBLK: + if (!init_generic(dev, _("Virtio Block Device"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_MD: + if (!init_generic(dev, _("Linux Software RAID Array"))) + goto error_free_arch_specific; + break; + + case PED_DEVICE_RAM: + if (!init_generic (dev, _("RAM Drive"))) + goto error_free_arch_specific; + break; + + default: + ped_exception_throw (PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("ped_device_new() Unsupported device type")); + goto error_free_arch_specific; + } + return dev; + +error_free_arch_specific: + free (dev->arch_specific); +error_free_path: + free (dev->path); +error_free_dev: + free (dev); +error: + return NULL; +} + +static void +linux_destroy (PedDevice* dev) +{ + LinuxSpecific *arch_specific = LINUX_SPECIFIC(dev); + void *p = arch_specific->dmtype; + +#if USE_BLKID + if (arch_specific->probe) + blkid_free_probe(arch_specific->probe); +#endif + free (p); + free (dev->arch_specific); + free (dev->path); + free (dev->model); + free (dev); +} + +static int +linux_is_busy (PedDevice* dev) +{ + int i; + char* part_name; + + if (_partition_is_mounted_by_path (dev->path)) + return 1; + + for (i = 0; i < 32; i++) { + int status; + + part_name = _device_get_part_path (dev, i); + if (!part_name) + return 1; + status = _partition_is_mounted_by_path (part_name); + free (part_name); + + if (status) + return 1; + } + + return 0; +} + +/* we need to flush the master device, and all the partition devices, + * because there is no coherency between the caches. + * We should only flush unmounted partition devices, because: + * - there is never a need to flush them (we're not doing IO there) + * - flushing a device that is mounted causes unnecessary IO, and can + * even screw journaling & friends up. Even cause oopsen! + */ +static void +_flush_cache (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + int i; + int lpn = _device_get_partition_range(dev); + + if (dev->read_only || dev->type == PED_DEVICE_RAM) + return; + dev->dirty = 0; + + ioctl (arch_specific->fd, BLKFLSBUF); + + for (i = 1; i < lpn; i++) { + char* name; + int fd; + + name = _device_get_part_path (dev, i); + if (!name) + break; + if (!_partition_is_mounted_by_path (name)) { + fd = open (name, WR_MODE, 0); + if (fd > -1) { + ioctl (fd, BLKFLSBUF); +retry: + if (fsync (fd) < 0 || close (fd) < 0) + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_RETRY + + PED_EXCEPTION_IGNORE, + _("Error fsyncing/closing %s: %s"), + name, strerror (errno)) + == PED_EXCEPTION_RETRY) + goto retry; + } + } + free (name); + } +} + +static int +_device_open_ro (PedDevice* dev) +{ + int rc = _device_open (dev, RD_MODE); + if (rc) + dev->open_count++; + return rc; +} + +static int +linux_open (PedDevice* dev) +{ + return _device_open (dev, RW_MODE); +} + +static int +_device_open (PedDevice* dev, int flags) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + +retry: + arch_specific->fd = open (dev->path, flags); + + if (arch_specific->fd == -1) { + char* rw_error_msg = strerror (errno); + + arch_specific->fd = open (dev->path, RD_MODE); + + if (arch_specific->fd == -1) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_CANCEL, + _("Error opening %s: %s"), + dev->path, strerror (errno)) + != PED_EXCEPTION_RETRY) { + return 0; + } else { + goto retry; + } + } else { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("Unable to open %s read-write (%s). %s has " + "been opened read-only."), + dev->path, rw_error_msg, dev->path); + dev->read_only = 1; + } + } else { + dev->read_only = 0; + } + + _flush_cache (dev); + + return 1; +} + +static int +linux_refresh_open (PedDevice* dev) +{ + return 1; +} + +static int +linux_close (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + + if (dev->dirty) + _flush_cache (dev); +retry: + if (fsync (arch_specific->fd) < 0 || close (arch_specific->fd) < 0) + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_RETRY + PED_EXCEPTION_IGNORE, + _("Error fsyncing/closing %s: %s"), + dev->path, strerror (errno)) + == PED_EXCEPTION_RETRY) + goto retry; + return 1; +} + +static int +linux_refresh_close (PedDevice* dev) +{ + if (dev->dirty) + _flush_cache (dev); + return 1; +} + +static int +_device_close (PedDevice* dev) +{ + int rc = linux_close (dev); + if (dev->open_count > 0) + dev->open_count--; + return rc; +} + +#if SIZEOF_OFF_T < 8 + +static _syscall5(int,_llseek, + unsigned int, fd, + unsigned long, offset_high, + unsigned long, offset_low, + loff_t*, result, + unsigned int, origin) + +loff_t +llseek (unsigned int fd, loff_t offset, unsigned int whence) +{ + loff_t result; + int retval; + + retval = _llseek(fd, + ((unsigned long long)offset) >> 32, + ((unsigned long long)offset) & 0xffffffff, + &result, + whence); + return (retval==-1 ? (loff_t) retval : result); +} + +#endif /* SIZEOF_OFF_T < 8 */ + +static int +_device_seek (const PedDevice* dev, PedSector sector) +{ + LinuxSpecific* arch_specific; + + PED_ASSERT (dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + + arch_specific = LINUX_SPECIFIC (dev); + +#if SIZEOF_OFF_T < 8 + if (sizeof (off_t) < 8) { + loff_t pos = (loff_t)(sector * dev->sector_size); + return llseek (arch_specific->fd, pos, SEEK_SET) == pos; + } else +#endif + { + off_t pos = sector * dev->sector_size; + return lseek (arch_specific->fd, pos, SEEK_SET) == pos; + } +} + +static int +_read_lastoddsector (const PedDevice* dev, void* buffer) +{ + LinuxSpecific* arch_specific; + struct blkdev_ioctl_param ioctl_param; + + PED_ASSERT(dev != NULL); + PED_ASSERT(buffer != NULL); + + arch_specific = LINUX_SPECIFIC (dev); + +retry: + ioctl_param.block = 0; /* read the last sector */ + ioctl_param.content_length = dev->sector_size; + ioctl_param.block_contents = buffer; + + if (ioctl(arch_specific->fd, BLKGETLASTSECT, &ioctl_param) == -1) { + PedExceptionOption opt; + opt = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during read on %s"), + strerror (errno), dev->path); + + if (opt == PED_EXCEPTION_CANCEL) + return 0; + if (opt == PED_EXCEPTION_RETRY) + goto retry; + } + + return 1; +} + +static int +linux_read (const PedDevice* dev, void* buffer, PedSector start, + PedSector count) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + PedExceptionOption ex_status; + void* diobuf = NULL; + + PED_ASSERT (dev != NULL); + PED_ASSERT (dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + + if (_get_linux_version() < KERNEL_VERSION (2,6,0)) { + /* Kludge. This is necessary to read/write the last + block of an odd-sized disk, until Linux 2.5.x kernel fixes. + */ + if (dev->type != PED_DEVICE_FILE && (dev->length & 1) + && start + count - 1 == dev->length - 1) + return ped_device_read (dev, buffer, start, count - 1) + && _read_lastoddsector ( + dev, (char *) buffer + (count-1) * 512); + } + while (1) { + if (_device_seek (dev, start)) + break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during seek for read on %s"), + strerror (errno), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + /* FALLTHROUGH */ + case PED_EXCEPTION_CANCEL: + return 0; + default: + PED_ASSERT (0); + break; + } + } + + size_t read_length = count * dev->sector_size; + if (posix_memalign (&diobuf, dev->sector_size, read_length) != 0) + return 0; + + while (1) { + ssize_t status = read (arch_specific->fd, diobuf, read_length); + if (status > 0) + memcpy(buffer, diobuf, status); + if (status == (ssize_t) read_length) + break; + if (status > 0) { + read_length -= status; + buffer = (char *) buffer + status; + continue; + } + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + (status == 0 + ? _("%0.0send of file while reading %s") + : _("%s during read on %s")), + strerror (errno), + dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + free(diobuf); + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + /* FALLTHROUGH */ + case PED_EXCEPTION_CANCEL: + free(diobuf); + return 0; + default: + PED_ASSERT (0); + break; + } + } + + free (diobuf); + + return 1; +} + +static int +_write_lastoddsector (PedDevice* dev, const void* buffer) +{ + LinuxSpecific* arch_specific; + struct blkdev_ioctl_param ioctl_param; + + PED_ASSERT(dev != NULL); + PED_ASSERT(buffer != NULL); + + arch_specific = LINUX_SPECIFIC (dev); + +retry: + ioctl_param.block = 0; /* write the last sector */ + ioctl_param.content_length = dev->sector_size; + ioctl_param.block_contents = (void*) buffer; + + if (ioctl(arch_specific->fd, BLKSETLASTSECT, &ioctl_param) == -1) { + PedExceptionOption opt; + opt = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (errno), dev->path); + + if (opt == PED_EXCEPTION_CANCEL) + return 0; + if (opt == PED_EXCEPTION_RETRY) + goto retry; + } + + return 1; +} + +static int +linux_write (PedDevice* dev, const void* buffer, PedSector start, + PedSector count) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + PedExceptionOption ex_status; + void* diobuf; + void* diobuf_start; + + PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + + if (dev->read_only) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Can't write to %s, because it is opened read-only."), + dev->path) + != PED_EXCEPTION_IGNORE) + return 0; + else + return 1; + } + + if (_get_linux_version() < KERNEL_VERSION (2,6,0)) { + /* Kludge. This is necessary to read/write the last + block of an odd-sized disk, until Linux 2.5.x kernel fixes. + */ + if (dev->type != PED_DEVICE_FILE && (dev->length & 1) + && start + count - 1 == dev->length - 1) + return ped_device_write (dev, buffer, start, count - 1) + && _write_lastoddsector ( + dev, ((char*) buffer + + (count-1) * dev->sector_size)); + } + while (1) { + if (_device_seek (dev, start)) + break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during seek for write on %s"), + strerror (errno), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + /* FALLTHROUGH */ + case PED_EXCEPTION_CANCEL: + return 0; + default: + PED_ASSERT (0); + break; + } + } + +#ifdef READ_ONLY + printf ("ped_device_write (\"%s\", %p, %d, %d)\n", + dev->path, buffer, (int) start, (int) count); +#else + size_t write_length = count * dev->sector_size; + dev->dirty = 1; + if (posix_memalign(&diobuf, dev->sector_size, write_length) != 0) + return 0; + memcpy(diobuf, buffer, write_length); + diobuf_start = diobuf; + while (1) { + ssize_t status = write (arch_specific->fd, diobuf, write_length); + if (status == write_length) break; + if (status > 0) { + write_length -= status; + diobuf = (char *) diobuf + status; + continue; + } + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (errno), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + free(diobuf_start); + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + /* FALLTHROUGH */ + case PED_EXCEPTION_CANCEL: + free(diobuf_start); + return 0; + default: + PED_ASSERT (0); + break; + } + } + free(diobuf_start); +#endif /* !READ_ONLY */ + return 1; +} + +/* returns the number of sectors that are ok. + */ +static PedSector +linux_check (PedDevice* dev, void* buffer, PedSector start, PedSector count) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + PedSector done = 0; + int status; + void* diobuf; + + PED_ASSERT(dev != NULL); + + if (!_device_seek (dev, start)) + return 0; + + if (posix_memalign(&diobuf, PED_SECTOR_SIZE_DEFAULT, + count * PED_SECTOR_SIZE_DEFAULT) != 0) + return 0; + + for (done = 0; done < count; done += status / dev->sector_size) { + status = read (arch_specific->fd, diobuf, + (size_t) ((count-done) * dev->sector_size)); + if (status > 0) + memcpy(buffer, diobuf, status); + if (status < 0) + break; + } + free(diobuf); + + return done; +} + +static int +_do_fsync (PedDevice* dev) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + int status; + PedExceptionOption ex_status; + + while (1) { + status = fsync (arch_specific->fd); + if (status >= 0) break; + + ex_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_RETRY_IGNORE_CANCEL, + _("%s during write on %s"), + strerror (errno), dev->path); + + switch (ex_status) { + case PED_EXCEPTION_IGNORE: + return 1; + + case PED_EXCEPTION_RETRY: + break; + + case PED_EXCEPTION_UNHANDLED: + ped_exception_catch (); + /* FALLTHROUGH */ + case PED_EXCEPTION_CANCEL: + return 0; + default: + PED_ASSERT (0); + break; + } + } + return 1; +} + +static int +linux_sync (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + + if (dev->read_only) + return 1; + if (!_do_fsync (dev)) + return 0; + _flush_cache (dev); + return 1; +} + +static int +linux_sync_fast (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + + if (dev->read_only) + return 1; + if (!_do_fsync (dev)) + return 0; + /* no cache flush... */ + return 1; +} + +static inline int +_compare_digit_state (char ch, int need_digit) +{ + return !!isdigit (ch) == need_digit; +} + +/* matches the regexp "[^0-9]+[0-9]+[^0-9]+[0-9]+$". + * Motivation: accept devices looking like /dev/rd/c0d0, but + * not looking like /dev/hda1 and /dev/rd/c0d0p1 + */ +static int _GL_ATTRIBUTE_PURE +_match_rd_device (const char* name) +{ + const char* pos; + int state; + + /* exclude directory names from test */ + pos = strrchr(name, '/') ?: name; + + /* states: + * 0 non-digits + * 1 digits + * 2 non-digits + * 3 digits + */ + for (state = 0; state < 4; state++) { + int want_digits = (state % 2 == 1); + do { + if (!*pos) + return 0; + if (!_compare_digit_state (*pos, want_digits)) + return 0; + pos++; + } while (_compare_digit_state (*pos, want_digits)); + } + + return *pos == 0; +} + +static int +_probe_proc_partitions () +{ + FILE* proc_part_file; + int major, minor, size; + char buf [512]; + char part_name [256]; + char dev_name [256]; + int ok = 0; + + proc_part_file = fopen ("/proc/partitions", "r"); + if (!proc_part_file) + return 0; + + if (fgets (buf, 256, proc_part_file) == NULL) + goto done; + + if (fgets (buf, 256, proc_part_file) == NULL) + goto done; + + while (fgets (buf, 512, proc_part_file) + && sscanf (buf, "%d %d %d %255s", &major, &minor, &size, + part_name) == 4) { + /* Heuristic for telling partitions and devices apart + * Probably needs to be improved + */ + if (!_match_rd_device (part_name) + && isdigit (part_name [strlen (part_name) - 1])) + continue; + + strcpy (dev_name, "/dev/"); + strcat (dev_name, part_name); + _ped_device_probe (dev_name); + } + + ok = 1; + done: + fclose (proc_part_file); + return ok; +} + +struct _entry { + const char *name; + size_t len; +}; + +static int _GL_ATTRIBUTE_PURE +_skip_entry (const char *name) +{ + struct _entry *i; + static struct _entry entries[] = { + { ".", sizeof (".") - 1 }, + { "..", sizeof ("..") - 1 }, + { "dm-", sizeof ("dm-") - 1 }, + { "loop", sizeof ("loop") - 1 }, + { "ram", sizeof ("ram") - 1 }, + { "fd", sizeof ("fd") - 1 }, + { 0, 0 }, + }; + + for (i = entries; i->name != 0; i++) { + if (strncmp (name, i->name, i->len) == 0) + return 1; + } + + return 0; +} + +static int +_probe_sys_block () +{ + DIR *blockdir; + struct dirent *dirent; + char dev_name [256]; + char *ptr; + + if (!(blockdir = opendir ("/sys/block"))) + return 0; + while ((dirent = readdir (blockdir))) { + if (_skip_entry (dirent->d_name)) + continue; + + if (strlen (dirent->d_name) > sizeof (dev_name) - 6) + continue; /* device name too long! */ + + strcpy (dev_name, "/dev/"); + strcat (dev_name, dirent->d_name); + /* in /sys/block, '/'s are replaced with '!' */ + for (ptr = dev_name; *ptr != '\0'; ptr++) { + if (*ptr == '!') + *ptr = '/'; + } + _ped_device_probe (dev_name); + } + + closedir (blockdir); + return 1; +} + +static int +_probe_standard_devices () +{ + _ped_device_probe ("/dev/hda"); + _ped_device_probe ("/dev/hdb"); + _ped_device_probe ("/dev/hdc"); + _ped_device_probe ("/dev/hdd"); + _ped_device_probe ("/dev/hde"); + _ped_device_probe ("/dev/hdf"); + _ped_device_probe ("/dev/hdg"); + _ped_device_probe ("/dev/hdh"); + + _ped_device_probe ("/dev/sda"); + _ped_device_probe ("/dev/sdb"); + _ped_device_probe ("/dev/sdc"); + _ped_device_probe ("/dev/sdd"); + _ped_device_probe ("/dev/sde"); + _ped_device_probe ("/dev/sdf"); + + return 1; +} + +static void +linux_probe_all () +{ + /* we should probe the standard devs too, even with /proc/partitions, + * because /proc/partitions might return devfs stuff, and we might not + * have devfs available + */ + _probe_standard_devices (); + +#ifdef ENABLE_DEVICE_MAPPER + /* device-mapper devices aren't listed in /proc/partitions; or, if + * they are, they're listed as dm-X. So, instead of relying on that, + * we do our own checks. + */ + _probe_dm_devices (); +#endif + + /* /sys/block is more reliable and consistent; fall back to using + * /proc/partitions if the former is unavailable, however. + */ + if (!_probe_sys_block ()) + _probe_proc_partitions (); +} + +static char * _GL_ATTRIBUTE_FORMAT ((__printf__, 1, 2)) +zasprintf (const char *format, ...) +{ + va_list args; + char *resultp; + va_start (args, format); + int r = vasprintf (&resultp, format, args); + va_end (args); + return r < 0 ? NULL : resultp; +} + +#ifdef ENABLE_DEVICE_MAPPER +static char * +dm_canonical_path (PedDevice const *dev) +{ + LinuxSpecific const *arch_specific = LINUX_SPECIFIC (dev); + + /* Get map name from devicemapper */ + struct dm_task *task = dm_task_create (DM_DEVICE_INFO); + if (!task) + goto err; + if (!dm_task_set_major_minor (task, arch_specific->major, + arch_specific->minor, 0)) + goto err; + if (!dm_task_run(task)) + goto err; + char *dev_name = zasprintf ("/dev/mapper/%s", dm_task_get_name (task)); + if (dev_name == NULL) + goto err; + dm_task_destroy (task); + return dev_name; +err: + return NULL; +} +#endif + +static char* +_device_get_part_path (PedDevice const *dev, int num) +{ + char *devpath; + size_t path_len; + char *result; +#ifdef ENABLE_DEVICE_MAPPER + devpath = (dev->type == PED_DEVICE_DM + ? dm_canonical_path (dev) : dev->path); +#else + devpath = dev->path; +#endif + if (!devpath) + return NULL; + + path_len = strlen (devpath); + /* Check for devfs-style /disc => /partN transformation + unconditionally; the system might be using udev with devfs rules, + and if not the test is harmless. */ + if (5 < path_len && !strcmp (devpath + path_len - 5, "/disc")) { + /* replace /disc with /part%d */ + result = zasprintf ("%.*s/part%d", + (int) (path_len - 5), devpath, num); + } else { + char const *p = (dev->type == PED_DEVICE_DAC960 + || dev->type == PED_DEVICE_CPQARRAY + || dev->type == PED_DEVICE_ATARAID + || isdigit (devpath[path_len - 1]) + ? "p" : ""); + result = zasprintf ("%s%s%d", devpath, p, num); + } +#ifdef ENABLE_DEVICE_MAPPER + if (dev->type == PED_DEVICE_DM) + free (devpath); +#endif + return result; +} + +static char* +linux_partition_get_path (const PedPartition* part) +{ + /* loop label means use the whole disk */ + if (strcmp (part->disk->type->name, "loop") == 0) + return xstrdup (part->disk->dev->path); + return _device_get_part_path (part->disk->dev, part->num); +} + +static int +_mount_table_search (const char* file_name, dev_t dev) +{ + struct stat part_stat; + char line[512]; + char part_name[512]; + FILE* file; + + file = fopen (file_name, "r"); + if (!file) + return 0; + while (fgets (line, 512, file)) { + if (sscanf (line, "%s", part_name) == 1 + && stat (part_name, &part_stat) == 0) { + if (part_stat.st_rdev == dev) { + fclose (file); + return 1; + } + } + } + fclose (file); + return 0; +} + +static int +_partition_is_mounted_by_dev (dev_t dev) +{ + return _mount_table_search( "/proc/mounts", dev) + || _mount_table_search( "/proc/swaps", dev) + || _mount_table_search( "/etc/mtab", dev); +} + +static int +_partition_is_mounted_by_path (const char *path) +{ + struct stat part_stat; + if (stat (path, &part_stat) != 0) + return 0; + if (!S_ISBLK(part_stat.st_mode)) + return 0; + return _partition_is_mounted_by_dev (part_stat.st_rdev); +} + +/* If partition PART is mounted, or if we encounter an out-of-memory error + while trying to determine its status, return 1. Otherwise, return 0. */ +static int +_partition_is_mounted (const PedPartition *part) +{ + if (!ped_partition_is_active (part)) + return 0; + char *part_name = _device_get_part_path (part->disk->dev, part->num); + if (!part_name) + return 1; + int status = _partition_is_mounted_by_path (part_name); + free (part_name); + return !!status; +} + +static int +linux_partition_is_busy (const PedPartition* part) +{ + PedPartition* walk; + + PED_ASSERT (part != NULL); + + if (strcmp (part->disk->type->name, "loop") == 0) + return linux_is_busy (part->disk->dev); + if (_partition_is_mounted (part)) + return 1; + if (part->type == PED_PARTITION_EXTENDED) { + for (walk = part->part_list; walk; walk = walk->next) { + if (linux_partition_is_busy (walk)) + return 1; + } + } + return 0; +} + +static int +_blkpg_part_command (PedDevice* dev, struct blkpg_partition* part, int op) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (dev); + struct blkpg_ioctl_arg ioctl_arg; + + ioctl_arg.op = op; + ioctl_arg.flags = 0; + ioctl_arg.datalen = sizeof (struct blkpg_partition); + ioctl_arg.data = (void*) part; + + return ioctl (arch_specific->fd, BLKPG, &ioctl_arg) == 0; +} + +static int +_blkpg_add_partition (PedDisk* disk, const PedPartition *part) +{ + struct blkpg_partition linux_part; + const char* vol_name; + char* dev_name; + + PED_ASSERT(disk != NULL); + PED_ASSERT(disk->dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + + if (ped_disk_type_check_feature (disk->type, + PED_DISK_TYPE_PARTITION_NAME)) + vol_name = ped_partition_get_name (part); + else + vol_name = NULL; + + dev_name = _device_get_part_path (disk->dev, part->num); + if (!dev_name) + return 0; + + memset (&linux_part, 0, sizeof (linux_part)); + linux_part.start = part->geom.start * disk->dev->sector_size; + /* see fs/partitions/msdos.c:msdos_partition(): "leave room for LILO" */ + if (part->type & PED_PARTITION_EXTENDED) { + linux_part.length = 1; + if (disk->dev->sector_size == 512) { + if (linux_part.length == 1) + linux_part.length = 2; + PedPartition *walk; + /* if the second sector is claimed by a logical partition, + then there's just no room for lilo, so don't try to use it */ + for (walk = part->part_list; walk; walk = walk->next) { + if (walk->geom.start == part->geom.start+1) + linux_part.length = 1; + } + } + linux_part.length *= disk->dev->sector_size; + } + else { + linux_part.length = part->geom.length * disk->dev->sector_size; + } + linux_part.pno = part->num; + strncpy (linux_part.devname, dev_name, BLKPG_DEVNAMELTH-1); + linux_part.devname[BLKPG_DEVNAMELTH-1] = '\0'; + if (vol_name) { + strncpy (linux_part.volname, vol_name, BLKPG_VOLNAMELTH-1); + linux_part.volname[BLKPG_VOLNAMELTH-1] = '\0'; + } + + free (dev_name); + + if (!_blkpg_part_command (disk->dev, &linux_part, + BLKPG_ADD_PARTITION)) { + return 0; + } + + return 1; +} + +static int +_blkpg_remove_partition (PedDisk* disk, int n) +{ + struct blkpg_partition linux_part; + + memset (&linux_part, 0, sizeof (linux_part)); + linux_part.pno = n; + return _blkpg_part_command (disk->dev, &linux_part, + BLKPG_DEL_PARTITION); +} + +#ifdef BLKPG_RESIZE_PARTITION +static int _blkpg_resize_partition (PedDisk* disk, const PedPartition *part) +{ + struct blkpg_partition linux_part; + char* dev_name; + + PED_ASSERT(disk != NULL); + PED_ASSERT(disk->dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); + + dev_name = _device_get_part_path (disk->dev, part->num); + if (!dev_name) + return 0; + memset (&linux_part, 0, sizeof (linux_part)); + linux_part.start = part->geom.start * disk->dev->sector_size; + /* see fs/partitions/msdos.c:msdos_partition(): "leave room for LILO" */ + if (part->type & PED_PARTITION_EXTENDED) { + if (disk->dev->sector_size == 512) { + linux_part.length = 2; + PedPartition *walk; + /* if the second sector is claimed by a logical partition, + then there's just no room for lilo, so don't try to use it */ + for (walk = part->part_list; walk; walk = walk->next) { + if (walk->geom.start == part->geom.start+1) + linux_part.length = 1; + } + } else { + linux_part.length = 1; + } + linux_part.length *= disk->dev->sector_size; + } + else + linux_part.length = part->geom.length * disk->dev->sector_size; + linux_part.pno = part->num; + strncpy (linux_part.devname, dev_name, BLKPG_DEVNAMELTH-1); + linux_part.devname[BLKPG_DEVNAMELTH-1] = '\0'; + + free (dev_name); + + if (!_blkpg_part_command (disk->dev, &linux_part, + BLKPG_RESIZE_PARTITION)) { + return ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Error informing the kernel about modifications to " + "partition %s -- %s. This means Linux won't know " + "about any changes you made to %s until you reboot " + "-- so you shouldn't mount it or use it in any way " + "before rebooting."), + linux_part.devname, + strerror (errno), + linux_part.devname) + == PED_EXCEPTION_IGNORE; + } + + return 1; +} +#endif + +/* Read the integer from /sys/block/DEV_BASE/ENTRY and set *VAL + to that value, where DEV_BASE is the last component of DEV->path. + Upon success, return true. Otherwise, return false. */ +static bool +_sysfs_int_entry_from_dev(PedDevice const* dev, const char *entry, int *val) +{ + char path[128]; + int r = snprintf(path, sizeof(path), "/sys/block/%s/%s", + last_component(dev->path), entry); + if (r < 0 || r >= sizeof(path)) + return false; + + FILE *fp = fopen(path, "r"); + if (!fp) + return false; + + bool ok = fscanf(fp, "%d", val) == 1; + fclose(fp); + + return ok; +} + +/* Read the unsigned long long from /sys/block/DEV_BASE/PART_BASE/ENTRY + and set *VAL to that value, where DEV_BASE is the last component of path to + block device corresponding to PART and PART_BASE is the sysfs name of PART. + Upon success, return true. Otherwise, return false. */ +static bool +_sysfs_ull_entry_from_part(PedPartition const* part, const char *entry, + unsigned long long *val) +{ + char path[128]; + char *part_name = _device_get_part_path (part->disk->dev, part->num); + if (!part_name) + return false; + + int r = snprintf(path, sizeof(path), "/sys/block/%s/%s/%s", + last_component(part->disk->dev->path), + last_component(part_name), entry); + free(part_name); + if (r < 0 || r >= sizeof(path)) + return false; + + FILE *fp = fopen(path, "r"); + if (!fp) + return false; + + bool ok = fscanf(fp, "%llu", val) == 1; + fclose(fp); + + return ok; +} + + +/* Get the starting sector and length of a partition PART within a block device + Use blkpg if available, then check sysfs and then use HDIO_GETGEO and + BLKGETSIZE64 ioctls as fallback. Upon success, return true. Otherwise, + return false. */ +static bool +_kernel_get_partition_start_and_length(PedPartition const *part, + unsigned long long *start, + unsigned long long *length) +{ + PED_ASSERT(part); + PED_ASSERT(start); + PED_ASSERT(length); + + char *dev_name = _device_get_part_path (part->disk->dev, part->num); + if (!dev_name) + return false; + + int ok = _sysfs_ull_entry_from_part (part, "start", start); + if (!ok) { + struct hd_geometry geom; + int dev_fd = open (dev_name, O_RDONLY); + if (dev_fd != -1 && ioctl (dev_fd, HDIO_GETGEO, &geom)) { + *start = geom.start; + close (dev_fd); + ok = true; + } else { + if (dev_fd != -1) + close(dev_fd); + free (dev_name); + return false; + } + } + *start = (*start * 512) / part->disk->dev->sector_size; + ok = _sysfs_ull_entry_from_part (part, "size", length); + + int fd; + if (!ok) { + fd = open (dev_name, O_RDONLY); + if (fd != -1 && ioctl (fd, BLKGETSIZE64, length)) + ok = true; + } else { + fd = -1; + *length *= 512; + } + *length /= part->disk->dev->sector_size; + if (fd != -1) + close (fd); + + if (!ok) + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Unable to determine the start and length of %s."), + dev_name); + free (dev_name); + return ok; +} + + +/* + * The number of partitions that a device can have depends on the kernel. + * If we don't find this value in /sys/block/DEV/ext_range, we will use our own + * value. + */ +static unsigned int +_device_get_partition_range(PedDevice const* dev) +{ + int range; + if (dev->type == PED_DEVICE_DM) + return MAX_NUM_PARTS; + bool ok = _sysfs_int_entry_from_dev(dev, "ext_range", &range); + + if (!ok) + return MAX_NUM_PARTS; + /* both 0 and 1 mean no partitions */ + return range > 1 ? range : 0; +} + +#ifdef ENABLE_DEVICE_MAPPER +static int +_dm_remove_partition(PedDisk* disk, int partno) +{ + int rc = 0; + uint32_t cookie = 0; + char *part_name = _device_get_part_path (disk->dev, partno); + + int fd = open (part_name, O_RDONLY | O_EXCL); + if (fd == -1) { + if (errno == ENOENT) + errno = ENXIO; /* nothing to remove, device already doesn't exist */ + goto err; + } + close (fd); + struct dm_task *task = dm_task_create(DM_DEVICE_REMOVE); + if (!task) + goto err; + dm_task_set_name (task, part_name); + dm_task_retry_remove(task); + if (!dm_task_set_cookie (task, &cookie, 0)) + goto err; + rc = _dm_task_run_wait (task, cookie); + dm_task_update_nodes(); + dm_task_destroy(task); +err: + free (part_name); + return rc; +} + +static bool +_dm_get_partition_start_and_length(PedPartition const *part, + unsigned long long *start, + unsigned long long *length) +{ + struct dm_task* task = NULL; + int rc = 0; + + if (!(task = dm_task_create(DM_DEVICE_TABLE))) + return 0; + char *path = _device_get_part_path (part->disk->dev, part->num); + PED_ASSERT(path); + /* libdevmapper likes to complain on stderr instead of quietly + returning ENOENT or ENXIO, so try to stat first */ + struct stat st; + if (stat(path, &st)) + goto err; + dm_task_set_name(task, path); + if (!dm_task_run(task)) + goto err; + + int major, minor; + char *params; + char *target_type; + dm_get_next_target(task, NULL, (uint64_t *)start, (uint64_t *)length, &target_type, ¶ms); + if (sscanf (params, "%d:%d %Lu", &major, &minor, start) != 3) + goto err; + rc = 1; + + /* device-mapper uses 512b units, make sure we return length and start in terms of the device's + * sector size. + */ + *start /= (part->disk->dev->sector_size / PED_SECTOR_SIZE_DEFAULT); + *length /= (part->disk->dev->sector_size / PED_SECTOR_SIZE_DEFAULT); +err: + free (path); + dm_task_destroy(task); + return rc; +} + + +static int +_dm_add_partition (PedDisk* disk, const PedPartition* part) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (disk->dev); + char* params = NULL; + char* vol_name = NULL; + const char* dev_name = NULL; + char* vol_uuid = NULL; + const char* dev_uuid = NULL; + uint32_t cookie = 0; + + /* Get map name from devicemapper */ + struct dm_task *task = dm_task_create (DM_DEVICE_INFO); + if (!task) + goto err; + + if (!dm_task_set_major_minor (task, arch_specific->major, + arch_specific->minor, 0)) + goto err; + + if (!dm_task_run(task)) + goto err; + + dev_name = dm_task_get_name (task); + size_t name_len = strlen (dev_name); + vol_name = zasprintf ("%s%s%d", + dev_name, + isdigit (dev_name[name_len - 1]) ? "p" : "", + part->num); + if (vol_name == NULL) + goto err; + + dev_uuid = dm_task_get_uuid (task); + if (dev_uuid && (strlen(dev_uuid) > 0) + && !(vol_uuid = zasprintf ("part%d-%s", part->num, dev_uuid))) + goto err; + + /* Caution: dm_task_destroy frees dev_name. */ + dm_task_destroy (task); + task = NULL; + /* device-mapper uses 512b units, not the device's sector size */ + if ( ! (params = zasprintf ("%d:%d %lld", arch_specific->major, + arch_specific->minor, + part->geom.start * (disk->dev->sector_size / PED_SECTOR_SIZE_DEFAULT)))) + goto err; + + task = dm_task_create (DM_DEVICE_CREATE); + if (!task) + goto err; + + dm_task_set_name (task, vol_name); + if (vol_uuid) + dm_task_set_uuid (task, vol_uuid); + /* device-mapper uses 512b units, not the device's sector size */ + dm_task_add_target (task, 0, part->geom.length * (disk->dev->sector_size / PED_SECTOR_SIZE_DEFAULT), + "linear", params); + if (!dm_task_set_cookie (task, &cookie, 0)) + goto err; + if (_dm_task_run_wait (task, cookie)) { + dm_task_update_nodes (); + dm_task_destroy (task); + free (params); + free (vol_uuid); + free (vol_name); + return 1; + } else { + _dm_remove_partition (disk, part->num); + } +err: + dm_task_update_nodes(); + if (task) + dm_task_destroy (task); + free (params); + free (vol_uuid); + free (vol_name); + return 0; +} + +static int +_dm_resize_partition (PedDisk* disk, const PedPartition* part) +{ + LinuxSpecific* arch_specific = LINUX_SPECIFIC (disk->dev); + char* params = NULL; + char* vol_name = NULL; + const char* dev_name = NULL; + uint32_t cookie = 0; + int rc = 0; + + /* Get map name from devicemapper */ + struct dm_task *task = dm_task_create (DM_DEVICE_INFO); + if (!task) + goto err; + + if (!dm_task_set_major_minor (task, arch_specific->major, + arch_specific->minor, 0)) + goto err; + + if (!dm_task_run(task)) + goto err; + + dev_name = dm_task_get_name (task); + size_t name_len = strlen (dev_name); + vol_name = zasprintf ("%s%s%d", + dev_name, + isdigit (dev_name[name_len - 1]) ? "p" : "", + part->num); + if (vol_name == NULL) + goto err; + + /* Caution: dm_task_destroy frees dev_name. */ + dm_task_destroy (task); + task = NULL; + + /* device-mapper uses 512b units, not the device's sector size */ + if ( ! (params = zasprintf ("%d:%d %lld", arch_specific->major, + arch_specific->minor, + part->geom.start * (disk->dev->sector_size / PED_SECTOR_SIZE_DEFAULT)))) + goto err; + + task = dm_task_create (DM_DEVICE_RELOAD); + if (!task) + goto err; + + dm_task_set_name (task, vol_name); + /* device-mapper uses 512b units, not the device's sector size */ + dm_task_add_target (task, 0, part->geom.length * (disk->dev->sector_size / PED_SECTOR_SIZE_DEFAULT), + "linear", params); + /* NOTE: DM_DEVICE_RELOAD doesn't generate udev events, so no cookie is needed (it will freeze). + * DM_DEVICE_RESUME does, so get a cookie and synchronize with udev. + */ + if (dm_task_run (task)) { + dm_task_destroy (task); + task = dm_task_create (DM_DEVICE_RESUME); + if (!task) + goto err; + dm_task_set_name (task, vol_name); + if (!dm_task_set_cookie (task, &cookie, 0)) + goto err; + if (_dm_task_run_wait (task, cookie)) { + rc = 1; + } + } +err: + dm_task_update_nodes(); + if (task) + dm_task_destroy (task); + free (params); + free (vol_name); + return rc; +} + +#endif + +/* + * Sync the partition table in two step process: + * 1. Remove all of the partitions from the kernel's tables, but do not attempt + * removal of any partition for which the corresponding ioctl call fails. + * 2. Add all the partitions that we hold in disk, throwing a warning + * if we cannot because step 1 failed to remove it and it is not being + * added back with the same start and length. + * + * To achieve this two step process we must calculate the minimum number of + * maximum possible partitions between what linux supports and what the label + * type supports. EX: + * + * number=MIN(max_parts_supported_in_linux,max_parts_supported_in_msdos_tables) + */ +static int +_disk_sync_part_table (PedDisk* disk) +{ + PED_ASSERT(disk != NULL); + PED_ASSERT(disk->dev != NULL); + int lpn, lpn2; + unsigned int part_range = _device_get_partition_range(disk->dev); + int (*add_partition)(PedDisk* disk, const PedPartition *part); + int (*resize_partition)(PedDisk* disk, const PedPartition *part); + int (*remove_partition)(PedDisk* disk, int partno); + bool (*get_partition_start_and_length)(PedPartition const *part, + unsigned long long *start, + unsigned long long *length); + + +#ifdef ENABLE_DEVICE_MAPPER + if (disk->dev->type == PED_DEVICE_DM) { + add_partition = _dm_add_partition; + remove_partition = _dm_remove_partition; + resize_partition = _dm_resize_partition; + get_partition_start_and_length = _dm_get_partition_start_and_length; + } else +#endif + { + add_partition = _blkpg_add_partition; + remove_partition = _blkpg_remove_partition; +#ifdef BLKPG_RESIZE_PARTITION + resize_partition = _blkpg_resize_partition; +#else + resize_partition = NULL; +#endif + get_partition_start_and_length = _kernel_get_partition_start_and_length; + } + + /* lpn = largest partition number. + * for remove pass, use greater of device or label limit */ + if (ped_disk_get_max_supported_partition_count(disk, &lpn)) + lpn = PED_MAX(lpn, part_range); + else + lpn = part_range; + /* for add pass, use lesser of device or label limit */ + if (ped_disk_get_max_supported_partition_count(disk, &lpn2)) + lpn2 = PED_MIN(lpn2, part_range); + else + lpn2 = part_range; + /* Its not possible to support largest_partnum < 0. + * largest_partnum == 0 would mean does not support partitions. + * */ + if (lpn < 1) + return 0; + int ret = 0; + int *ok = calloc (lpn, sizeof *ok); + if (!ok) + return 0; + int *errnums = ped_malloc(sizeof(int) * lpn); + if (!errnums) + goto cleanup; + + int i; + /* remove old partitions first */ + for (i = 1; i <= lpn; i++) { + PedPartition *part = ped_disk_get_partition (disk, i); + if (part) { + unsigned long long length; + unsigned long long start; + /* get start and length of existing partition */ + if (get_partition_start_and_length(part, + &start, &length) + && start == part->geom.start + && (length == part->geom.length + || (resize_partition && part->num < lpn2))) + { + /* partition is unchanged, or will be resized so nothing to do */ + ok[i - 1] = 1; + continue; + } + } + /* Attempt to remove the partition, retrying for + up to max_sleep_seconds upon any failure due to EBUSY. */ + unsigned int sleep_microseconds = 10000; + unsigned int max_sleep_seconds = 1; + unsigned int n_sleep = (max_sleep_seconds + * 1000000 / sleep_microseconds); + do { + ok[i - 1] = remove_partition (disk, i); + errnums[i - 1] = errno; + if (ok[i - 1] || errnums[i - 1] != EBUSY) + break; + usleep (sleep_microseconds); + } while (n_sleep--); + if (!ok[i - 1] && errnums[i - 1] == ENXIO) + ok[i - 1] = 1; /* it already doesn't exist */ + } + lpn = lpn2; + /* don't actually add partitions for loop */ + if (strcmp (disk->type->name, "loop") == 0) + lpn = 0; + for (i = 1; i <= lpn; i++) { + PedPartition *part = ped_disk_get_partition (disk, i); + if (!part) + continue; + unsigned long long length; + unsigned long long start; + /* get start and length of existing partition */ + if (get_partition_start_and_length(part, + &start, &length) + && start == part->geom.start) + { + if (length == part->geom.length) { + ok[i - 1] = 1; + /* partition is unchanged, so nothing to do */ + continue; + } + if (resize_partition + && start == part->geom.start) + { + /* try to resize */ + if (resize_partition (disk, part)) { + ok[i - 1] = 1; + continue; + } + } + } + /* add the (possibly modified or new) partition */ + if (!add_partition (disk, part)) { + ok[i - 1] = 0; + errnums[i - 1] = errno; + } + } + + char *bad_part_list = NULL; + /* now warn about any errors */ + for (i = 1; i <= lpn; i++) { + if (ok[i - 1] || errnums[i - 1] == ENXIO) + continue; + if (bad_part_list == NULL) { + bad_part_list = malloc (lpn * 5); + if (!bad_part_list) + goto cleanup; + bad_part_list[0] = 0; + } + sprintf (bad_part_list + strlen (bad_part_list), "%d, ", i); + } + if (bad_part_list == NULL) + ret = 1; + else { + bad_part_list[strlen (bad_part_list) - 2] = 0; + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Partition(s) %s on %s have been written, but we have " + "been unable to inform the kernel of the change, " + "probably because it/they are in use. As a result, " + "the old partition(s) will remain in use. You " + "should reboot now before making further changes."), + bad_part_list, disk->dev->path) == PED_EXCEPTION_IGNORE) + ret = 1; + free (bad_part_list); + } + cleanup: + free (errnums); + free (ok); + return ret; +} + +static int +_have_blkpg () +{ + static int have_blkpg = -1; + int kver; + + if (have_blkpg != -1) + return have_blkpg; + + kver = _get_linux_version(); + return have_blkpg = kver >= KERNEL_VERSION (2,4,0) ? 1 : 0; +} + +/* Return nonzero upon success, 0 if something fails. */ +static int +linux_disk_commit (PedDisk* disk) +{ + if (disk->dev->type != PED_DEVICE_FILE) { + + /* We now require BLKPG support. If this assertion fails, + please write to the mailing list describing your system. + Assuming it's never triggered, ... + FIXME: remove this assertion in 2012. */ + assert (_have_blkpg ()); + + if (!_disk_sync_part_table (disk)) + return 0; + } + + return 1; +} + +#if USE_BLKID +static PedAlignment* +linux_get_minimum_alignment(const PedDevice *dev) +{ + blkid_topology tp = LINUX_SPECIFIC(dev)->topology; + if (!tp) + return NULL; + + if (blkid_topology_get_minimum_io_size(tp) == 0) + return ped_alignment_new( + blkid_topology_get_alignment_offset(tp) / + dev->sector_size, + dev->phys_sector_size / dev->sector_size); + + return ped_alignment_new( + blkid_topology_get_alignment_offset(tp) / dev->sector_size, + blkid_topology_get_minimum_io_size(tp) / dev->sector_size); +} + +static PedAlignment* +linux_get_optimum_alignment(const PedDevice *dev) +{ + blkid_topology tp = LINUX_SPECIFIC(dev)->topology; + if (!tp) + return NULL; + + /* When PED_DEFAULT_ALIGNMENT is divisible by the *_io_size or + there are no *_io_size values, use the PED_DEFAULT_ALIGNMENT + If one or the other will not divide evenly, fall through to + previous logic. */ + unsigned long optimal_io = blkid_topology_get_optimal_io_size(tp); + unsigned long minimum_io = blkid_topology_get_minimum_io_size(tp); + if ( + (!optimal_io && !minimum_io) + || (optimal_io && PED_DEFAULT_ALIGNMENT % optimal_io == 0 + && minimum_io && PED_DEFAULT_ALIGNMENT % minimum_io == 0) + || (!minimum_io && optimal_io + && PED_DEFAULT_ALIGNMENT % optimal_io == 0) + || (!optimal_io && minimum_io + && PED_DEFAULT_ALIGNMENT % minimum_io == 0) + ) + return ped_alignment_new( + blkid_topology_get_alignment_offset(tp) / dev->sector_size, + PED_DEFAULT_ALIGNMENT / dev->sector_size); + + /* If optimal_io_size is 0 and we don't meet the other criteria + for using the device.c default, return the minimum alignment. */ + if (blkid_topology_get_optimal_io_size(tp) == 0) + return linux_get_minimum_alignment(dev); + + return ped_alignment_new( + blkid_topology_get_alignment_offset(tp) / dev->sector_size, + blkid_topology_get_optimal_io_size(tp) / dev->sector_size); +} +#endif + +#if defined __s390__ || defined __s390x__ +/** + * Check whether this device could be a DASD + * + * The device probing yields PED_DEVICE_DASD for native DASD transport + * If the block device uses a different transport (e.g. virtio) + * a simplified heuristic (assuming a model 3390 with 4K sectors) + * is applied (only) on s390x systems for this check. + * + * \return 1 if the geometry indicates this could be a DASD + * and 0 otherwise + */ +static int +_ped_device_like_dasd(const PedDevice *dev) +{ + return (dev->type == PED_DEVICE_DASD) + || (dev->hw_geom.heads == 15 + && dev->hw_geom.sectors == 12 + && (dev->hw_geom.cylinders + * dev->hw_geom.heads + * dev->hw_geom.sectors + * dev->phys_sector_size + == dev->length * dev->sector_size)); +} + + + +static PedAlignment* +s390_get_minimum_alignment(const PedDevice *dev) +{ +#if USE_BLKID + return linux_get_minimum_alignment(dev); +#else + return ped_alignment_new(0, + dev->phys_sector_size + / dev->sector_size); +#endif +} + +static PedAlignment* +s390_get_optimum_alignment(const PedDevice *dev) +{ + /* DASD needs to use minimum alignment */ + if (_ped_device_like_dasd(dev)) + return s390_get_minimum_alignment(dev); +#if USE_BLKID + return linux_get_optimum_alignment(dev); +#else + return NULL; +#endif +} +#endif + +static PedDeviceArchOps linux_dev_ops = { + _new: linux_new, + destroy: linux_destroy, + is_busy: linux_is_busy, + open: linux_open, + refresh_open: linux_refresh_open, + close: linux_close, + refresh_close: linux_refresh_close, + read: linux_read, + write: linux_write, + check: linux_check, + sync: linux_sync, + sync_fast: linux_sync_fast, + probe_all: linux_probe_all, +#if defined __s390__ || defined __s390x__ + get_minimum_alignment: s390_get_minimum_alignment, + get_optimum_alignment: s390_get_optimum_alignment, +#elif USE_BLKID + get_minimum_alignment: linux_get_minimum_alignment, + get_optimum_alignment: linux_get_optimum_alignment, +#endif +}; + +PedDiskArchOps linux_disk_ops = { + partition_get_path: linux_partition_get_path, + partition_is_busy: linux_partition_is_busy, + disk_commit: linux_disk_commit +}; + +PedArchitecture ped_linux_arch = { + dev_ops: &linux_dev_ops, + disk_ops: &linux_disk_ops +}; diff --git a/libparted/arch/linux.h b/libparted/arch/linux.h new file mode 100644 index 0000000..3d4e5fa --- /dev/null +++ b/libparted/arch/linux.h @@ -0,0 +1,44 @@ +/* libparted - a library for manipulating disk partitions + Copyright (C) 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/>. +*/ + +#ifndef PED_ARCH_LINUX_H_INCLUDED +#define PED_ARCH_LINUX_H_INCLUDED + +#if HAVE_BLKID_BLKID_H +# include <blkid/blkid.h> +#endif + +#define LINUX_SPECIFIC(dev) ((LinuxSpecific*) (dev)->arch_specific) + +typedef struct _LinuxSpecific LinuxSpecific; + +struct _LinuxSpecific { + int fd; + int major; + int minor; + char* dmtype; /**< device map target type */ +#if defined __s390__ || defined __s390x__ + unsigned int real_sector_size; + unsigned int devno; +#endif +#if USE_BLKID + blkid_probe probe; + blkid_topology topology; +#endif +}; + +#endif /* PED_ARCH_LINUX_H_INCLUDED */ diff --git a/libparted/architecture.c b/libparted/architecture.c new file mode 100644 index 0000000..4020f98 --- /dev/null +++ b/libparted/architecture.c @@ -0,0 +1,43 @@ + /* + 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 <config.h> +#include "architecture.h" + +const PedArchitecture* ped_architecture; + +void +ped_set_architecture () +{ + /* Set just once */ + if (ped_architecture) + return; + +#ifdef linux + extern PedArchitecture ped_linux_arch; + const PedArchitecture* arch = &ped_linux_arch; +#elif defined(__BEOS__) + extern PedArchitecture ped_beos_arch; + const PedArchitecture* arch = &ped_beos_arch; +#else + extern PedArchitecture ped_gnu_arch; + const PedArchitecture* arch = &ped_gnu_arch; +#endif + + ped_architecture = arch; +} diff --git a/libparted/architecture.h b/libparted/architecture.h new file mode 100644 index 0000000..f058f74 --- /dev/null +++ b/libparted/architecture.h @@ -0,0 +1,38 @@ + /* + 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/>. +*/ + +/* + * WARNING: This shouldn't be exported to the API + */ + +#ifndef _LIBPARTED_ARCH_H_INCLUDED +#define _LIBPARTED_ARCH_H_INCLUDED + +#include <parted/disk.h> + +struct _PedArchitecture { + PedDiskArchOps* disk_ops; + PedDeviceArchOps* dev_ops; +}; +typedef struct _PedArchitecture PedArchitecture; + +extern const PedArchitecture* ped_architecture; + +extern void ped_set_architecture (); + +#endif /* _LIBPARTED_ARCH_H_INCLUDED */ diff --git a/libparted/cs/constraint.c b/libparted/cs/constraint.c new file mode 100644 index 0000000..146c318 --- /dev/null +++ b/libparted/cs/constraint.c @@ -0,0 +1,538 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000-2001, 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/>. +*/ + +/** + * \addtogroup PedConstraint + * + * \brief Constraint solver interface. + * + * Constraints are used to communicate restrictions on operations Constraints + * are restrictions on the location and alignment of the start and end of a + * partition, and the minimum and maximum size. + * + * Constraints are closed under intersection (for the proof see the source + * code). For background information see the Chinese Remainder Theorem. + * + * This interface consists of construction constraints, finding the intersection + * of constraints, and finding solutions to constraints. + * + * The constraint solver allows you to specify constraints on where a partition + * or file system (or any PedGeometry) may be placed/resized/etc. For example, + * you might want to make sure that a file system is at least 10 Gb, or that it + * starts at the beginning of new cylinder. + * + * The constraint solver in this file unifies solver in geom.c (which allows you + * to specify constraints on ranges) and natmath.c (which allows you to specify + * alignment constraints). + * + * @{ + */ + +#include <config.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <assert.h> + +/** + * Initializes a pre-allocated piece of memory to contain a constraint + * with the supplied default values. + * + * \return \c 0 on failure. + */ +int +ped_constraint_init ( + PedConstraint* constraint, + const PedAlignment* start_align, + const PedAlignment* end_align, + const PedGeometry* start_range, + const PedGeometry* end_range, + PedSector min_size, + PedSector max_size) +{ + PED_ASSERT (constraint != NULL); + PED_ASSERT (start_range != NULL); + PED_ASSERT (end_range != NULL); + PED_ASSERT (min_size > 0); + PED_ASSERT (max_size > 0); + + constraint->start_align = ped_alignment_duplicate (start_align); + constraint->end_align = ped_alignment_duplicate (end_align); + constraint->start_range = ped_geometry_duplicate (start_range); + constraint->end_range = ped_geometry_duplicate (end_range); + constraint->min_size = min_size; + constraint->max_size = max_size; + + return 1; +} + +/** + * Convenience wrapper for ped_constraint_init(). + * + * Allocates a new piece of memory and initializes the constraint. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_new ( + const PedAlignment* start_align, + const PedAlignment* end_align, + const PedGeometry* start_range, + const PedGeometry* end_range, + PedSector min_size, + PedSector max_size) +{ + PedConstraint* constraint; + + constraint = (PedConstraint*) ped_malloc (sizeof (PedConstraint)); + if (!constraint) + goto error; + if (!ped_constraint_init (constraint, start_align, end_align, + start_range, end_range, min_size, max_size)) + goto error_free_constraint; + return constraint; + +error_free_constraint: + free (constraint); +error: + return NULL; +} + +/** + * Return a constraint that requires a region to be entirely contained inside + * \p max, and to entirely contain \p min. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_new_from_min_max ( + const PedGeometry* min, + const PedGeometry* max) +{ + PedGeometry start_range; + PedGeometry end_range; + + PED_ASSERT (min != NULL); + PED_ASSERT (max != NULL); + PED_ASSERT (ped_geometry_test_inside (max, min)); + + ped_geometry_init (&start_range, min->dev, max->start, + min->start - max->start + 1); + ped_geometry_init (&end_range, min->dev, min->end, + max->end - min->end + 1); + + return ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + &start_range, &end_range, + min->length, max->length); +} + +/** + * Return a constraint that requires a region to entirely contain \p min. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_new_from_min (const PedGeometry* min) +{ + PedGeometry full_dev; + + PED_ASSERT (min != NULL); + + ped_geometry_init (&full_dev, min->dev, 0, min->dev->length); + return ped_constraint_new_from_min_max (min, &full_dev); +} + +/** + * Return a constraint that requires a region to be entirely contained inside + * \p max. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_new_from_max (const PedGeometry* max) +{ + PED_ASSERT (max != NULL); + + return ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + max, max, 1, max->length); +} + +/** + * Duplicate a constraint. + * + * \return \c NULL on failure. + */ +PedConstraint* +ped_constraint_duplicate (const PedConstraint* constraint) +{ + PED_ASSERT (constraint != NULL); + + return ped_constraint_new ( + constraint->start_align, + constraint->end_align, + constraint->start_range, + constraint->end_range, + constraint->min_size, + constraint->max_size); +} + +/** + * Return a constraint that requires a region to satisfy both \p a and \p b. + * + * Moreover, any region satisfying \p a and \p b will also satisfy the returned + * constraint. + * + * \return \c NULL if no solution could be found (note that \c NULL is a valid + * PedConstraint). + */ +PedConstraint* +ped_constraint_intersect (const PedConstraint* a, const PedConstraint* b) +{ + PedAlignment* start_align; + PedAlignment* end_align; + PedGeometry* start_range; + PedGeometry* end_range; + PedSector min_size; + PedSector max_size; + PedConstraint* constraint; + + if (!a || !b) + return NULL; + + start_align = ped_alignment_intersect (a->start_align, b->start_align); + if (!start_align) + goto empty; + end_align = ped_alignment_intersect (a->end_align, b->end_align); + if (!end_align) + goto empty_destroy_start_align; + start_range = ped_geometry_intersect (a->start_range, b->start_range); + if (!start_range) + goto empty_destroy_end_align; + end_range = ped_geometry_intersect (a->end_range, b->end_range); + if (!end_range) + goto empty_destroy_start_range; + min_size = PED_MAX (a->min_size, b->min_size); + max_size = PED_MIN (a->max_size, b->max_size); + + constraint = ped_constraint_new ( + start_align, end_align, start_range, end_range, + min_size, max_size); + if (!constraint) + goto empty_destroy_end_range; + + ped_alignment_destroy (start_align); + ped_alignment_destroy (end_align); + ped_geometry_destroy (start_range); + ped_geometry_destroy (end_range); + return constraint; + +empty_destroy_end_range: + ped_geometry_destroy (end_range); +empty_destroy_start_range: + ped_geometry_destroy (start_range); +empty_destroy_end_align: + ped_alignment_destroy (end_align); +empty_destroy_start_align: + ped_alignment_destroy (start_align); +empty: + return NULL; +} + +/** + * Release the memory allocated for a PedConstraint constructed with + * ped_constraint_init(). + */ +void +ped_constraint_done (PedConstraint* constraint) +{ + PED_ASSERT (constraint != NULL); + + ped_alignment_destroy (constraint->start_align); + ped_alignment_destroy (constraint->end_align); + ped_geometry_destroy (constraint->start_range); + ped_geometry_destroy (constraint->end_range); +} + +/** + * Release the memory allocated for a PedConstraint constructed with + * ped_constraint_new(). + */ +void +ped_constraint_destroy (PedConstraint* constraint) +{ + if (constraint) { + ped_constraint_done (constraint); + free (constraint); + } +} + +/* + * Return the region within which the start must lie + * in order to satisfy a constriant. It takes into account + * constraint->start_range, constraint->min_size and constraint->max_size. + * All sectors in this range that also satisfy alignment requirements have + * an end, such that the (start, end) satisfy the constraint. + */ +static PedGeometry* +_constraint_get_canonical_start_range (const PedConstraint* constraint) +{ + PedSector first_end_soln; + PedSector last_end_soln; + PedSector min_start; + PedSector max_start; + PedGeometry start_min_max_range; + + if (constraint->min_size > constraint->max_size) + return NULL; + + first_end_soln = ped_alignment_align_down ( + constraint->end_align, constraint->end_range, + constraint->end_range->start); + last_end_soln = ped_alignment_align_up ( + constraint->end_align, constraint->end_range, + constraint->end_range->end); + if (first_end_soln == -1 || last_end_soln == -1 + || first_end_soln > last_end_soln + || last_end_soln < constraint->min_size) + return NULL; + + min_start = first_end_soln - constraint->max_size + 1; + if (min_start < 0) + min_start = 0; + max_start = last_end_soln - constraint->min_size + 1; + if (max_start < 0) + return NULL; + + ped_geometry_init ( + &start_min_max_range, constraint->start_range->dev, + min_start, max_start - min_start + 1); + + return ped_geometry_intersect (&start_min_max_range, + constraint->start_range); +} + +/* + * Return the nearest start that will have at least one other end that + * together satisfy the constraint. + */ +static PedSector +_constraint_get_nearest_start_soln (const PedConstraint* constraint, + PedSector start) +{ + PedGeometry* start_range; + PedSector result; + + start_range = _constraint_get_canonical_start_range (constraint); + if (!start_range) + return -1; + result = ped_alignment_align_nearest ( + constraint->start_align, start_range, start); + ped_geometry_destroy (start_range); + return result; +} + +/* + * Given a constraint and a start ("half of the solution"), find the + * range of all possible ends, such that all (start, end) are solutions + * to constraint (subject to additional alignment requirements). + */ +static PedGeometry* +_constraint_get_end_range (const PedConstraint* constraint, PedSector start) +{ + PedDevice* dev = constraint->end_range->dev; + PedSector first_min_max_end; + PedSector last_min_max_end; + PedGeometry end_min_max_range; + + if (start + constraint->min_size - 1 > dev->length - 1) + return NULL; + + first_min_max_end = start + constraint->min_size - 1; + last_min_max_end = start + constraint->max_size - 1; + if (last_min_max_end > dev->length - 1) + last_min_max_end = dev->length - 1; + + ped_geometry_init (&end_min_max_range, dev, + first_min_max_end, + last_min_max_end - first_min_max_end + 1); + + return ped_geometry_intersect (&end_min_max_range, + constraint->end_range); +} + +/* + * Given "constraint" and "start", find the end that is nearest to + * "end", such that ("start", the end) together form a solution to + * "constraint". + */ +static PedSector +_constraint_get_nearest_end_soln (const PedConstraint* constraint, + PedSector start, PedSector end) +{ + PedGeometry* end_range; + PedSector result; + + end_range = _constraint_get_end_range (constraint, start); + if (!end_range) + return -1; + + result = ped_alignment_align_nearest (constraint->end_align, end_range, + end); + ped_geometry_destroy (end_range); + return result; +} + +/** + * Return the nearest region to \p geom that satisfy a \p constraint. + * + * Note that "nearest" is somewhat ambiguous. This function makes + * no guarantees about how this ambiguity is resovled. + * + * \return PedGeometry, or NULL when a \p constrain cannot be satisfied + */ +PedGeometry* +ped_constraint_solve_nearest ( + const PedConstraint* constraint, const PedGeometry* geom) +{ + PedSector start; + PedSector end; + PedGeometry* result; + + if (constraint == NULL) + return NULL; + + PED_ASSERT (geom != NULL); + PED_ASSERT (constraint->start_range->dev == geom->dev); + + start = _constraint_get_nearest_start_soln (constraint, geom->start); + if (start == -1) + return NULL; + end = _constraint_get_nearest_end_soln (constraint, start, geom->end); + if (end == -1) + return NULL; + + result = ped_geometry_new (geom->dev, start, end - start + 1); + if (!result) + return NULL; + PED_ASSERT (ped_constraint_is_solution (constraint, result)); + return result; +} + +/** + * Find the largest region that satisfies a constraint. + * + * There might be more than one solution. This function makes no + * guarantees about which solution it will choose in this case. + */ +PedGeometry* +ped_constraint_solve_max (const PedConstraint* constraint) +{ + PedDevice* dev; + PedGeometry full_dev; + + if (!constraint) + return NULL; + dev = constraint->start_range->dev; + ped_geometry_init (&full_dev, dev, 0, dev->length); + return ped_constraint_solve_nearest (constraint, &full_dev); +} + +/** + * Check whether \p geom satisfies the given constraint. + * + * \return \c 1 if it does. + **/ +int +ped_constraint_is_solution (const PedConstraint* constraint, + const PedGeometry* geom) +{ + PED_ASSERT (constraint != NULL); + PED_ASSERT (geom != NULL); + + if (!ped_alignment_is_aligned (constraint->start_align, NULL, + geom->start)) + return 0; + if (!ped_alignment_is_aligned (constraint->end_align, NULL, geom->end)) + return 0; + if (!ped_geometry_test_sector_inside (constraint->start_range, + geom->start)) + return 0; + if (!ped_geometry_test_sector_inside (constraint->end_range, geom->end)) + return 0; + if (geom->length < constraint->min_size) + return 0; + if (geom->length > constraint->max_size) + return 0; + return 1; +} + +/** + * Return a constraint that any region on the given device will satisfy. + */ +PedConstraint* +ped_constraint_any (const PedDevice* dev) +{ + PedGeometry full_dev; + + if (!ped_geometry_init (&full_dev, dev, 0, dev->length)) + return NULL; + + return ped_constraint_new ( + ped_alignment_any, + ped_alignment_any, + &full_dev, + &full_dev, + 1, + dev->length); +} + +/** + * Return a constraint that only the given region will satisfy. + */ +PedConstraint* +ped_constraint_exact (const PedGeometry* geom) +{ + PedAlignment start_align; + PedAlignment end_align; + PedGeometry start_sector; + PedGeometry end_sector; + int ok; + + /* With grain size of 0, it always succeeds. */ + ok = ped_alignment_init (&start_align, geom->start, 0); + assert (ok); + ok = ped_alignment_init (&end_align, geom->end, 0); + assert (ok); + + ok = ped_geometry_init (&start_sector, geom->dev, geom->start, 1); + if (!ok) + return NULL; + ok = ped_geometry_init (&end_sector, geom->dev, geom->end, 1); + if (!ok) + return NULL; + + return ped_constraint_new (&start_align, &end_align, + &start_sector, &end_sector, 1, + geom->dev->length); +} + +/** + * @} + */ diff --git a/libparted/cs/geom.c b/libparted/cs/geom.c new file mode 100644 index 0000000..99280ac --- /dev/null +++ b/libparted/cs/geom.c @@ -0,0 +1,487 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2000, 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/>. +*/ + +/** \file geom.c */ + + +/** + * \addtogroup PedGeometry + * + * \brief PedGeometry represents a continuous region on a device. All addressing + * through a PedGeometry object is in terms of the start of the continuous + * region. + * + * The following conditions are always true on a PedGeometry object manipulated + * with the GNU Parted API: + * + * - <tt>start + length - 1 == end</tt> + * - <tt>length > 0</tt> + * - <tt>start >= 0</tt> + * - <tt>end < dev->length</tt> + * + * @{ + */ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/** + * Initialize the previously allocated PedGeometry \p geom. + */ +int +ped_geometry_init (PedGeometry* geom, const PedDevice* dev, + PedSector start, PedSector length) +{ + PED_ASSERT (geom != NULL); + PED_ASSERT (dev != NULL); + + geom->dev = (PedDevice*) dev; + return ped_geometry_set (geom, start, length); +} + +/** + * Create a new PedGeometry object on \p disk, starting at \p start with a + * size of \p length sectors. + * + * \return NULL on failure. + */ +PedGeometry* +ped_geometry_new (const PedDevice* dev, PedSector start, PedSector length) +{ + PedGeometry* geom; + + PED_ASSERT (dev != NULL); + + geom = (PedGeometry*) ped_malloc (sizeof (PedGeometry)); + if (!geom) + goto error; + if (!ped_geometry_init (geom, dev, start, length)) + goto error_free_geom; + return geom; + +error_free_geom: + free (geom); +error: + return NULL; +} + +/** + * Duplicate a PedGeometry object. + * + * This function constructs a PedGeometry object that is an identical but + * independent copy of \p geom. Both the input, \p geom, and the output + * should be destroyed with ped_geometry_destroy() when they are no + * longer needed. + * + * \return NULL on failure. + */ +PedGeometry* +ped_geometry_duplicate (const PedGeometry* geom) +{ + PED_ASSERT (geom != NULL); + return ped_geometry_new (geom->dev, geom->start, geom->length); +} + +/** + * Return a PedGeometry object that refers to the intersection of + * \p a and \p b. + * + * This function constructs a PedGeometry object that describes the + * region that is common to both a and b. If there is no such common + * region, it returns NULL. (This situation is not treated as an + * error by much of GNU Parted.) + */ +PedGeometry* +ped_geometry_intersect (const PedGeometry* a, const PedGeometry* b) +{ + PedSector start; + PedSector end; + + if (!a || !b || a->dev != b->dev) + return NULL; + + start = PED_MAX (a->start, b->start); + end = PED_MIN (a->end, b->end); + if (start > end) + return NULL; + + return ped_geometry_new (a->dev, start, end - start + 1); +} + +/** + * Destroy a PedGeometry object. + */ +void +ped_geometry_destroy (PedGeometry* geom) +{ + PED_ASSERT (geom != NULL); + + free (geom); +} + +/** + * Assign a new \p start, \p end (implicitly) and \p length to \p geom. + * + * \p geom->end is calculated from \p start and \p length. + */ +int +ped_geometry_set (PedGeometry* geom, PedSector start, PedSector length) +{ + PED_ASSERT (geom != NULL); + PED_ASSERT (geom->dev != NULL); + PED_ASSERT (start >= 0); + + if (length < 1) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have the end before the start!" + " (start sector=%jd length=%jd)"), start, length); + return 0; + } + + geom->start = start; + geom->length = length; + geom->end = start + length - 1; + + return 1; +} + +/** + * Assign a new start to \p geom without changing \p geom->end. + * + * \p geom->length is updated accordingly. + */ +int +ped_geometry_set_start (PedGeometry* geom, PedSector start) +{ + return ped_geometry_set (geom, start, geom->end - start + 1); +} + +/** + * Assign a new end to \p geom without changing \p geom->start. + * + * \p geom->length is updated accordingly. + */ +int +ped_geometry_set_end (PedGeometry* geom, PedSector end) +{ + return ped_geometry_set (geom, geom->start, end - geom->start + 1); +} +/** + * Test if \p a overlaps with \p b. + * + * That is, they lie on the same physical device, and they share + * the same physical region at least partially. + * + * \return 1 if \p a and \p b overlap. + */ +int +ped_geometry_test_overlap (const PedGeometry* a, const PedGeometry* b) +{ + PED_ASSERT (a != NULL); + PED_ASSERT (b != NULL); + + if (a->dev != b->dev) + return 0; + + if (a->start < b->start) + return a->end >= b->start; + else + return b->end >= a->start; +} + +/** + * Tests if \p b lies completely within \p a. That is, they lie on the same + * physical device, and all of the \p b's region is contained inside + * \p a's. + * + * \return 1 if the region \p b describes is contained entirely inside \p a +*/ +int +ped_geometry_test_inside (const PedGeometry* a, const PedGeometry* b) +{ + PED_ASSERT (a != NULL); + PED_ASSERT (b != NULL); + + if (a->dev != b->dev) + return 0; + + return b->start >= a->start && b->end <= a->end; +} + +/** + * Tests if \a a and \p b refer to the same physical region. + * + * \return 1 if \p a and \p b describe the same regions + * + */ +int +ped_geometry_test_equal (const PedGeometry* a, const PedGeometry* b) +{ + PED_ASSERT (a != NULL); + PED_ASSERT (b != NULL); + + return a->dev == b->dev + && a->start == b->start + && a->end == b->end; +} + +/** + * Tests if \p sector is inside \p geom. + * + * \return 1 if sector lies within the \p region that \p geom describes + */ +int +ped_geometry_test_sector_inside (const PedGeometry* geom, PedSector sector) +{ + PED_ASSERT (geom != NULL); + + return sector >= geom->start && sector <= geom->end; +} + +/** + * Reads data from the region represented by \p geom. \p offset is the + * location from within the region, not from the start of the disk. + * \p count sectors are read into \p buffer. + * This is essentially equivalent to: + * \code + * ped_device_read (geom->disk->dev, buffer, geom->start + offset, count) + * \endcode + * + * \throws PED_EXCEPTION_ERROR when attempting to read sectors outside of + * partition + * + * \return 0 on failure + */ +int +ped_geometry_read (const PedGeometry* geom, void* buffer, PedSector offset, + PedSector count) +{ + PedSector real_start; + + PED_ASSERT (geom != NULL); + PED_ASSERT (buffer != NULL); + PED_ASSERT (offset >= 0); + PED_ASSERT (count >= 0); + + real_start = geom->start + offset; + + if (real_start + count - 1 > geom->end) + return 0; + + if (!ped_device_read (geom->dev, buffer, real_start, count)) + return 0; + return 1; +} + +/* Like ped_device_read, but read into malloc'd storage. */ +int +ped_geometry_read_alloc (const PedGeometry* geom, void** buffer, + PedSector offset, PedSector count) +{ + char *buf = ped_malloc (count * geom->dev->sector_size); + if (buf == NULL) + return 0; + int ok = ped_geometry_read (geom, buf, offset, count); + if (!ok) { + free (buf); + buf = NULL; + } + + *buffer = buf; + return ok; +} + +/** + * Flushes the cache on \p geom. + * + * This function flushes all write-behind caches that might be holding + * writes made by ped_geometry_write() to \p geom. It is slow, because + * it guarantees cache coherency among all relevant caches. + * + * \return 0 on failure + */ +int +ped_geometry_sync (PedGeometry* geom) +{ + PED_ASSERT (geom != NULL); + return ped_device_sync (geom->dev); +} + +/** + * Flushes the cache on \p geom. + * + * This function flushes all write-behind caches that might be holding writes + * made by ped_geometry_write() to \p geom. It does NOT ensure cache coherency + * with other caches that cache data in the region described by \p geom. + * If you need cache coherency, use ped_geometry_sync() instead. + * + * \return 0 on failure + */ +int +ped_geometry_sync_fast (PedGeometry* geom) +{ + PED_ASSERT (geom != NULL); + return ped_device_sync_fast (geom->dev); +} + +/** + * Writes data into the region represented by \p geom. \p offset is the + * location from within the region, not from the start of the disk. + * \p count sectors are written. + * + * \return 0 on failure + */ +int +ped_geometry_write (PedGeometry* geom, const void* buffer, PedSector offset, + PedSector count) +{ + int exception_status; + PedSector real_start; + + PED_ASSERT (geom != NULL); + PED_ASSERT (buffer != NULL); + PED_ASSERT (offset >= 0); + PED_ASSERT (count >= 0); + + real_start = geom->start + offset; + + if (real_start + count - 1 > geom->end) { + exception_status = ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Attempt to write sectors %ld-%ld outside of " + "partition on %s."), + (long) offset, (long) (offset + count - 1), + geom->dev->path); + return exception_status == PED_EXCEPTION_IGNORE; + } + + if (!ped_device_write (geom->dev, buffer, real_start, count)) + return 0; + return 1; +} + +/** + * Checks for physical disk errors. \todo use ped_device_check() + * + * Checks a region for physical defects on \p geom. \p buffer is used + * for temporary storage for ped_geometry_check(), and has an undefined + * value. \p buffer is \p buffer_size sectors long. + * The region checked starts at \p offset sectors inside the + * region represented by \p geom, and is \p count sectors long. + * \p granularity specificies how sectors should be grouped + * together. The first bad sector to be returned will always be in + * the form: + * <tt>offset + n * granularity</tt> + * + * \return the first bad sector, or 0 if there were no physical errors + */ +PedSector +ped_geometry_check (PedGeometry* geom, void* buffer, PedSector buffer_size, + PedSector offset, PedSector granularity, PedSector count, + PedTimer* timer) +{ + PedSector group; + PedSector i; + PedSector read_len; + + PED_ASSERT (geom != NULL); + PED_ASSERT (buffer != NULL); + + ped_timer_reset (timer); + ped_timer_set_state_name (timer, _("checking for bad blocks")); + +retry: + ped_exception_fetch_all(); + for (group = offset; group < offset + count; group += buffer_size) { + ped_timer_update (timer, 1.0 * (group - offset) / count); + read_len = PED_MIN (buffer_size, offset + count - group); + if (!ped_geometry_read (geom, buffer, group, read_len)) + goto found_error; + } + ped_exception_leave_all(); + ped_timer_update (timer, 1.0); + return 0; + +found_error: + ped_exception_catch(); + for (i = group; i + granularity < group + count; i += granularity) { + if (!ped_geometry_read (geom, buffer, i, granularity)) { + ped_exception_catch(); + ped_exception_leave_all(); + return i; + } + } + ped_exception_leave_all(); + goto retry; /* weird: failure on group read, but not individually */ +} + +/** + * This function takes a \p sector inside the region described by src, and + * returns that sector's address inside dst. This means that + * + * \code + * ped_geometry_read (dst, buf, ped_geometry_map(dst, src, sector), 1) + * \endcode + * + * does the same thing as + * + * \code + * ped_geometry_read (src, buf, sector, 1) + * \endcode + * + * Clearly, this will only work if \p src and \p dst overlap. + * + * \return -1 if \p sector is not within \p dst's space, + * or \p sector's address inside \p dst + * + */ +PedSector +ped_geometry_map (const PedGeometry* dst, const PedGeometry* src, + PedSector sector) +{ + PedSector result; + + PED_ASSERT (dst != NULL); + PED_ASSERT (src != NULL); + + if (!ped_geometry_test_sector_inside (src, sector)) + return -1; + if (dst->dev != src->dev) + return -1; + + result = src->start + sector - dst->start; + if (result < 0 || result > dst->length) + return -1; + + return result; +} + +/** @} */ diff --git a/libparted/cs/natmath.c b/libparted/cs/natmath.c new file mode 100644 index 0000000..ea53afc --- /dev/null +++ b/libparted/cs/natmath.c @@ -0,0 +1,481 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 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/>. +*/ + +/** + * \file natmath.c + */ + +/** + * \addtogroup PedAlignment + * + * \brief Alignment constraint model. + * + * This part of libparted models alignment constraints. + * + * @{ + */ + +#include <config.h> +#include <stdlib.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/natmath.h> + +/* Arrrghhh! Why doesn't C have tuples? */ +typedef struct { + PedSector gcd; /* "converges" to the gcd */ + PedSector x; + PedSector y; +} EuclidTriple; + +static const PedAlignment _any = { + offset: 0, + grain_size: 1 +}; + +const PedAlignment* ped_alignment_any = &_any; +const PedAlignment* ped_alignment_none = NULL; + +/* This function returns "a mod b", the way C should have done it! + * Mathematicians prefer -3 mod 4 to be 3. Reason: division by N + * is all about adding or subtracting N, and we like our remainders + * to be between 0 and N - 1. + */ +static PedSector +abs_mod (PedSector a, PedSector b) +{ + if (a < 0) + return a % b + b; + else + return a % b; +} + +/* Rounds a number down to the closest number that is a multiple of + * grain_size. + */ +PedSector +ped_round_down_to (PedSector sector, PedSector grain_size) +{ + return sector - abs_mod (sector, grain_size); +} + +/* Rounds a number up to the closest number that is a multiple of + * grain_size. + */ +PedSector +ped_round_up_to (PedSector sector, PedSector grain_size) +{ + if (sector % grain_size) + return ped_round_down_to (sector, grain_size) + grain_size; + else + return sector; +} + +/* Rounds a number to the closest number that is a multiple of grain_size. */ +PedSector +ped_round_to_nearest (PedSector sector, PedSector grain_size) +{ + if (sector % grain_size > grain_size/2) + return ped_round_up_to (sector, grain_size); + else + return ped_round_down_to (sector, grain_size); +} + +/* This function returns the largest number that divides both a and b. + * It uses the ancient Euclidean algorithm. + */ +PedSector +ped_greatest_common_divisor (PedSector a, PedSector b) +{ + PED_ASSERT (a >= 0); + PED_ASSERT (b >= 0); + + /* Put the arguments in the "right" format. (Recursive calls made by + * this function are always in the right format.) + */ + if (b > a) + return ped_greatest_common_divisor (b, a); + + if (b) + return ped_greatest_common_divisor (b, a % b); + else + return a; +} + +/** + * Initialize a preallocated piece of memory for an alignment object + * (used by PedConstraint). + * + * The object will represent all sectors \e s for which the equation + * <tt>s = offset + X * grain_size</tt> holds. + */ +int +ped_alignment_init (PedAlignment* align, PedSector offset, PedSector grain_size) +{ + PED_ASSERT (align != NULL); + + if (grain_size < 0) + return 0; + + if (grain_size) + align->offset = abs_mod (offset, grain_size); + else + align->offset = offset; + align->grain_size = grain_size; + + return 1; +} + +/** + * Return an alignment object (used by PedConstraint), representing all + * PedSector's that are of the form <tt>offset + X * grain_size</tt>. + */ +PedAlignment* +ped_alignment_new (PedSector offset, PedSector grain_size) +{ + PedAlignment* align; + + align = (PedAlignment*) ped_malloc (sizeof (PedAlignment)); + if (!align) + goto error; + + if (!ped_alignment_init (align, offset, grain_size)) + goto error_free_align; + + return align; + +error_free_align: + free (align); +error: + return NULL; +} + +/** + * Free up memory associated with \p align. + */ +void +ped_alignment_destroy (PedAlignment* align) +{ + free (align); +} + +/** + * Return a duplicate of \p align. + */ +PedAlignment* +ped_alignment_duplicate (const PedAlignment* align) +{ + if (!align) + return NULL; + return ped_alignment_new (align->offset, align->grain_size); +} + +/* the extended Euclid algorithm. + * + * input: + * a and b, a > b + * + * output: + * gcd, x and y, such that: + * + * gcd = greatest common divisor of a and b + * gcd = x*a + y*b + */ +static EuclidTriple _GL_ATTRIBUTE_PURE +extended_euclid (int a, int b) +{ + EuclidTriple result; + EuclidTriple tmp; + + if (b == 0) { + result.gcd = a; + result.x = 1; + result.y = 0; + return result; + } + + tmp = extended_euclid (b, a % b); + result.gcd = tmp.gcd; + result.x = tmp.y; + result.y = tmp.x - (a/b) * tmp.y; + return result; +} + +/** + * This function computes a PedAlignment object that describes the + * intersection of two alignments. That is, a sector satisfies the + * new alignment object if and only if it satisfies both of the original + * ones. (See ped_alignment_is_aligned() for the meaning of "satisfies") + * + * Apart from the trivial cases (where one or both of the alignment objects + * constraints have no sectors that satisfy them), this is what we're trying to + * do: + * - two input constraints: \p a and \p b. + * - the new grain_size is going to be the lowest common multiple of + * \p a->grain_size and \p b->grain_size + * - hard part - solve the simultaneous equations, for offset, where offset, + * X and Y are variables. (Note: offset can be obtained from either X or Y, + * by substituing into either equation) + * + * \code + * offset = \p a->offset + X * \p a->grain_size (1) + * offset = \p b->offset + Y * \p b->grain_size (2) + * \endcode + * + * or, abbreviated: + * + * \code + * o = Ao + X*Ag (1) + * o = Bo + Y*Bg (2) + * + * => Ao + X*Ag = Bo + Y*Bg (1) = (2) + * X*Ag - Y*Bg = Bo - Ao (3) + * \endcode + * + * As it turns out, there only exists a solution if (Bo - Ao) is a multiple + * of the GCD of Ag and Bg. Reason: all linear combinations of Ag and Bg are + * multiples of the GCD. + * + * Proof: + * + * \code + * A * Ag + B * Bg + * = A * (\p a * gcd) + B * (\p b * gcd) + * = gcd * (A * \p a + B * \p b) + * \endcode + * + * gcd is a factor of the linear combination. QED + * + * Anyway, \p a * Ag + \p b * Bg = gcd can be solved (for \p a, \p b and gcd) + * with Euclid's extended algorithm. Then, we just multiply through by + * (Bo - Ao) / gcd to get (3). + * + * i.e. + * \code + * A * Ag + B * Bg = gcd + * A*(Bo-Ao)/gcd * Ag + B(Bo-Ao)/gcd * Bg = gcd * (Bo-Ao)/gcd + * X*Ag - Y*Bg = Bo - Ao (3) + * + * X = A*(Bo-Ao)/gcd + * Y = - B*(Bo-Ao)/gcd + * \endcode + * + * then: + * \code + * o = Ao + X*Ag (1) + * = Ao + A*(Bo-Ao)/gcd*Ag + * o = Bo + Y*Bg (2) + * = Bo - B*(Bo-Ao)/gcd*Ag + * \endcode + * + * Thanks go to Nathan Hurst (njh@hawthorn.csse.monash.edu.au) for figuring + * this algorithm out :-) + * + * \note Returned \c NULL is a valid PedAlignment object, and can be used + for ped_alignment_*() function. + * + * \return a PedAlignment on success, \c NULL on failure + */ +PedAlignment* +ped_alignment_intersect (const PedAlignment* a, const PedAlignment* b) +{ + PedSector new_grain_size; + PedSector new_offset; + PedSector delta_on_gcd; + EuclidTriple gcd_factors; + + + if (!a || !b) + return NULL; + + /*PED_DEBUG (0x10, "intersecting alignments (%d,%d) and (%d,%d)", + a->offset, a->grain_size, b->offset, b->grain_size); + */ + + if (a->grain_size < b->grain_size) { + const PedAlignment* tmp; + tmp = a; a = b; b = tmp; + } + + /* weird/trivial case: where the solution space for "a" or "b" is + * either empty or contains exactly one solution + */ + if (a->grain_size == 0 && b->grain_size == 0) { + if (a->offset == b->offset) + return ped_alignment_duplicate (a); + else + return NULL; + } + + /* general case */ + gcd_factors = extended_euclid (a->grain_size, b->grain_size); + + delta_on_gcd = (b->offset - a->offset) / gcd_factors.gcd; + new_offset = a->offset + gcd_factors.x * delta_on_gcd * a->grain_size; + new_grain_size = a->grain_size * b->grain_size / gcd_factors.gcd; + + /* inconsistency => no solution */ + if (new_offset + != b->offset - gcd_factors.y * delta_on_gcd * b->grain_size) + return NULL; + + return ped_alignment_new (new_offset, new_grain_size); +} + +/* This function returns the sector closest to "sector" that lies inside + * geom and satisfies the alignment constraint. + */ +static PedSector _GL_ATTRIBUTE_PURE +_closest_inside_geometry (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + PED_ASSERT (align != NULL); + + if (!align->grain_size) { + if (ped_alignment_is_aligned (align, geom, sector) + && (!geom || ped_geometry_test_sector_inside (geom, + sector))) + return sector; + else + return -1; + } + + if (sector < geom->start) + sector += ped_round_up_to (geom->start - sector, + align->grain_size); + if (sector > geom->end) + sector -= ped_round_up_to (sector - geom->end, + align->grain_size); + + if (!ped_geometry_test_sector_inside (geom, sector)) + return -1; + return sector; +} + +/** + * This function returns the closest sector to \p sector that lies inside + * \p geom that satisfies the given alignment constraint \p align. It prefers + * sectors that are beyond \p sector (are not smaller than \p sector), + * but does not guarantee that this. + * + * \return a PedSector on success, \c -1 on failure + */ +PedSector +ped_alignment_align_up (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + PedSector result; + + PED_ASSERT (align != NULL); + + if (!align->grain_size) + result = align->offset; + else + result = ped_round_up_to (sector - align->offset, + align->grain_size) + + align->offset; + + if (geom) + result = _closest_inside_geometry (align, geom, result); + return result; +} + +/** + * This function returns the closest sector to \p sector that lies inside + * \p geom that satisfies the given alignment constraint \p align. It prefers + * sectors that are before \p sector (are not larger than \p sector), + * but does not guarantee that this. + * + * \return a PedSector on success, \c -1 on failure + */ +PedSector +ped_alignment_align_down (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + PedSector result; + + PED_ASSERT (align != NULL); + + if (!align->grain_size) + result = align->offset; + else + result = ped_round_down_to (sector - align->offset, + align->grain_size) + + align->offset; + + if (geom) + result = _closest_inside_geometry (align, geom, result); + return result; +} + +/* Returns either a or b, depending on which is closest to "sector". */ +static PedSector +closest (PedSector sector, PedSector a, PedSector b) +{ + if (a == -1) + return b; + if (b == -1) + return a; + + if (llabs (sector - a) < llabs (sector - b)) + return a; + else + return b; +} + +/** + * This function returns the sector that is closest to \p sector, + * satisfies the \p align constraint and lies inside \p geom. + * + * \return a PedSector on success, \c -1 on failure + */ +PedSector +ped_alignment_align_nearest (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + PED_ASSERT (align != NULL); + + return closest (sector, ped_alignment_align_up (align, geom, sector), + ped_alignment_align_down (align, geom, sector)); +} + +/** + * This function returns 1 if \p sector satisfies the alignment + * constraint \p align and lies inside \p geom. + * + * \return \c 1 on success, \c 0 on failure + */ +int +ped_alignment_is_aligned (const PedAlignment* align, const PedGeometry* geom, + PedSector sector) +{ + if (!align) + return 0; + + if (geom && !ped_geometry_test_sector_inside (geom, sector)) + return 0; + + if (align->grain_size) + return (sector - align->offset) % align->grain_size == 0; + else + return sector == align->offset; +} + +/** + * @} + */ diff --git a/libparted/debug.c b/libparted/debug.c new file mode 100644 index 0000000..9a348bd --- /dev/null +++ b/libparted/debug.c @@ -0,0 +1,115 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2005, 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 <config.h> +#include <parted/parted.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#ifdef DEBUG + +#if HAVE_BACKTRACE +#include <execinfo.h> +#endif + +static void default_handler ( const int level, const char* file, int line, + const char* function, const char* msg ); +static PedDebugHandler* debug_handler = &default_handler; + + +/** + * Default debug handler. + * Will print all information to stderr. + */ +static void default_handler ( const int level, const char* file, int line, + const char* function, const char* msg ) +{ + fprintf ( stderr, "[%d] %s:%d (%s): %s\n", + level, file, line, function, msg ); +} + +/** + * Send a debug message. + * Do not call this directly -- use PED_DEBUG() instead. + * + * level log level, 0 ~= "print definitely" + */ +void ped_debug ( const int level, const char* file, int line, + const char* function, const char* msg, ... ) +{ + va_list arg_list; + char* msg_concat = ped_malloc(8192); + + va_start ( arg_list, msg ); + vsnprintf ( msg_concat, 8192, msg, arg_list ); + va_end ( arg_list ); + + debug_handler ( level, file, line, function, msg_concat ); + + free ( msg_concat ); +} + +/* + * handler debug handler; NULL for default handler + */ +void ped_debug_set_handler ( PedDebugHandler* handler ) +{ + debug_handler = ( handler ? handler : default_handler ); +} + +/* + * Check an assertion. + * Do not call this directly -- use PED_ASSERT() instead. + */ +void ped_assert (const char* cond_text, + const char* file, int line, const char* function) +{ +#if HAVE_BACKTRACE + /* Print backtrace stack */ + void *stack[20]; + char **strings, **string; + int size = backtrace(stack, 20); + strings = backtrace_symbols(stack, size); + + if (strings) { + printf(_("Backtrace has %d calls on stack:\n"), size); + + for (string = strings; size > 0; size--, string++) + printf(" %d: %s\n", size, *string); + + free(strings); + } +#endif + + /* Throw the exception */ + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Assertion (%s) at %s:%d in function %s() failed."), + cond_text, file, line, function); + abort (); +} + +#endif /* DEBUG */ diff --git a/libparted/device.c b/libparted/device.c new file mode 100644 index 0000000..36fecd2 --- /dev/null +++ b/libparted/device.c @@ -0,0 +1,562 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999 - 2001, 2005, 2007-2010 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/>. +*/ + +/** \file device.c */ + +/** + * \addtogroup PedDevice + * + * \brief Device access. + * + * When ped_device_probe_all() is called, libparted attempts to detect all + * devices. It constructs a list which can be accessed with + * ped_device_get_next(). + * + * If you want to use a device that isn't on the list, use + * ped_device_get(). Also, there may be OS-specific constructors, for creating + * devices from file descriptors, stores, etc. For example, + * ped_device_new_from_store(). + * + * @{ + */ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> + +#include <limits.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> + +#include "architecture.h" + +static PedDevice* devices; /* legal advice says: initialized to NULL, + under section 6.7.8 part 10 + of ISO/EIC 9899:1999 */ + +static void +_device_register (PedDevice* dev) +{ + PedDevice* walk; + for (walk = devices; walk && walk->next; walk = walk->next); + if (walk) + walk->next = dev; + else + devices = dev; + dev->next = NULL; +} + +static void +_device_unregister (PedDevice* dev) +{ + PedDevice* walk; + PedDevice* last = NULL; + + for (walk = devices; walk != NULL; last = walk, walk = walk->next) { + if (walk == dev) break; + } + + /* This function may be called twice for the same device if a + libparted user explictly removes the device from the cache using + ped_device_cache_remove(), we get called and it then becomes the + user's responsibility to free the PedDevice by calling + ped_device_destroy(). + ped_device_destroy() will then call us a second time, so if the + device is not found in the list do nothing. */ + if (walk == NULL) + return; + + if (last) + last->next = dev->next; + else + devices = dev->next; +} + +/** + * Returns the next device that was detected by ped_device_probe_all(), or + * calls to ped_device_get_next(). + * If dev is NULL, returns the first device. + * + * \return NULL if dev is the last device. + */ +PedDevice* +ped_device_get_next (const PedDevice* dev) +{ + if (dev) + return dev->next; + else + return devices; +} + +void +_ped_device_probe (const char* path) +{ + PedDevice* dev; + + PED_ASSERT (path != NULL); + + ped_exception_fetch_all (); + dev = ped_device_get (path); + if (!dev) + ped_exception_catch (); + ped_exception_leave_all (); +} + +/** + * Attempts to detect all devices. + */ +void +ped_device_probe_all () +{ + ped_architecture->dev_ops->probe_all (); +} + +/** + * Close/free all devices. + * Called by ped_done(), so you do not need to worry about it. + */ +void +ped_device_free_all () +{ + while (devices) + ped_device_destroy (devices); +} + +/** + * Gets the device "name", where name is usually the block device, e.g. + * /dev/sdb. If the device wasn't detected with ped_device_probe_all(), + * an attempt will be made to detect it again. If it is found, it will + * be added to the list. + */ +PedDevice* +ped_device_get (const char* path) +{ + PedDevice* walk; + char* normal_path = NULL; + + PED_ASSERT (path != NULL); + /* Don't canonicalize /dev/mapper or /dev/md/ paths, see + tests/symlink.c + */ + if (strncmp (path, "/dev/mapper/", 12) && + strncmp (path, "/dev/md/", 8)) + normal_path = canonicalize_file_name (path); + if (!normal_path) + /* Well, maybe it is just that the file does not exist. + * Try it anyway. */ + normal_path = strdup (path); + if (!normal_path) + return NULL; + + for (walk = devices; walk != NULL; walk = walk->next) { + if (!strcmp (walk->path, normal_path)) { + free (normal_path); + return walk; + } + } + + walk = ped_architecture->dev_ops->_new (normal_path); + free (normal_path); + if (!walk) + return NULL; + _device_register (walk); + return walk; +} + +/** + * Destroys a device and removes it from the device list, and frees + * all resources associated with the device (all resources allocated + * when the device was created). + */ +void +ped_device_destroy (PedDevice* dev) +{ + _device_unregister (dev); + + while (dev->open_count) { + if (!ped_device_close (dev)) + break; + } + + ped_architecture->dev_ops->destroy (dev); +} + +void +ped_device_cache_remove(PedDevice *dev) +{ + _device_unregister (dev); +} + +int +ped_device_is_busy (PedDevice* dev) +{ + return ped_architecture->dev_ops->is_busy (dev); +} + +/** + * Attempt to open a device to allow use of read, write and sync functions. + * + * The meaning of "open" is architecture-dependent. Apart from requesting + * access to the device from the operating system, it does things like flushing + * caches. + * \note May allocate resources. Any resources allocated here will + * be freed by a final ped_device_close(). (ped_device_open() may be + * called multiple times -- it's a ref-count-like mechanism) + * + * \return zero on failure + */ +int +ped_device_open (PedDevice* dev) +{ + int status; + + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + + if (dev->open_count) + status = ped_architecture->dev_ops->refresh_open (dev); + else + status = ped_architecture->dev_ops->open (dev); + if (status) + dev->open_count++; + return status; +} + +/** + * Close dev. + * If this is the final close, then resources allocated by + * ped_device_open() are freed. + * + * \return zero on failure + */ +int +ped_device_close (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + PED_ASSERT (dev->open_count > 0); + + if (--dev->open_count) + return ped_architecture->dev_ops->refresh_close (dev); + else + return ped_architecture->dev_ops->close (dev); +} + +/** + * Begins external access mode. External access mode allows you to + * safely do IO on the device. If a PedDevice is open, then you should + * not do any IO on that device, e.g. by calling an external program + * like e2fsck, unless you put it in external access mode. You should + * not use any libparted commands that do IO to a device, e.g. + * ped_file_system_{open|resize|copy}, ped_disk_{read|write}), while + * a device is in external access mode. + * Also, you should not ped_device_close() a device, while it is + * in external access mode. + * Note: ped_device_begin_external_access_mode() does things like + * tell the kernel to flush its caches. + * + * Close a device while pretending it is still open. + * This is useful for temporarily suspending libparted access to the device + * in order for an external program to access it. + * (Running external programs while the device is open can cause cache + * coherency problems.) + * + * In particular, this function keeps track of dev->open_count, so that + * reference counting isn't screwed up. + * + * \return zero on failure. + */ +int +ped_device_begin_external_access (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + + dev->external_mode = 1; + if (dev->open_count) + return ped_architecture->dev_ops->close (dev); + else + return 1; +} + +/** + * \brief Complementary function to ped_device_begin_external_access. + * + * \note does things like tell the kernel to flush the device's cache. + * + * \return zero on failure. + */ +int +ped_device_end_external_access (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (dev->external_mode); + + dev->external_mode = 0; + if (dev->open_count) + return ped_architecture->dev_ops->open (dev); + else + return 1; +} + +/** + * \internal Read count sectors from dev into buffer, beginning with sector + * start. + * + * \return zero on failure. + */ +int +ped_device_read (const PedDevice* dev, void* buffer, PedSector start, + PedSector count) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (buffer != NULL); + PED_ASSERT (!dev->external_mode); + PED_ASSERT (dev->open_count > 0); + + return (ped_architecture->dev_ops->read) (dev, buffer, start, count); +} + +/** + * \internal Write count sectors from buffer to dev, starting at sector + * start. + * + * \return zero on failure. + * + * \sa PedDevice::sector_size + * \sa PedDevice::phys_sector_size + */ +int +ped_device_write (PedDevice* dev, const void* buffer, PedSector start, + PedSector count) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (buffer != NULL); + PED_ASSERT (!dev->external_mode); + PED_ASSERT (dev->open_count > 0); + + return (ped_architecture->dev_ops->write) (dev, buffer, start, count); +} + +PedSector +ped_device_check (PedDevice* dev, void* buffer, PedSector start, + PedSector count) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + PED_ASSERT (dev->open_count > 0); + + return (ped_architecture->dev_ops->check) (dev, buffer, start, count); +} + +/** + * \internal Flushes all write-behind caches that might be holding up + * writes. + * It is slow because it guarantees cache coherency among all relevant caches. + * + * \return zero on failure + */ +int +ped_device_sync (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + PED_ASSERT (dev->open_count > 0); + + return ped_architecture->dev_ops->sync (dev); +} + +/** + * \internal Flushes all write-behind caches that might be holding writes. + * \warning Does NOT ensure cache coherency with other caches. + * If you need cache coherency, use ped_device_sync() instead. + * + * \return zero on failure + */ +int +ped_device_sync_fast (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + PED_ASSERT (!dev->external_mode); + PED_ASSERT (dev->open_count > 0); + + return ped_architecture->dev_ops->sync_fast (dev); +} + +/** + * Get a constraint that represents hardware requirements on geometry. + * This function will return a constraint representing the limits imposed + * by the size of the disk, it will *not* provide any alignment constraints. + * + * Alignment constraints may be desirable when using media that have a physical + * sector size that is a multiple of the logical sector size, as in this case + * proper partition alignment can benefit disk performance signigicantly. + * When you want a constraint with alignment info, use + * ped_device_get_minimal_aligned_constraint() or + * ped_device_get_optimal_aligned_constraint(). + * + * \return NULL on error, otherwise a pointer to a dynamically allocated + * constraint. + */ +PedConstraint* +ped_device_get_constraint (const PedDevice* dev) +{ + PedGeometry *s, *e; + PedConstraint* c = ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + s = ped_geometry_new (dev, 0, dev->length), + e = ped_geometry_new (dev, 0, dev->length), + 1, dev->length); + + free (s); + free (e); + return c; +} + +static PedConstraint* +_ped_device_get_aligned_constraint(const PedDevice *dev, + PedAlignment* start_align) +{ + PedAlignment *end_align = NULL; + PedGeometry *whole_dev_geom = NULL; + PedConstraint *c = NULL; + + if (start_align) { + end_align = ped_alignment_new(start_align->offset - 1, + start_align->grain_size); + if (!end_align) + goto free_start_align; + } + + whole_dev_geom = ped_geometry_new (dev, 0, dev->length); + + if (start_align) + c = ped_constraint_new (start_align, end_align, + whole_dev_geom, whole_dev_geom, + 1, dev->length); + else + c = ped_constraint_new (ped_alignment_any, ped_alignment_any, + whole_dev_geom, whole_dev_geom, + 1, dev->length); + + free (whole_dev_geom); + free (end_align); +free_start_align: + free (start_align); + return c; +} + +/** + * Get a constraint that represents hardware requirements on geometry and + * alignment. + * + * This function will return a constraint representing the limits imposed + * by the size of the disk and the minimal alignment requirements for proper + * performance of the disk. + * + * \return NULL on error, otherwise a pointer to a dynamically allocated + * constraint. + */ +PedConstraint* +ped_device_get_minimal_aligned_constraint(const PedDevice *dev) +{ + return _ped_device_get_aligned_constraint(dev, + ped_device_get_minimum_alignment(dev)); +} + +/** + * Get a constraint that represents hardware requirements on geometry and + * alignment. + * + * This function will return a constraint representing the limits imposed + * by the size of the disk and the alignment requirements for optimal + * performance of the disk. + * + * \return NULL on error, otherwise a pointer to a dynamically allocated + * constraint. + */ +PedConstraint* +ped_device_get_optimal_aligned_constraint(const PedDevice *dev) +{ + return _ped_device_get_aligned_constraint(dev, + ped_device_get_optimum_alignment(dev)); +} + +/** + * Get an alignment that represents minimum hardware requirements on alignment. + * When for example using media that has a physical sector size that is a + * multiple of the logical sector size, it is desirable to have disk accesses + * (and thus partitions) properly aligned. Having partitions not aligned to + * the minimum hardware requirements may lead to a performance penalty. + * + * The returned alignment describes the alignment for the start sector of the + * partition, the end sector should be aligned too, to get the end sector + * alignment decrease the returned alignment's offset by 1. + * + * \return the minimum alignment of partition start sectors, or NULL if this + * information is not available. + */ +PedAlignment* +ped_device_get_minimum_alignment(const PedDevice *dev) +{ + PedAlignment *align = NULL; + + if (ped_architecture->dev_ops->get_minimum_alignment) + align = ped_architecture->dev_ops->get_minimum_alignment(dev); + + if (align == NULL) + align = ped_alignment_new(0, + dev->phys_sector_size / dev->sector_size); + + return align; +} + +/** + * Get an alignment that represents the hardware requirements for optimal + * performance. + * + * The returned alignment describes the alignment for the start sector of the + * partition, the end sector should be aligned too, to get the end sector + * alignment decrease the returned alignment's offset by 1. + * + * \return the optimal alignment of partition start sectors, or NULL if this + * information is not available. + */ +PedAlignment* +ped_device_get_optimum_alignment(const PedDevice *dev) +{ + PedAlignment *align = NULL; + + if (ped_architecture->dev_ops->get_optimum_alignment) + align = ped_architecture->dev_ops->get_optimum_alignment(dev); + + /* If the arch specific code could not give as an alignment + return a default value based on the type of device. */ + if (align == NULL) { + /* Align to a grain of 1MiB (like vista / win7) */ + align = ped_alignment_new(0, + (PED_DEFAULT_ALIGNMENT + / dev->sector_size)); + } + + return align; +} + +/** @} */ diff --git a/libparted/disk.c b/libparted/disk.c new file mode 100644 index 0000000..0db7b5c --- /dev/null +++ b/libparted/disk.c @@ -0,0 +1,2668 @@ + /* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2003, 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/>. +*/ + +/** \file disk.c */ + +/** + * \addtogroup PedDisk + * + * \brief Disk label access. + * + * Most programs will need to use ped_disk_new() or ped_disk_new_fresh() to get + * anything done. A PedDisk is always associated with a device and has a + * partition table. There are different types of partition tables (or disk + * labels). These are represented by the PedDiskType enumeration. + * + * @{ + */ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <stdbool.h> +#include <limits.h> + +#include "architecture.h" +#include "labels/pt-tools.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +# define N_(String) (String) +#else +# define _(String) (String) +# define N_(String) (String) +#endif /* ENABLE_NLS */ + +/* UPDATE MODE functions */ +#ifdef DEBUG +static int _disk_check_sanity (PedDisk* disk); +#endif +static int _disk_push_update_mode (PedDisk* disk); +static int _disk_pop_update_mode (PedDisk* disk); +static int _disk_raw_insert_before (PedDisk* disk, PedPartition* loc, + PedPartition* part); +static int _disk_raw_insert_after (PedDisk* disk, PedPartition* loc, + PedPartition* part); +static int _disk_raw_remove (PedDisk* disk, PedPartition* part); +static int _disk_raw_add (PedDisk* disk, PedPartition* part); + +static PedDiskType* disk_types = NULL; + +void +ped_disk_type_register (PedDiskType* disk_type) +{ + PED_ASSERT (disk_type != NULL); + PED_ASSERT (disk_type->ops != NULL); + PED_ASSERT (disk_type->name != NULL); + + disk_type->next = disk_types; + disk_types = disk_type; +} + +void +ped_disk_type_unregister (PedDiskType* disk_type) +{ + PedDiskType* walk; + PedDiskType* last = NULL; + + PED_ASSERT (disk_types != NULL); + PED_ASSERT (disk_type != NULL); + + for (walk = disk_types; walk && walk != disk_type; + last = walk, walk = walk->next); + + PED_ASSERT (walk != NULL); + if (last) + ((struct _PedDiskType*) last)->next = disk_type->next; + else + disk_types = disk_type->next; +} + +/** + * Return the next disk type registers, after "type". If "type" is + * NULL, returns the first disk type. + * + * \return Next disk; NULL if "type" is the last registered disk type. + */ +PedDiskType* +ped_disk_type_get_next (PedDiskType const *type) +{ + if (type) + return type->next; + else + return disk_types; +} + +/** + * Return the disk type with a name of "name". + * + * \return Disk type; NULL if no match. + */ +PedDiskType* +ped_disk_type_get (const char* name) +{ + PedDiskType* walk = NULL; + + PED_ASSERT (name != NULL); + + for (walk = ped_disk_type_get_next (NULL); walk; + walk = ped_disk_type_get_next (walk)) + if (strcasecmp (walk->name, name) == 0) + break; + + return walk; +} + +/** + * Return the type of partition table detected on "dev". + * + * \return Type; NULL if none was detected. + */ +PedDiskType* +ped_disk_probe (PedDevice* dev) +{ + PedDiskType* walk = NULL; + + PED_ASSERT (dev != NULL); + + if (!ped_device_open (dev)) + return NULL; + + ped_exception_fetch_all (); + for (walk = ped_disk_type_get_next (NULL); walk; + walk = ped_disk_type_get_next (walk)) + { + if (getenv ("PARTED_DEBUG")) { + fprintf (stderr, "probe label: %s\n", + walk->name); + fflush (stderr); + } + if (walk->ops->probe (dev)) + break; + } + + if (ped_exception) + ped_exception_catch (); + ped_exception_leave_all (); + + ped_device_close (dev); + return walk; +} + +/** + * Read the partition table off a device (if one is found). + * + * \warning May modify \p dev->cylinders, \p dev->heads and \p dev->sectors + * if the partition table indicates that the existing values + * are incorrect. + * + * \return A new \link _PedDisk PedDisk \endlink object; + * NULL on failure (e.g. partition table not detected). + */ +PedDisk* +ped_disk_new (PedDevice* dev) +{ + PedDiskType* type; + PedDisk* disk; + + PED_ASSERT (dev != NULL); + + if (!ped_device_open (dev)) + goto error; + + type = ped_disk_probe (dev); + if (!type) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s: unrecognised disk label"), + dev->path); + goto error_close_dev; + } + disk = ped_disk_new_fresh (dev, type); + if (!disk) + goto error_close_dev; + if (!type->ops->read (disk)) + goto error_destroy_disk; + disk->needs_clobber = 0; + ped_device_close (dev); + return disk; + +error_destroy_disk: + ped_disk_destroy (disk); +error_close_dev: + ped_device_close (dev); +error: + return NULL; +} + +static int +_add_duplicate_part (PedDisk* disk, PedPartition* old_part) +{ + PedPartition* new_part; + int ret; + + new_part = disk->type->ops->partition_duplicate (old_part); + if (!new_part) + goto error; + new_part->disk = disk; + + if (!_disk_push_update_mode (disk)) + goto error_destroy_new_part; + ret = _disk_raw_add (disk, new_part); + if (!_disk_pop_update_mode (disk)) + goto error_destroy_new_part; + if (!ret) + goto error_destroy_new_part; +#ifdef DEBUG + if (!_disk_check_sanity (disk)) + goto error_destroy_new_part; +#endif + return 1; + +error_destroy_new_part: + ped_partition_destroy (new_part); +error: + return 0; +} + +/** + * Clone a \link _PedDisk PedDisk \endlink object. + * + * \return Deep copy of \p old_disk, NULL on failure. + */ +PedDisk* +ped_disk_duplicate (const PedDisk* old_disk) +{ + PedDisk* new_disk; + PedPartition* old_part; + + PED_ASSERT (old_disk != NULL); + PED_ASSERT (!old_disk->update_mode); + PED_ASSERT (old_disk->type->ops->duplicate != NULL); + PED_ASSERT (old_disk->type->ops->partition_duplicate != NULL); + + new_disk = old_disk->type->ops->duplicate (old_disk); + if (!new_disk) + goto error; + + if (!_disk_push_update_mode (new_disk)) + goto error_destroy_new_disk; + for (old_part = ped_disk_next_partition (old_disk, NULL); old_part; + old_part = ped_disk_next_partition (old_disk, old_part)) { + if (ped_partition_is_active (old_part)) { + if (!_add_duplicate_part (new_disk, old_part)){ + _disk_pop_update_mode (new_disk); + goto error_destroy_new_disk; + } + } + } + if (!_disk_pop_update_mode (new_disk)) + goto error_destroy_new_disk; + + new_disk->needs_clobber = old_disk->needs_clobber; + + return new_disk; + +error_destroy_new_disk: + ped_disk_destroy (new_disk); +error: + return NULL; +} + +/* Given a partition table type NAME, e.g., "gpt", return its PedDiskType + handle. If no known type has a name matching NAME, return NULL. */ +static PedDiskType const * _GL_ATTRIBUTE_PURE +find_disk_type (char const *name) +{ + PedDiskType const *t; + for (t = ped_disk_type_get_next (NULL); t; t = ped_disk_type_get_next (t)) + { + if (strcmp (t->name, name) == 0) + return t; + } + return NULL; +} + +/** + * Remove all identifying signatures of a partition table, + * + * \return 0 on error, 1 otherwise. + * + * \sa ped_disk_clobber() + */ +int +ped_disk_clobber (PedDevice* dev) +{ + PED_ASSERT (dev != NULL); + + if (!ped_device_open (dev)) + goto error; + + PedDiskType const *gpt = find_disk_type ("gpt"); + PED_ASSERT (gpt != NULL); + + /* If there is a GPT table, don't clobber the protective MBR. */ + bool is_gpt = gpt->ops->probe (dev); + PedSector first_sector = (is_gpt ? 1 : 0); + + /* How many sectors to zero out at each end. + This must be large enough to zero out the magic bytes + starting at offset 8KiB on a DASD partition table. + Doing the same from the end of the disk is probably + overkill, but at least on GPT, we do need to zero out + the final sector. */ + const PedSector n_sectors = 9 * 1024 / dev->sector_size + 1; + + /* Clear the first few. */ + PedSector n = n_sectors; + if (dev->length < first_sector + n_sectors) + n = dev->length - first_sector; + if (!ptt_clear_sectors (dev, first_sector, n)) + goto error_close_dev; + + /* Clear the last few. */ + PedSector t = (dev->length - + (n_sectors < dev->length ? n_sectors : 1)); + + /* Don't clobber the pMBR if we have a pathologically small disk. */ + if (t < first_sector) + t = first_sector; + if (!ptt_clear_sectors (dev, t, dev->length - t)) + goto error_close_dev; + + ped_device_close (dev); + return 1; + +error_close_dev: + ped_device_close (dev); +error: + return 0; +} + +/** + * Create a new partition table on \p dev. + * + * This new partition table is only created in-memory, and nothing is written + * to disk until ped_disk_commit_to_dev() is called. + * + * \return The newly constructed \link _PedDisk PedDisk \endlink, + * NULL on failure. + */ +PedDisk* +ped_disk_new_fresh (PedDevice* dev, const PedDiskType* type) +{ + PedDisk* disk; + + PED_ASSERT (dev != NULL); + PED_ASSERT (type != NULL); + PED_ASSERT (type->ops->alloc != NULL); + PedCHSGeometry* bios_geom = &dev->bios_geom; + PED_ASSERT (bios_geom->sectors != 0); + PED_ASSERT (bios_geom->heads != 0); + + disk = type->ops->alloc (dev); + if (!disk) + goto error; + if (!_disk_pop_update_mode (disk)) + goto error_destroy_disk; + PED_ASSERT (disk->update_mode == 0); + + disk->needs_clobber = 1; + return disk; + +error_destroy_disk: + ped_disk_destroy (disk); +error: + return NULL; +} + +PedDisk* +_ped_disk_alloc (const PedDevice* dev, const PedDiskType* disk_type) +{ + PedDisk* disk; + + disk = (PedDisk*) ped_malloc (sizeof (PedDisk)); + if (!disk) + goto error; + + disk->dev = (PedDevice*)dev; + disk->type = disk_type; + disk->update_mode = 1; + disk->part_list = NULL; + disk->needs_clobber = 0; + return disk; + +error: + return NULL; +} + +void +_ped_disk_free (PedDisk* disk) +{ + _disk_push_update_mode (disk); + ped_disk_delete_all (disk); + free (disk); +} + +/** + * Close \p disk. + * + * What this function does depends on the PedDiskType of \p disk, + * but you can generally assume that outstanding writes are flushed + * (this mainly means that _ped_disk_free is called). + */ +void +ped_disk_destroy (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (!disk->update_mode); + + disk->type->ops->free (disk); +} + +/** + * Tell the operating system kernel about the partition table layout + * of \p disk. + * + * This is rather loosely defined: for example, on old versions of Linux, + * it simply calls the BLKRRPART ioctl, which tells the kernel to + * reread the partition table. On newer versions (2.4.x), it will + * use the new blkpg interface to tell Linux where each partition + * starts/ends, etc. In this case, Linux does not need to have support for + * a specific type of partition table. + * + * \return 0 on failure, 1 otherwise. + */ +int +ped_disk_commit_to_os (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + + if (!ped_device_open (disk->dev)) + goto error; + if (!ped_architecture->disk_ops->disk_commit (disk)) + goto error_close_dev; + ped_device_close (disk->dev); + return 1; + +error_close_dev: + ped_device_close (disk->dev); +error: + return 0; +} + +/** + * Write the changes made to the in-memory description + * of a partition table to the device. + * + * \return 0 on failure, 1 otherwise. + */ +int +ped_disk_commit_to_dev (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (!disk->update_mode); + + if (!disk->type->ops->write) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("This libparted doesn't have write support for " + "%s. Perhaps it was compiled read-only."), + disk->type->name); + goto error; + } + + if (!ped_device_open (disk->dev)) + goto error; + + if (disk->needs_clobber) { + if (!ped_disk_clobber (disk->dev)) + goto error_close_dev; + disk->needs_clobber = 0; + } + if (!disk->type->ops->write (disk)) + goto error_close_dev; + ped_device_close (disk->dev); + return 1; + +error_close_dev: + ped_device_close (disk->dev); +error: + return 0; +} + +/* + * This function writes the in-memory changes to a partition table to + * disk and informs the operating system of the changes. + * + * \note Equivalent to calling first ped_disk_commit_to_dev(), then + * ped_disk_commit_to_os(). + * + * \return 0 on failure, 1 otherwise. + */ +int +ped_disk_commit (PedDisk* disk) +{ + /* Open the device here, so that the underlying fd is not closed + between commit_to_dev and commit_to_os (closing causes unwanted + udev events to be sent under Linux). */ + if (!ped_device_open (disk->dev)) + goto error; + + if (!ped_disk_commit_to_dev (disk)) + goto error_close_dev; + + if (!ped_disk_commit_to_os (disk)) + goto error_close_dev; + + ped_device_close (disk->dev); + return 1; + +error_close_dev: + ped_device_close (disk->dev); +error: + return 0; +} + +/** + * \addtogroup PedPartition + * + * @{ + */ + +/** + * Check whether a partition is mounted or busy in some + * other way. + * + * \note An extended partition is busy if any logical partitions are mounted. + * + * \return \c 1 if busy. + */ +int +ped_partition_is_busy (const PedPartition* part) +{ + PED_ASSERT (part != NULL); + + return ped_architecture->disk_ops->partition_is_busy (part); +} + +/** + * Return a path that can be used to address the partition in the + * operating system. + */ +char* +ped_partition_get_path (const PedPartition* part) +{ + PED_ASSERT (part != NULL); + + return ped_architecture->disk_ops->partition_get_path (part); +} + +/** @} */ + +/** + * \addtogroup PedDisk + * + * @{ + */ + +/** + * Perform a sanity check on a partition table. + * + * \note The check performed is generic (i.e. it does not depends on the label + * type of the disk. + * + * \throws PED_EXCEPTION_WARNING if a partition type ID does not match the file + * system on it. + * + * \return 0 if the check fails, 1 otherwise. + */ +int +ped_disk_check (const PedDisk* disk) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL); + + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + const PedFileSystemType* fs_type = walk->fs_type; + PedGeometry* geom; + PedSector length_error; + PedSector max_length_error; + + if (!ped_partition_is_active (walk) || !fs_type) + continue; + + geom = ped_file_system_probe_specific (fs_type, &walk->geom); + if (!geom) + continue; + + length_error = llabs (walk->geom.length - geom->length); + max_length_error = PED_MAX (4096, walk->geom.length / 100); + bool ok = (ped_geometry_test_inside (&walk->geom, geom) + && length_error <= max_length_error); + char *fs_size = ped_unit_format (disk->dev, geom->length); + ped_geometry_destroy (geom); + if (!ok) { + char* part_size = ped_unit_format (disk->dev, + walk->geom.length); + PedExceptionOption choice; + choice = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Partition %d is %s, but the file system is " + "%s."), + walk->num, part_size, fs_size); + + free (part_size); + + free (fs_size); + fs_size = NULL; + + if (choice != PED_EXCEPTION_IGNORE) + return 0; + } + free (fs_size); + } + + return 1; +} + +/** + * This function checks if a particular type of partition table supports + * a feature. + * + * \return 1 if \p disk_type supports \p feature, 0 otherwise. + */ +int +ped_disk_type_check_feature (const PedDiskType* disk_type, + PedDiskTypeFeature feature) +{ + return (disk_type->features & feature) != 0; +} + +/** + * Get the number of primary partitions. + */ +int +ped_disk_get_primary_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; +} + +/** + * Get the highest available partition number on \p disk. + */ +int +ped_disk_get_last_partition_num (const PedDisk* disk) +{ + PedPartition* walk; + int highest = -1; + + PED_ASSERT (disk != NULL); + + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (walk->num > highest) + highest = walk->num; + } + + return highest; +} + +/** + * Get the highest supported partition number on \p disk. + * + * \return 0 if call fails. 1 otherwise. + */ +bool +ped_disk_get_max_supported_partition_count(const PedDisk* disk, int* supported) +{ + PED_ASSERT(disk != NULL); + PED_ASSERT(disk->type->ops->get_max_supported_partition_count != NULL); + + return disk->type->ops->get_max_supported_partition_count(disk, supported); +} + +/** + * Get the alignment needed for partition boundaries on this disk. + * The returned alignment describes the alignment for the start sector of the + * partition, for all disklabel types which require alignment, except Sun + * disklabels, the end sector must be aligned too. To get the end sector + * alignment decrease the PedAlignment offset by 1. + * + * \return NULL on error, otherwise a pointer to a dynamically allocated + * alignment. + */ +PedAlignment* +ped_disk_get_partition_alignment(const PedDisk *disk) +{ + /* disklabel handlers which don't need alignment don't define this */ + if (!disk->type->ops->get_partition_alignment) + return ped_alignment_duplicate(ped_alignment_any); + + return disk->type->ops->get_partition_alignment(disk); +} + +/** + * Get the maximum number of (primary) partitions the disk label supports. + * + * For example, MacIntosh partition maps can have different sizes, + * and accordingly support a different number of partitions. + */ +int +ped_disk_get_max_primary_partition_count (const PedDisk* disk) +{ + PED_ASSERT (disk->type != NULL); + PED_ASSERT (disk->type->ops->get_max_primary_partition_count != NULL); + + return disk->type->ops->get_max_primary_partition_count (disk); +} + +/** + * Set the state (\c 1 or \c 0) of a flag on a disk. + * + * \note It is an error to call this on an unavailable flag -- use + * ped_disk_is_flag_available() to determine which flags are available + * for a given disk label. + * + * \throws PED_EXCEPTION_ERROR if the requested flag is not available for this + * label. + */ +int +ped_disk_set_flag(PedDisk *disk, PedDiskFlag flag, int state) +{ + int ret; + + PED_ASSERT (disk != NULL); + + PedDiskOps *ops = disk->type->ops; + + if (!_disk_push_update_mode(disk)) + return 0; + + if (!ped_disk_is_flag_available(disk, flag)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "The flag '%s' is not available for %s disk labels.", + ped_disk_flag_get_name(flag), + disk->type->name); + _disk_pop_update_mode(disk); + return 0; + } + + ret = ops->disk_set_flag(disk, flag, state); + + if (!_disk_pop_update_mode (disk)) + return 0; + + return ret; +} + +/** + * Get the state (\c 1 or \c 0) of a flag on a disk. + */ +int +ped_disk_get_flag(const PedDisk *disk, PedDiskFlag flag) +{ + PED_ASSERT (disk != NULL); + + PedDiskOps *ops = disk->type->ops; + + if (!ped_disk_is_flag_available(disk, flag)) + return 0; + + return ops->disk_get_flag(disk, flag); +} + +/** + * Check whether a given flag is available on a disk. + * + * \return \c 1 if the flag is available. + */ +int +ped_disk_is_flag_available(const PedDisk *disk, PedDiskFlag flag) +{ + PED_ASSERT (disk != NULL); + + PedDiskOps *ops = disk->type->ops; + + if (!ops->disk_is_flag_available) + return 0; + + return ops->disk_is_flag_available(disk, flag); +} + +/** + * Returns a name for a \p flag, e.g. PED_DISK_CYLINDER_ALIGNMENT will return + * "cylinder_alignment". + * + * \note The returned string will be in English. However, + * translations are provided, so the caller can call + * dgettext("parted", RESULT) on the result. + */ +const char * +ped_disk_flag_get_name(PedDiskFlag flag) +{ + switch (flag) { + case PED_DISK_CYLINDER_ALIGNMENT: + return N_("cylinder_alignment"); + case PED_DISK_GPT_PMBR_BOOT: + return N_("pmbr_boot"); + default: + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Unknown disk flag, %d."), + flag); + return NULL; + } +} + +/** + * Returns the flag associated with \p name. + * + * \p name can be the English + * string, or the translation for the native language. + */ +PedDiskFlag +ped_disk_flag_get_by_name(const char *name) +{ + PedDiskFlag flag; + + for (flag = ped_disk_flag_next(0); flag; + flag = ped_disk_flag_next(flag)) { + const char *flag_name = ped_disk_flag_get_name(flag); + if (strcasecmp(name, flag_name) == 0 + || strcasecmp(name, _(flag_name)) == 0) + return flag; + } + + return 0; +} + +/** + * Iterates through all disk flags. + * + * ped_disk_flag_next(0) returns the first flag + * + * \return the next flag, or 0 if there are no more flags + */ +PedDiskFlag +ped_disk_flag_next(PedDiskFlag flag) +{ + return (flag + 1) % (PED_DISK_LAST_FLAG + 1); +} + +static int +_assert_disk_uuid_feature (const PedDiskType* disk_type) +{ + if (!ped_disk_type_check_feature ( + disk_type, PED_DISK_TYPE_DISK_UUID)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "%s disk labels do not support disk uuids.", + disk_type->name); + return 0; + } + return 1; +} + +/** + * Get the uuid of the disk \p disk. This will only work if the disk label + * supports it. + */ +uint8_t* +ped_disk_get_uuid (const PedDisk *disk) +{ + PED_ASSERT (disk != NULL); + + if (!_assert_disk_uuid_feature (disk->type)) + return NULL; + + PED_ASSERT (disk->type->ops->disk_get_uuid != NULL); + return disk->type->ops->disk_get_uuid (disk); +} + +/** + * \internal We turned a really nasty bureaucracy problem into an elegant maths + * problem :-) Basically, there are some constraints to a partition's + * geometry: + * + * (1) it must start and end on a "disk" block, determined by the disk label + * (not the hardware). (constraint represented by a PedAlignment) + * + * (2) if we're resizing a partition, we MIGHT need to keep each block aligned. + * Eg: if an ext2 file system has 4k blocks, then we can only move the start + * by a multiple of 4k. (constraint represented by a PedAlignment) + * + * (3) we need to keep the start and end within the device's physical + * boundaries. (constraint represented by a PedGeometry) + * + * Satisfying (1) and (2) simultaneously required a bit of fancy maths ;-) See + * ped_alignment_intersect() + * + * The application of these constraints is in disk_*.c's *_partition_align() + * function. + */ +static int +_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + const PedDiskType* disk_type; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->num != -1); + PED_ASSERT (part->disk != NULL); + disk_type = part->disk->type; + PED_ASSERT (disk_type != NULL); + PED_ASSERT (disk_type->ops->partition_align != NULL); + PED_ASSERT (part->disk->update_mode); + + if (part->disk->needs_clobber) + return 1; /* do not attempt to align partitions while reading them */ + return disk_type->ops->partition_align (part, constraint); +} + +static int +_partition_enumerate (PedPartition* part) +{ + const PedDiskType* disk_type; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + disk_type = part->disk->type; + PED_ASSERT (disk_type != NULL); + PED_ASSERT (disk_type->ops->partition_enumerate != NULL); + + return disk_type->ops->partition_enumerate (part); +} + +/** + * Gives all the (active) partitions a number. It should preserve the numbers + * and orders as much as possible. + */ +static int +ped_disk_enumerate_partitions (PedDisk* disk) +{ + PedPartition* walk; + int i; + int end; + + PED_ASSERT (disk != NULL); + +/* first "sort" already-numbered partitions. (e.g. if a logical partition + * is removed, then all logical partitions that were number higher MUST be + * renumbered) + */ + end = ped_disk_get_last_partition_num (disk); + for (i=1; i<=end; i++) { + walk = ped_disk_get_partition (disk, i); + if (walk) { + if (!_partition_enumerate (walk)) + return 0; + } + } + +/* now, number un-numbered partitions */ + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (ped_partition_is_active (walk) && walk->num == -1) { + if (!_partition_enumerate (walk)) + return 0; + } + } + + return 1; +} + +static int +_disk_remove_metadata (PedDisk* disk) +{ + PedPartition* walk = NULL; + PedPartition* next; + + PED_ASSERT (disk != NULL); + + next = ped_disk_next_partition (disk, walk); + + while (next) { + walk = next; + while (1) { + next = ped_disk_next_partition (disk, next); + if (!next || next->type & PED_PARTITION_METADATA) + break; + } + if (walk->type & PED_PARTITION_METADATA) + ped_disk_delete_partition (disk, walk); + } + return 1; +} + +static int +_disk_alloc_metadata (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + + if (!disk->update_mode) + _disk_remove_metadata (disk); + + return disk->type->ops->alloc_metadata (disk); +} + +static int +_disk_remove_freespace (PedDisk* disk) +{ + PedPartition* walk; + PedPartition* next; + + walk = ped_disk_next_partition (disk, NULL); + for (; walk; walk = next) { + next = ped_disk_next_partition (disk, walk); + + if (walk->type & PED_PARTITION_FREESPACE) { + _disk_raw_remove (disk, walk); + ped_partition_destroy (walk); + } + } + + return 1; +} + +static int +_alloc_extended_freespace (PedDisk* disk) +{ + PedSector last_end; + PedPartition* walk; + PedPartition* last; + PedPartition* free_space; + PedPartition* extended_part; + + extended_part = ped_disk_extended_partition (disk); + if (!extended_part) + return 1; + + last_end = extended_part->geom.start; + last = NULL; + + for (walk = extended_part->part_list; walk; walk = walk->next) { + if (walk->geom.start > last_end + 1) { + free_space = ped_partition_new ( + disk, + PED_PARTITION_FREESPACE + | PED_PARTITION_LOGICAL, + NULL, + last_end + 1, walk->geom.start - 1); + _disk_raw_insert_before (disk, walk, free_space); + } + + last = walk; + last_end = last->geom.end; + } + + if (last_end < extended_part->geom.end) { + free_space = ped_partition_new ( + disk, + PED_PARTITION_FREESPACE | PED_PARTITION_LOGICAL, + NULL, + last_end + 1, extended_part->geom.end); + + if (last) + return _disk_raw_insert_after (disk, last, free_space); + else + extended_part->part_list = free_space; + } + + return 1; +} + +static int +_disk_alloc_freespace (PedDisk* disk) +{ + PedSector last_end; + PedPartition* walk; + PedPartition* last; + PedPartition* free_space; + + if (!_disk_remove_freespace (disk)) + return 0; + if (!_alloc_extended_freespace (disk)) + return 0; + + last = NULL; + last_end = -1; + + for (walk = disk->part_list; walk; walk = walk->next) { + if (walk->geom.start > last_end + 1) { + free_space = ped_partition_new (disk, + PED_PARTITION_FREESPACE, NULL, + last_end + 1, walk->geom.start - 1); + _disk_raw_insert_before (disk, walk, free_space); + } + + last = walk; + last_end = last->geom.end; + } + + if (last_end < disk->dev->length - 1) { + free_space = ped_partition_new (disk, + PED_PARTITION_FREESPACE, NULL, + last_end + 1, disk->dev->length - 1); + if (last) + return _disk_raw_insert_after (disk, last, free_space); + else + disk->part_list = free_space; + } + + return 1; +} + +/** + * Update mode: used when updating the internal representation of the partition + * table. In update mode, the metadata and freespace placeholder/virtual + * partitions are removed, making it much easier for various manipulation + * routines... + */ +static int +_disk_push_update_mode (PedDisk* disk) +{ + if (!disk->update_mode) { +#ifdef DEBUG + if (!_disk_check_sanity (disk)) + return 0; +#endif + + _disk_remove_freespace (disk); + disk->update_mode++; + _disk_remove_metadata (disk); + +#ifdef DEBUG + if (!_disk_check_sanity (disk)) + return 0; +#endif + } else { + disk->update_mode++; + } + return 1; +} + +static int +_disk_pop_update_mode (PedDisk* disk) +{ + PED_ASSERT (disk->update_mode); + + if (disk->update_mode == 1) { + /* re-allocate metadata BEFORE leaving update mode, to prevent infinite + * recursion (metadata allocation requires update mode) + */ +#ifdef DEBUG + if (!_disk_check_sanity (disk)) + return 0; +#endif + + _disk_alloc_metadata (disk); + disk->update_mode--; + _disk_alloc_freespace (disk); + +#ifdef DEBUG + if (!_disk_check_sanity (disk)) + return 0; +#endif + } else { + disk->update_mode--; + } + return 1; +} + +/** @} */ + +/** + * \addtogroup PedPartition + * + * \brief Partition access. + * + * @{ + */ + +PedPartition* +_ped_partition_alloc (const PedDisk* disk, PedPartitionType type, + const PedFileSystemType* fs_type, + PedSector start, PedSector end) +{ + PedPartition* part; + + PED_ASSERT (disk != NULL); + + part = (PedPartition*) ped_malloc (sizeof (PedPartition)); + if (!part) + goto error; + + part->prev = NULL; + part->next = NULL; + + part->disk = (PedDisk*) disk; + if (!ped_geometry_init (&part->geom, disk->dev, start, end - start + 1)) + goto error_free_part; + + part->num = -1; + part->type = type; + part->part_list = NULL; + part->fs_type = fs_type; + + return part; + +error_free_part: + free (part); +error: + return NULL; +} + +void +_ped_partition_free (PedPartition* part) +{ + free (part); +} + +int +_ped_partition_attempt_align (PedPartition* part, + const PedConstraint* external, + PedConstraint* internal) +{ + PedConstraint* intersection; + PedGeometry* solution; + + intersection = ped_constraint_intersect (external, internal); + ped_constraint_destroy (internal); + if (!intersection) + goto fail; + + solution = ped_constraint_solve_nearest (intersection, &part->geom); + if (!solution) + goto fail_free_intersection; + ped_geometry_set (&part->geom, solution->start, solution->length); + ped_geometry_destroy (solution); + ped_constraint_destroy (intersection); + return 1; + +fail_free_intersection: + ped_constraint_destroy (intersection); +fail: + return 0; +} + +/** + * Create a new \link _PedPartition PedPartition \endlink on \p disk. + * + * \param type One of \p PED_PARTITION_NORMAL, \p PED_PARTITION_EXTENDED, + * \p PED_PARTITION_LOGICAL. + * + * \note The constructed partition is not added to <tt>disk</tt>'s + * partition table. Use ped_disk_add_partition() to do this. + * + * \return A new \link _PedPartition PedPartition \endlink object, + * NULL on failure. + * + * \throws PED_EXCEPTION_ERROR if \p type is \p EXTENDED or \p LOGICAL but the + * label does not support this concept. + */ +PedPartition* +ped_partition_new (const PedDisk* disk, PedPartitionType type, + const PedFileSystemType* fs_type, PedSector start, + PedSector end) +{ + int supports_extended; + PedPartition* part; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->type->ops->partition_new != NULL); + + supports_extended = ped_disk_type_check_feature (disk->type, + PED_DISK_TYPE_EXTENDED); + + if (!supports_extended + && (type == PED_PARTITION_EXTENDED + || type == PED_PARTITION_LOGICAL)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s disk labels do not support extended " + "partitions."), + disk->type->name); + goto error; + } + + part = disk->type->ops->partition_new (disk, type, fs_type, start, end); + if (!part) + goto error; + + if (fs_type || part->type == PED_PARTITION_EXTENDED) { + if (!ped_partition_set_system (part, fs_type)) + goto error_destroy_part; + } + return part; + +error_destroy_part: + ped_partition_destroy (part); +error: + return NULL; +} + +/** + * Destroy a \link _PedPartition PedPartition \endlink object. + * + * \note Should not be called on a partition that is in a partition table. + * Use ped_disk_delete_partition() instead. + */ +void +ped_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->type->ops->partition_new != NULL); + + part->disk->type->ops->partition_destroy (part); +} + + +/** + * Return whether or not the partition is "active". + * + * A partition is active if \p part->type is neither \p PED_PARTITION_METADATA + * nor \p PED_PARTITION_FREE. + */ +int +ped_partition_is_active (const PedPartition* part) +{ + PED_ASSERT (part != NULL); + + return !(part->type & PED_PARTITION_FREESPACE + || part->type & PED_PARTITION_METADATA); +} + +/** + * Set the state (\c 1 or \c 0) of a flag on a partition. + * + * Flags are disk label specific, although they have a global + * "namespace": the flag PED_PARTITION_BOOT, for example, roughly means + * "this" partition is bootable". But this means different things on different + * disk labels (and may not be defined on some disk labels). For example, + * on MS-DOS disk labels, there can only be one boot partition, and this + * refers to the partition that will be booted from on startup. On PC98 + * disk labels, the user can choose from any bootable partition on startup. + * + * \note It is an error to call this on an unavailable flag -- use + * ped_partition_is_flag_available() to determine which flags are available + * for a given disk label. + * + * \throws PED_EXCEPTION_ERROR if the requested flag is not available for this + * label. + */ +int +ped_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + PedDiskOps* ops; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (ped_partition_is_active (part)); + + ops = part->disk->type->ops; + PED_ASSERT (ops->partition_set_flag != NULL); + PED_ASSERT (ops->partition_is_flag_available != NULL); + + if (!ops->partition_is_flag_available (part, flag)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "The flag '%s' is not available for %s disk labels.", + ped_partition_flag_get_name (flag), + part->disk->type->name); + return 0; + } + + return ops->partition_set_flag (part, flag, state); +} + +/** + * Get the state (\c 1 or \c 0) of a flag on a partition. + * + * See ped_partition_set_flag() for conditions that must hold. + * + * \todo Where's the check for flag availability? + */ +int +ped_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->type->ops->partition_get_flag != NULL); + PED_ASSERT (ped_partition_is_active (part)); + + return part->disk->type->ops->partition_get_flag (part, flag); +} + +/** + * Check whether a given flag is available on a partition. + * + * \return \c 1 if the flag is available. + */ +int +ped_partition_is_flag_available (const PedPartition* part, + PedPartitionFlag flag) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (part->disk->type->ops->partition_is_flag_available != NULL); + PED_ASSERT (ped_partition_is_active (part)); + + return part->disk->type->ops->partition_is_flag_available (part, flag); +} + +/** + * Sets the system type on the partition to \p fs_type. + * + * \note The file system may be opened, to get more information about the + * file system, e.g. to determine if it's FAT16 or FAT32. + * + * \return \c 0 on failure. + */ +int +ped_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + const PedDiskType* disk_type; + + PED_ASSERT (part != NULL); + PED_ASSERT (ped_partition_is_active (part)); + PED_ASSERT (part->disk != NULL); + disk_type = part->disk->type; + PED_ASSERT (disk_type != NULL); + PED_ASSERT (disk_type->ops != NULL); + PED_ASSERT (disk_type->ops->partition_set_system != NULL); + + return disk_type->ops->partition_set_system (part, fs_type); +} + +static int +_assert_partition_name_feature (const PedDiskType* disk_type) +{ + if (!ped_disk_type_check_feature ( + disk_type, PED_DISK_TYPE_PARTITION_NAME)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "%s disk labels do not support partition names.", + disk_type->name); + return 0; + } + return 1; +} + +static int +_assert_partition_type_id_feature (const PedDiskType* disk_type) +{ + if (!ped_disk_type_check_feature ( + disk_type, PED_DISK_TYPE_PARTITION_TYPE_ID)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "%s disk labels do not support partition type-ids.", + disk_type->name); + return 0; + } + return 1; +} + +static int +_assert_partition_type_uuid_feature (const PedDiskType* disk_type) +{ + if (!ped_disk_type_check_feature ( + disk_type, PED_DISK_TYPE_PARTITION_TYPE_UUID)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "%s disk labels do not support partition type-uuids.", + disk_type->name); + return 0; + } + return 1; +} + +static int +_assert_partition_uuid_feature (const PedDiskType* disk_type) +{ + if (!ped_disk_type_check_feature ( + disk_type, PED_DISK_TYPE_PARTITION_UUID)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "%s disk labels do not support partition uuids.", + disk_type->name); + return 0; + } + return 1; +} + +/** + * Sets the name of a partition. + * + * \note This will only work if the disk label supports it. + * You can use + * \code + * ped_disk_type_check_feature (part->disk->type, PED_DISK_TYPE_PARTITION_NAME); + * \endcode + * to check whether this feature is enabled for a label. + * + * \note \p name will not be modified by libparted. It can be freed + * by the caller immediately after ped_partition_set_name() is called. + * + * \return \c 1 on success, \c 0 otherwise. + */ +int +ped_partition_set_name (PedPartition* part, const char* name) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (ped_partition_is_active (part)); + PED_ASSERT (name != NULL); + + if (!_assert_partition_name_feature (part->disk->type)) + return 0; + + PED_ASSERT (part->disk->type->ops->partition_set_name != NULL); + part->disk->type->ops->partition_set_name (part, name); + return 1; +} + +/** + * Returns the name of a partition \p part. This will only work if the disk + * label supports it. + * + * \note The returned string should not be modified. It should + * not be referenced after the partition is destroyed. + */ +const char* +ped_partition_get_name (const PedPartition* part) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (ped_partition_is_active (part)); + + if (!_assert_partition_name_feature (part->disk->type)) + return NULL; + + PED_ASSERT (part->disk->type->ops->partition_get_name != NULL); + return part->disk->type->ops->partition_get_name (part); +} + +/** + * Set the type-id of the partition \p part. This will only work if the disk label + * supports it. + */ +int +ped_partition_set_type_id (PedPartition *part, uint8_t id) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (ped_partition_is_active (part)); + + if (!_assert_partition_type_id_feature (part->disk->type)) + return 0; + + PED_ASSERT (part->disk->type->ops->partition_set_type_id != NULL); + return part->disk->type->ops->partition_set_type_id (part, id); +} + +/** + * Get the type-id of the partition \p part. This will only work if the disk label + * supports it. + */ +uint8_t +ped_partition_get_type_id (const PedPartition *part) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (ped_partition_is_active (part)); + + if (!_assert_partition_type_id_feature (part->disk->type)) + return 0; + + PED_ASSERT (part->disk->type->ops->partition_get_type_id != NULL); + return part->disk->type->ops->partition_get_type_id (part); +} + +/** + * Set the type-uuid of the partition \p part. This will only work if the disk label + * supports it. + */ +int +ped_partition_set_type_uuid (PedPartition *part, const uint8_t* uuid) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (ped_partition_is_active (part)); + + if (!_assert_partition_type_uuid_feature (part->disk->type)) + return 0; + + PED_ASSERT (part->disk->type->ops->partition_set_type_uuid != NULL); + return part->disk->type->ops->partition_set_type_uuid (part, uuid); +} + +/** + * Get the type-uuid of the partition \p part. This will only work if the disk label + * supports it. + */ +uint8_t* +ped_partition_get_type_uuid (const PedPartition *part) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (ped_partition_is_active (part)); + + if (!_assert_partition_type_uuid_feature (part->disk->type)) + return NULL; + + PED_ASSERT (part->disk->type->ops->partition_get_type_uuid != NULL); + return part->disk->type->ops->partition_get_type_uuid (part); +} + +/** + * Get the uuid of the partition \p part. This will only work if the disk label + * supports it. + */ +uint8_t* +ped_partition_get_uuid (const PedPartition *part) +{ + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + PED_ASSERT (ped_partition_is_active (part)); + + if (!_assert_partition_uuid_feature (part->disk->type)) + return NULL; + + PED_ASSERT (part->disk->type->ops->partition_get_uuid != NULL); + return part->disk->type->ops->partition_get_uuid (part); +} + +/** @} */ + +/** + * \addtogroup PedDisk + * + * @{ + */ + +PedPartition* +ped_disk_extended_partition (const PedDisk* disk) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL); + + for (walk = disk->part_list; walk; walk = walk->next) { + if (walk->type == PED_PARTITION_EXTENDED) + break; + } + return walk; +} + +/** + * Return the next partition after \p part on \p disk. If \p part is \c NULL, + * return the first partition. If \p part is the last partition, returns + * \c NULL. If \p part is an extended partition, returns the first logical + * partition. If this is called repeatedly passing the return value as \p part, + * a depth-first traversal is executed. + * + * \return The next partition, \c NULL if no more partitions left. + */ +PedPartition* +ped_disk_next_partition (const PedDisk* disk, const PedPartition* part) +{ + PED_ASSERT (disk != NULL); + + if (!part) + return disk->part_list; + if (part->type == PED_PARTITION_EXTENDED) + return part->part_list ? part->part_list : part->next; + if (part->next) + return part->next; + if (part->type & PED_PARTITION_LOGICAL) { + if (!ped_disk_extended_partition (disk)) + return NULL; + return ped_disk_extended_partition (disk)->next; + } + return NULL; +} + +/** @} */ + +#ifdef DEBUG +static int _GL_ATTRIBUTE_PURE +_disk_check_sanity (PedDisk* disk) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL); + + for (walk = disk->part_list; walk; walk = walk->next) { + PED_ASSERT (!(walk->type & PED_PARTITION_LOGICAL)); + PED_ASSERT (!walk->prev || walk->prev->next == walk); + } + + if (!ped_disk_extended_partition (disk)) + return 1; + + for (walk = ped_disk_extended_partition (disk)->part_list; walk; + walk = walk->next) { + PED_ASSERT (walk->type & PED_PARTITION_LOGICAL); + if (walk->prev) + PED_ASSERT (walk->prev->next == walk); + } + return 1; +} +#endif + +/** + * Returns the partition numbered \p num. + * + * \return \c NULL if the specified partition does not exist. + */ +PedPartition* +ped_disk_get_partition (const PedDisk* disk, int num) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL); + + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (walk->num == num && !(walk->type & PED_PARTITION_FREESPACE)) + return walk; + } + + return NULL; +} + +/** + * Returns the partition that contains sect. If sect lies within a logical + * partition, then the logical partition is returned (not the extended + * partition). + */ +PedPartition* +ped_disk_get_partition_by_sector (const PedDisk* disk, PedSector sect) +{ + PedPartition* walk; + + PED_ASSERT (disk != NULL); + + for (walk = disk->part_list; walk; + walk = ped_disk_next_partition (disk, walk)) { + if (ped_geometry_test_sector_inside (&walk->geom, sect) + && walk->type != PED_PARTITION_EXTENDED) + return walk; + } + + /* should never get here, unless sect is outside of disk's useable + * part, or we're in "update mode", and the free space place-holders + * have been removed with _disk_remove_freespace() + */ + return NULL; +} + +/** + * Return the maximum representable length (in sectors) of a + * partition on disk \disk. + */ +PedSector +ped_disk_max_partition_length (const PedDisk* disk) +{ + return disk->type->ops->max_length (); +} + +/** + * Return the maximum representable start sector of a + * partition on disk \disk. + */ +PedSector +ped_disk_max_partition_start_sector (const PedDisk* disk) +{ + return disk->type->ops->max_start_sector (); +} + +/* I'm beginning to agree with Sedgewick :-/ */ +static int +_disk_raw_insert_before (PedDisk* disk, PedPartition* loc, PedPartition* part) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (loc != NULL); + PED_ASSERT (part != NULL); + + part->prev = loc->prev; + part->next = loc; + if (part->prev) { + part->prev->next = part; + } else { + if (loc->type & PED_PARTITION_LOGICAL) + ped_disk_extended_partition (disk)->part_list = part; + else + disk->part_list = part; + } + loc->prev = part; + + return 1; +} + +static int +_disk_raw_insert_after (PedDisk* disk, PedPartition* loc, PedPartition* part) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (loc != NULL); + PED_ASSERT (part != NULL); + + part->prev = loc; + part->next = loc->next; + if (loc->next) + loc->next->prev = part; + loc->next = part; + + return 1; +} + +static int +_disk_raw_remove (PedDisk* disk, PedPartition* part) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (part != NULL); + + if (part->prev) { + part->prev->next = part->next; + if (part->next) + part->next->prev = part->prev; + } else { + if (part->type & PED_PARTITION_LOGICAL) { + ped_disk_extended_partition (disk)->part_list + = part->next; + } else { + disk->part_list = part->next; + } + if (part->next) + part->next->prev = NULL; + } + + return 1; +} + +/* + *UPDATE MODE ONLY + */ +static int +_disk_raw_add (PedDisk* disk, PedPartition* part) +{ + PedPartition* walk; + PedPartition* last; + PedPartition* ext_part; + + PED_ASSERT (disk->update_mode); + + ext_part = ped_disk_extended_partition (disk); + + last = NULL; + walk = (part->type & PED_PARTITION_LOGICAL) ? + ext_part->part_list : disk->part_list; + + for (; walk; last = walk, walk = walk->next) { + if (walk->geom.start > part->geom.end) + break; + } + + if (walk) { + return _disk_raw_insert_before (disk, walk, part); + } else { + if (last) { + return _disk_raw_insert_after (disk, last, part); + } else { + if (part->type & PED_PARTITION_LOGICAL) + ext_part->part_list = part; + else + disk->part_list = part; + } + } + + return 1; +} + +static PedConstraint* +_partition_get_overlap_constraint (PedPartition* part, PedGeometry* geom) +{ + PedSector min_start; + PedSector max_end; + PedPartition* walk; + PedGeometry free_space; + + PED_ASSERT (part->disk->update_mode); + PED_ASSERT (part->geom.dev == geom->dev); + + if (part->type & PED_PARTITION_LOGICAL) { + PedPartition* ext_part; + + ext_part = ped_disk_extended_partition (part->disk); + PED_ASSERT (ext_part != NULL); + + min_start = ext_part->geom.start; + max_end = ext_part->geom.end; + walk = ext_part->part_list; + } else { + min_start = 0; + max_end = LLONG_MAX - 1; + walk = part->disk->part_list; + } + + while (walk != NULL + && (walk->geom.start < geom->start + || min_start >= walk->geom.start)) { + if (walk != part) + min_start = walk->geom.end + 1; + walk = walk->next; + } + + if (walk == part) + walk = walk->next; + + if (walk) + max_end = walk->geom.start - 1; + + if (min_start > max_end) + return NULL; + + ped_geometry_init (&free_space, part->disk->dev, + min_start, max_end - min_start + 1); + return ped_constraint_new_from_max (&free_space); +} + +static int +_partition_check_basic_sanity (PedDisk* disk, PedPartition* part) +{ + PedPartition* ext_part = ped_disk_extended_partition (disk); + + PED_ASSERT (part->disk == disk); + + PED_ASSERT (part->geom.start >= 0); + PED_ASSERT (part->geom.start <= part->geom.end); + + if (!ped_disk_type_check_feature (disk->type, PED_DISK_TYPE_EXTENDED) + && (part->type == PED_PARTITION_EXTENDED + || part->type == PED_PARTITION_LOGICAL)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s disk labels don't support logical or extended " + "partitions."), + disk->type->name); + return 0; + } + + if (ped_partition_is_active (part) + && ! (part->type & PED_PARTITION_LOGICAL)) { + if (ped_disk_get_primary_partition_count (disk) + 1 + > ped_disk_get_max_primary_partition_count (disk)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Too many primary partitions.")); + return 0; + } + } + + if ((part->type & PED_PARTITION_LOGICAL) && !ext_part) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't add a logical partition to %s, because " + "there is no extended partition."), + disk->dev->path); + return 0; + } + + return 1; +} + +static int +_check_extended_partition (PedDisk* disk, PedPartition* part) +{ + PedPartition* walk; + PedPartition* ext_part; + + PED_ASSERT (disk != NULL); + ext_part = ped_disk_extended_partition (disk); + if (!ext_part) ext_part = part; + PED_ASSERT (ext_part != NULL); + + if (part != ext_part) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have more than one extended partition on %s."), + disk->dev->path); + return 0; + } + + for (walk = ext_part->part_list; walk; walk = walk->next) { + if (!ped_geometry_test_inside (&ext_part->geom, &walk->geom)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have logical partitions outside of " + "the extended partition.")); + return 0; + } + } + return 1; +} + +static int +_check_partition (PedDisk* disk, PedPartition* part) +{ + PedPartition* ext_part = ped_disk_extended_partition (disk); + + PED_ASSERT (part->geom.start <= part->geom.end); + + if (part->type == PED_PARTITION_EXTENDED) { + if (!_check_extended_partition (disk, part)) + return 0; + } + + if (part->type & PED_PARTITION_LOGICAL + && !ped_geometry_test_inside (&ext_part->geom, &part->geom)) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Can't have a logical partition outside of the " + "extended partition on %s."), + disk->dev->path) != PED_EXCEPTION_IGNORE) + return 0; + } + + if (! (part->type & PED_PARTITION_LOGICAL) + && ext_part && ext_part != part + && ped_geometry_test_inside (&ext_part->geom, &part->geom)) { + if (ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL, + _("Can't have a primary partition inside an extended " + "partition.")) != PED_EXCEPTION_IGNORE) + return 0; + } + + if (part->geom.end >= disk->dev->length) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Can't have a partition outside the disk!")) + != PED_EXCEPTION_IGNORE ) + return 0; + } + + if (!(part->type & PED_PARTITION_METADATA)) + if (!disk->type->ops->partition_check(part)) + return 0; + + return 1; +} + +/** + * Adds PedPartition \p part to PedDisk \p disk. + * + * \warning The partition's geometry may be changed, subject to \p constraint. + * You could set \p constraint to <tt>ped_constraint_exact(&part->geom)</tt>, + * but many partition table schemes have special requirements on the start + * and end of partitions. Therefore, having an overly strict constraint + * will probably mean that this function will fail (in which + * case \p part will be left unmodified) + * \p part is assigned a number (\p part->num) in this process. + * + * \return \c 0 on failure. + */ +int +ped_disk_add_partition (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint) +{ + PedConstraint* overlap_constraint = NULL; + PedConstraint* constraints = NULL; + + PED_ASSERT (disk != NULL); + PED_ASSERT (part != NULL); + + if (!_partition_check_basic_sanity (disk, part)) + return 0; + + if (!_disk_push_update_mode (disk)) + return 0; + + if (ped_partition_is_active (part)) { + overlap_constraint + = _partition_get_overlap_constraint (part, &part->geom); + constraints = ped_constraint_intersect (overlap_constraint, + constraint); + + if (!constraints && constraint) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Can't have overlapping partitions.")) != PED_EXCEPTION_IGNORE) + goto error; + } else constraint = constraints; + if (!_partition_enumerate (part)) + goto error; + if (!_partition_align (part, constraint)) + goto error; + } + /* FIXME: when _check_partition fails, we end up leaking PART + at least for DVH partition tables. Simply calling + ped_partition_destroy(part) here fixes it for DVH, but + causes trouble for other partition types. Similarly, + reordering these two checks, putting _check_partition after + _disk_raw_add induces an infinite loop. */ + if (!_check_partition (disk, part)) + goto error; + if (!_disk_raw_add (disk, part)) + goto error; + + ped_constraint_destroy (overlap_constraint); + ped_constraint_destroy (constraints); + if (!_disk_pop_update_mode (disk)) + return 0; +#ifdef DEBUG + if (!_disk_check_sanity (disk)) + return 0; +#endif + return 1; + +error: + ped_constraint_destroy (overlap_constraint); + ped_constraint_destroy (constraints); + _disk_pop_update_mode (disk); + return 0; +} + +/** + * Removes PedPartition \p part from PedDisk \p disk. + * + * If \p part is an extended partition, it must not contain any logical + * partitions. \p part is *NOT* destroyed. The caller must call + * ped_partition_destroy(), or use ped_disk_delete_partition() instead. + * + * \return \c 0 on error. + */ +int +ped_disk_remove_partition (PedDisk* disk, PedPartition* part) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (part != NULL); + + if (!_disk_push_update_mode (disk)) + return 0; + PED_ASSERT (part->part_list == NULL); + _disk_raw_remove (disk, part); + if (!_disk_pop_update_mode (disk)) + return 0; + ped_disk_enumerate_partitions (disk); + return 1; +} + +static int +ped_disk_delete_all_logical (PedDisk* disk); + +/** + * Removes \p part from \p disk, and destroys \p part. + * + * \return \c 0 on failure. + */ +int +ped_disk_delete_partition (PedDisk* disk, PedPartition* part) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (part != NULL); + + if (!_disk_push_update_mode (disk)) + return 0; + if (part->type == PED_PARTITION_EXTENDED) + ped_disk_delete_all_logical (disk); + ped_disk_remove_partition (disk, part); + ped_partition_destroy (part); + if (!_disk_pop_update_mode (disk)) + return 0; + + return 1; +} + +static int +ped_disk_delete_all_logical (PedDisk* disk) +{ + PedPartition* walk; + PedPartition* next; + PedPartition* ext_part; + + PED_ASSERT (disk != NULL); + ext_part = ped_disk_extended_partition (disk); + PED_ASSERT (ext_part != NULL); + + for (walk = ext_part->part_list; walk; walk = next) { + next = walk->next; + + if (!ped_disk_delete_partition (disk, walk)) + return 0; + } + return 1; +} + +/** + * Removes and destroys all partitions on \p disk. + * + * \return \c 0 on failure. + */ +int +ped_disk_delete_all (PedDisk* disk) +{ + PedPartition* walk; + PedPartition* next; + + PED_ASSERT (disk != NULL); + + if (!_disk_push_update_mode (disk)) + return 0; + + for (walk = disk->part_list; walk; walk = next) { + next = walk->next; + + if (!ped_disk_delete_partition (disk, walk)) { + _disk_pop_update_mode(disk); + return 0; + } + } + + if (!_disk_pop_update_mode (disk)) + return 0; + + return 1; +} + +/** + * Sets the geometry of \p part (i.e. change a partitions location). This can + * fail for many reasons, e.g. can't overlap with other partitions. If it + * does fail, \p part will remain unchanged. Returns \c 0 on failure. \p part's + * geometry may be set to something different from \p start and \p end subject + * to \p constraint. + * + * \warning The constraint warning from ped_disk_add_partition() applies. + * + * \note this function does not modify the contents of the partition. You need + * to call ped_file_system_resize() separately. + */ +int +ped_disk_set_partition_geom (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint, + PedSector start, PedSector end) +{ + PedConstraint* overlap_constraint = NULL; + PedConstraint* constraints = NULL; + PedGeometry old_geom; + PedGeometry new_geom; + + PED_ASSERT (disk != NULL); + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk == disk); + + old_geom = part->geom; + if (!ped_geometry_init (&new_geom, part->geom.dev, start, end - start + 1)) + return 0; + + if (!_disk_push_update_mode (disk)) + return 0; + + overlap_constraint + = _partition_get_overlap_constraint (part, &new_geom); + constraints = ped_constraint_intersect (overlap_constraint, constraint); + if (!constraints && constraint) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't have overlapping partitions.")); + goto error_pop_update_mode; + } + + part->geom = new_geom; + if (!_partition_align (part, constraints)) + goto error_pop_update_mode; + if (!_check_partition (disk, part)) + goto error_pop_update_mode; + + /* remove and add, to ensure the ordering gets updated if necessary */ + _disk_raw_remove (disk, part); + _disk_raw_add (disk, part); + + if (!_disk_pop_update_mode (disk)) + goto error; + + ped_constraint_destroy (overlap_constraint); + ped_constraint_destroy (constraints); + return 1; + +error_pop_update_mode: + _disk_pop_update_mode (disk); +error: + ped_constraint_destroy (overlap_constraint); + ped_constraint_destroy (constraints); + part->geom = old_geom; + return 0; +} + +/** + * Grow PedPartition \p part geometry to the maximum possible subject to + * \p constraint. The new geometry will be a superset of the old geometry. + * + * \return 0 on failure + */ +int +ped_disk_maximize_partition (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint) +{ + PedGeometry old_geom; + PedSector global_min_start; + PedSector global_max_end; + PedSector new_start; + PedSector new_end; + PedPartition* ext_part = ped_disk_extended_partition (disk); + PedConstraint* constraint_any; + + PED_ASSERT (disk != NULL); + PED_ASSERT (part != NULL); + + if (part->type & PED_PARTITION_LOGICAL) { + PED_ASSERT (ext_part != NULL); + global_min_start = ext_part->geom.start; + global_max_end = ext_part->geom.end; + } else { + global_min_start = 0; + global_max_end = disk->dev->length - 1; + } + + old_geom = part->geom; + + if (!_disk_push_update_mode (disk)) + return 0; + + if (part->prev) + new_start = part->prev->geom.end + 1; + else + new_start = global_min_start; + + if (part->next) + new_end = part->next->geom.start - 1; + else + new_end = global_max_end; + + if (!ped_disk_set_partition_geom (disk, part, constraint, new_start, + new_end)) + goto error; + + if (!_disk_pop_update_mode (disk)) + return 0; + return 1; + +error: + constraint_any = ped_constraint_any (disk->dev); + ped_disk_set_partition_geom (disk, part, constraint_any, + old_geom.start, old_geom.end); + ped_constraint_destroy (constraint_any); + _disk_pop_update_mode (disk); + return 0; +} + +/** + * Get the maximum geometry \p part can be grown to, subject to + * \p constraint. + * + * \return \c NULL on failure. + */ +PedGeometry* +ped_disk_get_max_partition_geometry (PedDisk* disk, PedPartition* part, + const PedConstraint* constraint) +{ + PedGeometry old_geom; + PedGeometry* max_geom; + PedConstraint* constraint_exact; + + PED_ASSERT(disk != NULL); + PED_ASSERT(part != NULL); + PED_ASSERT(ped_partition_is_active (part)); + + old_geom = part->geom; + if (!ped_disk_maximize_partition (disk, part, constraint)) + return NULL; + max_geom = ped_geometry_duplicate (&part->geom); + + constraint_exact = ped_constraint_exact (&old_geom); + ped_disk_set_partition_geom (disk, part, constraint_exact, + old_geom.start, old_geom.end); + ped_constraint_destroy (constraint_exact); + + /* this assertion should never fail, because the old + * geometry was valid + */ + PED_ASSERT (ped_geometry_test_equal (&part->geom, &old_geom)); + + return max_geom; +} + +/** + * Reduce the size of the extended partition to a minimum while still wrapping + * its logical partitions. If there are no logical partitions, remove the + * extended partition. + * + * \return 0 on failure. + */ +int +ped_disk_minimize_extended_partition (PedDisk* disk) +{ + PedPartition* first_logical; + PedPartition* last_logical; + PedPartition* walk; + PedPartition* ext_part; + PedConstraint* constraint; + int status; + + PED_ASSERT (disk != NULL); + + ext_part = ped_disk_extended_partition (disk); + if (!ext_part) + return 1; + + if (!_disk_push_update_mode (disk)) + return 0; + + first_logical = ext_part->part_list; + if (!first_logical) { + if (!_disk_pop_update_mode (disk)) + return 0; + return ped_disk_delete_partition (disk, ext_part); + } + + for (walk = first_logical; walk->next; walk = walk->next); + last_logical = walk; + + constraint = ped_constraint_any (disk->dev); + status = ped_disk_set_partition_geom (disk, ext_part, constraint, + first_logical->geom.start, + last_logical->geom.end); + ped_constraint_destroy (constraint); + + if (!_disk_pop_update_mode (disk)) + return 0; + return status; +} + +/** + * @} + */ + +/** + * \addtogroup PedPartition + * + * @{ + */ + +/** + * Returns a name that seems mildly appropriate for a partition type \p type. + * + * Eg, if you pass (PED_PARTITION_LOGICAL & PED_PARTITION_FREESPACE), it + * will return "free". This isn't to be taken too seriously - it's just + * useful for user interfaces, so you can show the user something ;-) + * + * \note The returned string will be in English. However, + * translations are provided, so the caller can call + * dgettext("parted", RESULT) on the result. + * + */ +const char* +ped_partition_type_get_name (PedPartitionType type) +{ + if (type & PED_PARTITION_METADATA) + return N_("metadata"); + else if (type & PED_PARTITION_FREESPACE) + return N_("free"); + else if (type & PED_PARTITION_EXTENDED) + return N_("extended"); + else if (type & PED_PARTITION_LOGICAL) + return N_("logical"); + else + return N_("primary"); +} + + +/** + * Returns a name for a \p flag, e.g. PED_PARTITION_BOOT will return "boot". + * + * \note The returned string will be in English. However, + * translations are provided, so the caller can call + * dgettext("parted", RESULT) on the result. + */ +const char* +ped_partition_flag_get_name (PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_BOOT: + return N_("boot"); + case PED_PARTITION_BIOS_GRUB: + return N_("bios_grub"); + case PED_PARTITION_ROOT: + return N_("root"); + case PED_PARTITION_SWAP: + return N_("swap"); + case PED_PARTITION_HIDDEN: + return N_("hidden"); + case PED_PARTITION_RAID: + return N_("raid"); + case PED_PARTITION_LVM: + return N_("lvm"); + case PED_PARTITION_LBA: + return N_("lba"); + case PED_PARTITION_HPSERVICE: + return N_("hp-service"); + case PED_PARTITION_PALO: + return N_("palo"); + case PED_PARTITION_PREP: + return N_("prep"); + case PED_PARTITION_MSFT_RESERVED: + return N_("msftres"); + case PED_PARTITION_MSFT_DATA: + return N_("msftdata"); + case PED_PARTITION_APPLE_TV_RECOVERY: + return N_("atvrecv"); + case PED_PARTITION_DIAG: + return N_("diag"); + case PED_PARTITION_LEGACY_BOOT: + return N_("legacy_boot"); + case PED_PARTITION_IRST: + return N_("irst"); + case PED_PARTITION_ESP: + return N_("esp"); + case PED_PARTITION_CHROMEOS_KERNEL: + return N_("chromeos_kernel"); + case PED_PARTITION_BLS_BOOT: + return N_("bls_boot"); + case PED_PARTITION_LINUX_HOME: + return N_("linux-home"); + case PED_PARTITION_NO_AUTOMOUNT: + return N_("no_automount"); + + default: + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Unknown partition flag, %d."), + flag); + return NULL; + } +} + +/** + * Iterates through all flags. + * + * ped_partition_flag_next(0) returns the first flag + * + * \return the next flag, or 0 if there are no more flags + */ +PedPartitionFlag +ped_partition_flag_next (PedPartitionFlag flag) +{ + return (flag + 1) % (PED_PARTITION_LAST_FLAG + 1); +} + +/** + * Returns the flag associated with \p name. + * + * \p name can be the English + * string, or the translation for the native language. + */ +PedPartitionFlag +ped_partition_flag_get_by_name (const char* name) +{ + PedPartitionFlag flag; + const char* flag_name; + + for (flag = ped_partition_flag_next (0); flag; + flag = ped_partition_flag_next (flag)) { + flag_name = ped_partition_flag_get_name (flag); + if (flag_name && (strcasecmp (name, flag_name) == 0 + || strcasecmp (name, _(flag_name)) == 0)) + return flag; + } + + return 0; +} + +static void +ped_partition_print (const PedPartition* part) +{ + PED_ASSERT (part != NULL); + + printf (" %-10s %02d (%d->%d)\n", + ped_partition_type_get_name (part->type), + part->num, + (int) part->geom.start, (int) part->geom.end); +} + +/** @} */ + +/** + * \addtogroup PedDisk + * + * @{ + */ + +/** + * Prints a summary of disk's partitions. Useful for debugging. + */ +void +ped_disk_print (const PedDisk* disk) +{ + PedPartition* part; + + PED_ASSERT (disk != NULL); + + for (part = disk->part_list; part; + part = ped_disk_next_partition (disk, part)) + ped_partition_print (part); +} + +/** @} */ diff --git a/libparted/exception.c b/libparted/exception.c new file mode 100644 index 0000000..1d95853 --- /dev/null +++ b/libparted/exception.c @@ -0,0 +1,313 @@ +/* + 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/>. +*/ + +/** \file exception.c */ + +/** + * \addtogroup PedException + * + * \brief Exception handling. + * + * There are a few types of exceptions: PED_EXCEPTION_INFORMATION, + * PED_EXCEPTION_WARNING, PED_EXCEPTION_ERROR, PED_EXCEPTION_FATAL, + * PED_EXCEPTION_BUG. + * + * They are "thrown" when one of the above events occur while executing + * a libparted function. For example, if ped_device_open() fails + * because the device doesn't exist, an exception will be thrown. + * Exceptions contain text describing what the event was. It will give + * at least one option for resolving the exception: PED_EXCEPTION_FIX, + * PED_EXCEPTION_YES, PED_EXCEPTION_NO, PED_EXCEPTION_OK, PED_EXCEPTION_RETRY, + * PED_EXCEPTION_IGNORE, PED_EXCEPTION_CANCEL. After an exception is thrown, + * there are two things that can happen: + * + * -# an exception handler is called, which selects how the exception should be + * resolved (usually by asking the user). Also note: an exception handler may + * choose to return PED_EXCEPTION_UNHANDLED. In this case, a default action + * will be taken by libparted (respectively the code that threw the + * exception). In general, a default action will be "safe". + * -# the exception is not handled, because the caller of the function wants to + * handle everything itself. In this case, PED_EXCEPTION_UNHANDLED is + * returned. + * + * @{ + */ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/exception.h> + +#define N_(String) String +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> + +int ped_exception = 0; + +static PedExceptionOption default_handler (PedException* ex); + +static PedExceptionHandler* ex_handler = default_handler; +static PedException* ex = NULL; +static int ex_fetch_count = 0; + +static const char *const type_strings [] = { + N_("Information"), + N_("Warning"), + N_("Error"), + N_("Fatal"), + N_("Bug"), + N_("No Implementation") +}; + +static const char *const option_strings [] = { + N_("Fix"), + N_("Yes"), + N_("No"), + N_("OK"), + N_("Retry"), + N_("Ignore"), + N_("Cancel") +}; + +/** + * Return a string describing an exception type. + */ +char* +ped_exception_get_type_string (PedExceptionType ex_type) +{ + return (char *) type_strings [ex_type - 1]; +} + +/* FIXME: move this out to the prospective math.c */ +/* FIXME: this can probably be done more efficiently */ +static int _GL_ATTRIBUTE_PURE +ped_log2 (int n) +{ + int x; + + PED_ASSERT (n > 0); + + for (x=0; 1 << x <= n; x++); + + return x - 1; +} + +/** + * Return a string describing an exception option. + */ +char* +ped_exception_get_option_string (PedExceptionOption ex_opt) +{ + return (char *) option_strings [ped_log2 (ex_opt)]; +} + +static PedExceptionOption +default_handler (PedException* e) +{ + if (e->type == PED_EXCEPTION_BUG) + fprintf (stderr, + _("A bug has been detected in GNU Parted. " + "Refer to the web site of parted " + "http://www.gnu.org/software/parted/parted.html " + "for more information of what could be useful " + "for bug submitting! " + "Please email a bug report to " + "%s containing at least the " + "version (%s) and the following message: "), + PACKAGE_BUGREPORT, VERSION); + else + fprintf (stderr, "%s: ", + ped_exception_get_type_string (e->type)); + fprintf (stderr, "%s\n", e->message); + + switch (e->options) { + case PED_EXCEPTION_OK: + case PED_EXCEPTION_CANCEL: + case PED_EXCEPTION_IGNORE: + return e->options; + + default: + return PED_EXCEPTION_UNHANDLED; + } +} + +/** + * Set the exception handler. + * + * The exception handler should return ONE of the options set in ex->options, + * indicating the way the event should be resolved. + */ +void +ped_exception_set_handler (PedExceptionHandler* handler) +{ + if (handler) + ex_handler = handler; + else + ex_handler = default_handler; +} + +/** + * Get the current exception handler. + */ +PedExceptionHandler * +ped_exception_get_handler (void) +{ + if (ex_handler) + return ex_handler; + return default_handler; +} + +/** + * Assert that the current exception has been resolved. + */ +void +ped_exception_catch () +{ + if (ped_exception) { + ped_exception = 0; + free (ex->message); + free (ex); + ex = NULL; + } +} + +static PedExceptionOption +do_throw () +{ + PedExceptionOption ex_opt; + + ped_exception = 1; + + if (ex_fetch_count) { + return PED_EXCEPTION_UNHANDLED; + } else { + ex_opt = ex_handler (ex); + ped_exception_catch (); + return ex_opt; + } +} + +/** + * Throw an exception. + * + * You can also use this in a program using libparted. + * "message" is a printf-like format string, so you can do + * + * \code + * ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_RETRY_CANCEL, + * "Can't open %s", file_name); + * \endcode + * + * Returns the option selected to resolve the exception. If the exception was + * unhandled, PED_EXCEPTION_UNHANDLED is returned. + */ +PedExceptionOption +ped_exception_throw (PedExceptionType ex_type, + PedExceptionOption ex_opts, const char* message, ...) +{ + va_list arg_list; + int result; + static int size = 1000; + + if (ex) + ped_exception_catch (); + + ex = (PedException*) malloc (sizeof (PedException)); + if (!ex) + goto no_memory; + + ex->type = ex_type; + ex->options = ex_opts; + + while (message) { + ex->message = (char*) malloc (size * sizeof (char)); + if (!ex->message) + goto no_memory; + + va_start (arg_list, message); + result = vsnprintf (ex->message, size, message, arg_list); + va_end (arg_list); + + if (result > -1 && result < size) + break; + + size += 10; + free (ex->message); + } + + return do_throw (); + +no_memory: + fputs ("Out of memory in exception handler!\n", stderr); + + va_start (arg_list, message); + vfprintf (stderr, message, arg_list); + va_end (arg_list); + + return PED_EXCEPTION_UNHANDLED; +} + +/** + * Rethrow an unhandled exception. + * This means repeating the last ped_exception_throw() statement. + */ +PedExceptionOption +ped_exception_rethrow () +{ + return do_throw (); +} + +/** + * Indicates that exceptions should not go to the exception handler, but + * passed up to the calling function(s). All calls to + * ped_exception_throw() will return PED_EXCEPTION_UNHANDLED. + */ +void +ped_exception_fetch_all () +{ + ex_fetch_count++; +} + +/** + * Indicates that the calling function does not want to accept any + * responsibility for exceptions any more. + * + * \note a caller of that function may still want responsibility, so + * ped_exception_throw() may not invoke the exception handler. + * + * \warning every call to this function must have a preceding + * ped_exception_fetch_all(). + */ +void +ped_exception_leave_all () +{ + PED_ASSERT (ex_fetch_count > 0); + ex_fetch_count--; +} + +/** @} */ diff --git a/libparted/filesys.c b/libparted/filesys.c new file mode 100644 index 0000000..f67a4e8 --- /dev/null +++ b/libparted/filesys.c @@ -0,0 +1,295 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-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/>. +*/ + +/** \file filesys.c */ + +/** + * \addtogroup PedFileSystem + * + * \note File systems exist on a PedGeometry - NOT a PedPartition. + * + * @{ + */ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#define BUFFER_SIZE 4096 /* in sectors */ + +static PedFileSystemType* fs_types = NULL; +static PedFileSystemAlias* fs_aliases = NULL; + +void +ped_file_system_type_register (PedFileSystemType* fs_type) +{ + PED_ASSERT (fs_type != NULL); + PED_ASSERT (fs_type->ops != NULL); + PED_ASSERT (fs_type->name != NULL); + + fs_type->next = fs_types; + fs_types = fs_type; +} + +void +ped_file_system_type_unregister (PedFileSystemType* fs_type) +{ + PedFileSystemType* walk; + PedFileSystemType* last = NULL; + + PED_ASSERT (fs_types != NULL); + PED_ASSERT (fs_type != NULL); + + for (walk = fs_types; walk && walk != fs_type; + last = walk, walk = walk->next); + + PED_ASSERT (walk != NULL); + if (last) + ((struct _PedFileSystemType*) last)->next = fs_type->next; + else + fs_types = fs_type->next; +} + +void +ped_file_system_alias_register (PedFileSystemType* fs_type, const char* alias, + int deprecated) +{ + PedFileSystemAlias* fs_alias; + + PED_ASSERT (fs_type != NULL); + PED_ASSERT (alias != NULL); + + fs_alias = ped_malloc (sizeof *fs_alias); + if (!fs_alias) + return; + + fs_alias->next = fs_aliases; + fs_alias->fs_type = fs_type; + fs_alias->alias = alias; + fs_alias->deprecated = deprecated; + fs_aliases = fs_alias; +} + +void +ped_file_system_alias_unregister (PedFileSystemType* fs_type, + const char* alias) +{ + PedFileSystemAlias* walk; + PedFileSystemAlias* last = NULL; + + PED_ASSERT (fs_aliases != NULL); + PED_ASSERT (fs_type != NULL); + PED_ASSERT (alias != NULL); + + for (walk = fs_aliases; walk; last = walk, walk = walk->next) { + if (walk->fs_type == fs_type && !strcmp (walk->alias, alias)) + break; + } + + PED_ASSERT (walk != NULL); + if (last) + last->next = walk->next; + else + fs_aliases = walk->next; + free (walk); +} + +/** + * Get a PedFileSystemType by its @p name. + * + * @return @c NULL if none found. + */ +PedFileSystemType* +ped_file_system_type_get (const char* name) +{ + PedFileSystemType* walk; + PedFileSystemAlias* alias_walk; + + PED_ASSERT (name != NULL); + + for (walk = fs_types; walk != NULL; walk = walk->next) { + if (!strcasecmp (walk->name, name)) + break; + } + if (walk != NULL) + return walk; + + for (alias_walk = fs_aliases; alias_walk != NULL; + alias_walk = alias_walk->next) { + if (!strcasecmp (alias_walk->alias, name)) + break; + } + if (alias_walk != NULL) { + if (alias_walk->deprecated) + PED_DEBUG (0, "File system alias %s is deprecated", + name); + return alias_walk->fs_type; + } + + return NULL; +} + +/** + * Get the next PedFileSystemType after @p fs_type. + * + * @return @c NULL if @p fs_type is the last item in the list. + */ +PedFileSystemType* +ped_file_system_type_get_next (const PedFileSystemType* fs_type) +{ + if (fs_type) + return fs_type->next; + else + return fs_types; +} + +/** + * Get the next PedFileSystemAlias after @p fs_alias. + * + * @return @c NULL if @p fs_alias is the last item in the list. + */ +PedFileSystemAlias* +ped_file_system_alias_get_next (const PedFileSystemAlias* fs_alias) +{ + if (fs_alias) + return fs_alias->next; + else + return fs_aliases; +} + +/** + * Attempt to find a file system and return the region it occupies. + * + * @param fs_type The file system type to probe for. + * @param geom The region to be searched. + * + * @return @p NULL if @p fs_type file system wasn't detected + */ +PedGeometry* +ped_file_system_probe_specific ( + const PedFileSystemType* fs_type, PedGeometry* geom) +{ + PedGeometry* result; + + PED_ASSERT (fs_type != NULL); + PED_ASSERT (fs_type->ops->probe != NULL); + PED_ASSERT (geom != NULL); + + if (!ped_device_open (geom->dev)) + return 0; + result = fs_type->ops->probe (geom); + ped_device_close (geom->dev); + return result; +} + +static int +_geometry_error (const PedGeometry* a, const PedGeometry* b) +{ + PedSector start_delta = a->start - b->start; + PedSector end_delta = a->end - b->end; + + return llabs (start_delta) + llabs (end_delta); +} + +static PedFileSystemType* +_best_match (const PedGeometry* geom, PedFileSystemType* detected [], + const int detected_error [], int detected_count) +{ + int best_match = 0; + int i; + PedSector min_error; + + min_error = PED_MAX (4096, geom->length / 100); + + for (i = 1; i < detected_count; i++) { + if (detected_error [i] < detected_error [best_match]) + best_match = i; + } + + /* make sure the best match is significantly better than all the + * other matches + */ + for (i = 0; i < detected_count; i++) { + if (i == best_match) + continue; + + if (abs (detected_error [best_match] - detected_error [i]) + < min_error) + return NULL; + } + + return detected [best_match]; +} + + +/** + * Attempt to detect a file system in region \p geom. + * This function tries to be clever at dealing with ambiguous + * situations, such as when one file system was not completely erased before a + * new file system was created on top of it. + * + * \return a new PedFileSystem on success, \c NULL on failure + */ +PedFileSystemType* +ped_file_system_probe (PedGeometry* geom) +{ + PedFileSystemType* detected[32]; + int detected_error[32]; + int detected_count = 0; + PedFileSystemType* walk = NULL; + + PED_ASSERT (geom != NULL); + + if (!ped_device_open (geom->dev)) + return NULL; + + ped_exception_fetch_all (); + while ( (walk = ped_file_system_type_get_next (walk)) ) { + PedGeometry* probed; + + probed = ped_file_system_probe_specific (walk, geom); + if (probed) { + detected [detected_count] = walk; + detected_error [detected_count] + = _geometry_error (geom, probed); + detected_count++; + ped_geometry_destroy (probed); + } else { + ped_exception_catch (); + } + } + ped_exception_leave_all (); + + ped_device_close (geom->dev); + + if (!detected_count) + return NULL; + walk = _best_match (geom, detected, detected_error, detected_count); + if (walk) + return walk; + return NULL; +} diff --git a/libparted/fs/Makefile.am b/libparted/fs/Makefile.am new file mode 100644 index 0000000..41a60d9 --- /dev/null +++ b/libparted/fs/Makefile.am @@ -0,0 +1,123 @@ +# This file is part of GNU Parted +# Copyright (C) 1999-2001, 2007, 2009-2014, 2019-2023 Free Software Foundation, +# Inc. +# +# This file may be modified and/or distributed without restriction. + +partedincludedir = -I$(top_builddir)/include -I$(top_srcdir)/include -I$(top_srcdir)/lib + +AM_CFLAGS = $(WARN_CFLAGS) + +noinst_LTLIBRARIES = libfs.la + +libfs_la_LIBADD = $(UUID_LIBS) \ + $(INTLLIBS) \ + $(OS_LIBS) + +libfs_la_SOURCES = \ + amiga/affs.c \ + amiga/affs.h \ + amiga/amiga.c \ + amiga/amiga.h \ + amiga/apfs.c \ + amiga/apfs.h \ + amiga/asfs.c \ + amiga/asfs.h \ + amiga/a-interface.c \ + btrfs/btrfs.c \ + ext2/ext2.h \ + ext2/ext2_fs.h \ + ext2/interface.c \ + fat/bootsector.c \ + fat/bootsector.h \ + fat/count.h \ + fat/fat.c \ + fat/fat.h \ + f2fs/f2fs.c \ + f2fs/f2fs.h \ + hfs/hfs.c \ + hfs/hfs.h \ + hfs/probe.c \ + hfs/probe.h \ + jfs/jfs.c \ + jfs/jfs_superblock.h \ + jfs/jfs_types.h \ + linux_swap/linux_swap.c \ + nilfs2/nilfs2.c \ + ntfs/ntfs.c \ + reiserfs/reiserfs.c \ + reiserfs/reiserfs.h \ + udf/udf.c \ + ufs/ufs.c \ + xfs/platform_defs.h \ + xfs/xfs.c \ + xfs/xfs_sb.h \ + xfs/xfs_types.h + +lib_LTLIBRARIES = libparted-fs-resize.la + +EXTRA_DIST = \ + hfs/DOC \ + hfs/HISTORY \ + hfs/TODO + +# Set the shared library version, per Libtool's guidelines. +# For details, see the "Updating library version information" section of +# "info libtool". +CURRENT = 0 +REVISION = 5 +AGE = 0 + +sym_file = $(srcdir)/fsresize.sym +libparted_fs_resize_la_LDFLAGS = \ + -Wl,--version-script=$(sym_file) \ + -version-info $(CURRENT):$(REVISION):$(AGE) +EXTRA_DIST += fsresize.sym +libparted_fs_resize_la_DEPENDENCIES = $(sym_file) + +libparted_fs_resize_la_SOURCES = \ + r/filesys.c \ + r/fat/bootsector.c \ + r/fat/bootsector.h \ + r/fat/calc.c \ + r/fat/calc.h \ + r/fat/clstdup.c \ + r/fat/clstdup.h \ + r/fat/context.c \ + r/fat/context.h \ + r/fat/count.c \ + r/fat/count.h \ + r/fat/fat.c \ + r/fat/fat.h \ + r/fat/fatio.c \ + r/fat/fatio.h \ + r/fat/resize.c \ + r/fat/table.c \ + r/fat/table.h \ + r/fat/traverse.c \ + r/fat/traverse.h \ + r/hfs/advfs.c \ + r/hfs/advfs.h \ + r/hfs/advfs_plus.c \ + r/hfs/advfs_plus.h \ + r/hfs/cache.c \ + r/hfs/cache.h \ + r/hfs/file.c \ + r/hfs/file.h \ + r/hfs/file_plus.c \ + r/hfs/file_plus.h \ + r/hfs/hfs.c \ + r/hfs/hfs.h \ + r/hfs/journal.c \ + r/hfs/journal.h \ + r/hfs/probe.c \ + r/hfs/probe.h \ + r/hfs/reloc.c \ + r/hfs/reloc.h \ + r/hfs/reloc_plus.c \ + r/hfs/reloc_plus.h + +AM_CPPFLAGS = \ + -I$(top_srcdir)/libparted/labels \ + $(partedincludedir) \ + $(INTLINCS) diff --git a/libparted/fs/Makefile.in b/libparted/fs/Makefile.in new file mode 100644 index 0000000..3b552b8 --- /dev/null +++ b/libparted/fs/Makefile.in @@ -0,0 +1,2532 @@ +# 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, 2009-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/fs +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 = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) +am__DEPENDENCIES_1 = +libfs_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am__dirstamp = $(am__leading_dot)dirstamp +am_libfs_la_OBJECTS = amiga/affs.lo amiga/amiga.lo amiga/apfs.lo \ + amiga/asfs.lo amiga/a-interface.lo btrfs/btrfs.lo \ + ext2/interface.lo fat/bootsector.lo fat/fat.lo f2fs/f2fs.lo \ + hfs/hfs.lo hfs/probe.lo jfs/jfs.lo linux_swap/linux_swap.lo \ + nilfs2/nilfs2.lo ntfs/ntfs.lo reiserfs/reiserfs.lo udf/udf.lo \ + ufs/ufs.lo xfs/xfs.lo +libfs_la_OBJECTS = $(am_libfs_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 = +libparted_fs_resize_la_LIBADD = +am_libparted_fs_resize_la_OBJECTS = r/filesys.lo r/fat/bootsector.lo \ + r/fat/calc.lo r/fat/clstdup.lo r/fat/context.lo r/fat/count.lo \ + r/fat/fat.lo r/fat/fatio.lo r/fat/resize.lo r/fat/table.lo \ + r/fat/traverse.lo r/hfs/advfs.lo r/hfs/advfs_plus.lo \ + r/hfs/cache.lo r/hfs/file.lo r/hfs/file_plus.lo r/hfs/hfs.lo \ + r/hfs/journal.lo r/hfs/probe.lo r/hfs/reloc.lo \ + r/hfs/reloc_plus.lo +libparted_fs_resize_la_OBJECTS = $(am_libparted_fs_resize_la_OBJECTS) +libparted_fs_resize_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libparted_fs_resize_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +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 = amiga/$(DEPDIR)/a-interface.Plo \ + amiga/$(DEPDIR)/affs.Plo amiga/$(DEPDIR)/amiga.Plo \ + amiga/$(DEPDIR)/apfs.Plo amiga/$(DEPDIR)/asfs.Plo \ + btrfs/$(DEPDIR)/btrfs.Plo ext2/$(DEPDIR)/interface.Plo \ + f2fs/$(DEPDIR)/f2fs.Plo fat/$(DEPDIR)/bootsector.Plo \ + fat/$(DEPDIR)/fat.Plo hfs/$(DEPDIR)/hfs.Plo \ + hfs/$(DEPDIR)/probe.Plo jfs/$(DEPDIR)/jfs.Plo \ + linux_swap/$(DEPDIR)/linux_swap.Plo \ + nilfs2/$(DEPDIR)/nilfs2.Plo ntfs/$(DEPDIR)/ntfs.Plo \ + r/$(DEPDIR)/filesys.Plo r/fat/$(DEPDIR)/bootsector.Plo \ + r/fat/$(DEPDIR)/calc.Plo r/fat/$(DEPDIR)/clstdup.Plo \ + r/fat/$(DEPDIR)/context.Plo r/fat/$(DEPDIR)/count.Plo \ + r/fat/$(DEPDIR)/fat.Plo r/fat/$(DEPDIR)/fatio.Plo \ + r/fat/$(DEPDIR)/resize.Plo r/fat/$(DEPDIR)/table.Plo \ + r/fat/$(DEPDIR)/traverse.Plo r/hfs/$(DEPDIR)/advfs.Plo \ + r/hfs/$(DEPDIR)/advfs_plus.Plo r/hfs/$(DEPDIR)/cache.Plo \ + r/hfs/$(DEPDIR)/file.Plo r/hfs/$(DEPDIR)/file_plus.Plo \ + r/hfs/$(DEPDIR)/hfs.Plo r/hfs/$(DEPDIR)/journal.Plo \ + r/hfs/$(DEPDIR)/probe.Plo r/hfs/$(DEPDIR)/reloc.Plo \ + r/hfs/$(DEPDIR)/reloc_plus.Plo reiserfs/$(DEPDIR)/reiserfs.Plo \ + udf/$(DEPDIR)/udf.Plo ufs/$(DEPDIR)/ufs.Plo \ + xfs/$(DEPDIR)/xfs.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 = $(libfs_la_SOURCES) $(libparted_fs_resize_la_SOURCES) +DIST_SOURCES = $(libfs_la_SOURCES) $(libparted_fs_resize_la_SOURCES) +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@ +partedincludedir = -I$(top_builddir)/include -I$(top_srcdir)/include -I$(top_srcdir)/lib +AM_CFLAGS = $(WARN_CFLAGS) +noinst_LTLIBRARIES = libfs.la +libfs_la_LIBADD = $(UUID_LIBS) \ + $(INTLLIBS) \ + $(OS_LIBS) + +libfs_la_SOURCES = \ + amiga/affs.c \ + amiga/affs.h \ + amiga/amiga.c \ + amiga/amiga.h \ + amiga/apfs.c \ + amiga/apfs.h \ + amiga/asfs.c \ + amiga/asfs.h \ + amiga/a-interface.c \ + btrfs/btrfs.c \ + ext2/ext2.h \ + ext2/ext2_fs.h \ + ext2/interface.c \ + fat/bootsector.c \ + fat/bootsector.h \ + fat/count.h \ + fat/fat.c \ + fat/fat.h \ + f2fs/f2fs.c \ + f2fs/f2fs.h \ + hfs/hfs.c \ + hfs/hfs.h \ + hfs/probe.c \ + hfs/probe.h \ + jfs/jfs.c \ + jfs/jfs_superblock.h \ + jfs/jfs_types.h \ + linux_swap/linux_swap.c \ + nilfs2/nilfs2.c \ + ntfs/ntfs.c \ + reiserfs/reiserfs.c \ + reiserfs/reiserfs.h \ + udf/udf.c \ + ufs/ufs.c \ + xfs/platform_defs.h \ + xfs/xfs.c \ + xfs/xfs_sb.h \ + xfs/xfs_types.h + +lib_LTLIBRARIES = libparted-fs-resize.la +EXTRA_DIST = hfs/DOC hfs/HISTORY hfs/TODO fsresize.sym + +# Set the shared library version, per Libtool's guidelines. +# For details, see the "Updating library version information" section of +# "info libtool". +CURRENT = 0 +REVISION = 5 +AGE = 0 +sym_file = $(srcdir)/fsresize.sym +libparted_fs_resize_la_LDFLAGS = \ + -Wl,--version-script=$(sym_file) \ + -version-info $(CURRENT):$(REVISION):$(AGE) + +libparted_fs_resize_la_DEPENDENCIES = $(sym_file) +libparted_fs_resize_la_SOURCES = \ + r/filesys.c \ + r/fat/bootsector.c \ + r/fat/bootsector.h \ + r/fat/calc.c \ + r/fat/calc.h \ + r/fat/clstdup.c \ + r/fat/clstdup.h \ + r/fat/context.c \ + r/fat/context.h \ + r/fat/count.c \ + r/fat/count.h \ + r/fat/fat.c \ + r/fat/fat.h \ + r/fat/fatio.c \ + r/fat/fatio.h \ + r/fat/resize.c \ + r/fat/table.c \ + r/fat/table.h \ + r/fat/traverse.c \ + r/fat/traverse.h \ + r/hfs/advfs.c \ + r/hfs/advfs.h \ + r/hfs/advfs_plus.c \ + r/hfs/advfs_plus.h \ + r/hfs/cache.c \ + r/hfs/cache.h \ + r/hfs/file.c \ + r/hfs/file.h \ + r/hfs/file_plus.c \ + r/hfs/file_plus.h \ + r/hfs/hfs.c \ + r/hfs/hfs.h \ + r/hfs/journal.c \ + r/hfs/journal.h \ + r/hfs/probe.c \ + r/hfs/probe.h \ + r/hfs/reloc.c \ + r/hfs/reloc.h \ + r/hfs/reloc_plus.c \ + r/hfs/reloc_plus.h + +AM_CPPFLAGS = \ + -I$(top_srcdir)/libparted/labels \ + $(partedincludedir) \ + $(INTLINCS) + +all: 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/fs/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu libparted/fs/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): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_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}; \ + } + +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}; \ + } +amiga/$(am__dirstamp): + @$(MKDIR_P) amiga + @: > amiga/$(am__dirstamp) +amiga/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) amiga/$(DEPDIR) + @: > amiga/$(DEPDIR)/$(am__dirstamp) +amiga/affs.lo: amiga/$(am__dirstamp) amiga/$(DEPDIR)/$(am__dirstamp) +amiga/amiga.lo: amiga/$(am__dirstamp) amiga/$(DEPDIR)/$(am__dirstamp) +amiga/apfs.lo: amiga/$(am__dirstamp) amiga/$(DEPDIR)/$(am__dirstamp) +amiga/asfs.lo: amiga/$(am__dirstamp) amiga/$(DEPDIR)/$(am__dirstamp) +amiga/a-interface.lo: amiga/$(am__dirstamp) \ + amiga/$(DEPDIR)/$(am__dirstamp) +btrfs/$(am__dirstamp): + @$(MKDIR_P) btrfs + @: > btrfs/$(am__dirstamp) +btrfs/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) btrfs/$(DEPDIR) + @: > btrfs/$(DEPDIR)/$(am__dirstamp) +btrfs/btrfs.lo: btrfs/$(am__dirstamp) btrfs/$(DEPDIR)/$(am__dirstamp) +ext2/$(am__dirstamp): + @$(MKDIR_P) ext2 + @: > ext2/$(am__dirstamp) +ext2/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) ext2/$(DEPDIR) + @: > ext2/$(DEPDIR)/$(am__dirstamp) +ext2/interface.lo: ext2/$(am__dirstamp) ext2/$(DEPDIR)/$(am__dirstamp) +fat/$(am__dirstamp): + @$(MKDIR_P) fat + @: > fat/$(am__dirstamp) +fat/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) fat/$(DEPDIR) + @: > fat/$(DEPDIR)/$(am__dirstamp) +fat/bootsector.lo: fat/$(am__dirstamp) fat/$(DEPDIR)/$(am__dirstamp) +fat/fat.lo: fat/$(am__dirstamp) fat/$(DEPDIR)/$(am__dirstamp) +f2fs/$(am__dirstamp): + @$(MKDIR_P) f2fs + @: > f2fs/$(am__dirstamp) +f2fs/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) f2fs/$(DEPDIR) + @: > f2fs/$(DEPDIR)/$(am__dirstamp) +f2fs/f2fs.lo: f2fs/$(am__dirstamp) f2fs/$(DEPDIR)/$(am__dirstamp) +hfs/$(am__dirstamp): + @$(MKDIR_P) hfs + @: > hfs/$(am__dirstamp) +hfs/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) hfs/$(DEPDIR) + @: > hfs/$(DEPDIR)/$(am__dirstamp) +hfs/hfs.lo: hfs/$(am__dirstamp) hfs/$(DEPDIR)/$(am__dirstamp) +hfs/probe.lo: hfs/$(am__dirstamp) hfs/$(DEPDIR)/$(am__dirstamp) +jfs/$(am__dirstamp): + @$(MKDIR_P) jfs + @: > jfs/$(am__dirstamp) +jfs/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) jfs/$(DEPDIR) + @: > jfs/$(DEPDIR)/$(am__dirstamp) +jfs/jfs.lo: jfs/$(am__dirstamp) jfs/$(DEPDIR)/$(am__dirstamp) +linux_swap/$(am__dirstamp): + @$(MKDIR_P) linux_swap + @: > linux_swap/$(am__dirstamp) +linux_swap/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) linux_swap/$(DEPDIR) + @: > linux_swap/$(DEPDIR)/$(am__dirstamp) +linux_swap/linux_swap.lo: linux_swap/$(am__dirstamp) \ + linux_swap/$(DEPDIR)/$(am__dirstamp) +nilfs2/$(am__dirstamp): + @$(MKDIR_P) nilfs2 + @: > nilfs2/$(am__dirstamp) +nilfs2/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) nilfs2/$(DEPDIR) + @: > nilfs2/$(DEPDIR)/$(am__dirstamp) +nilfs2/nilfs2.lo: nilfs2/$(am__dirstamp) \ + nilfs2/$(DEPDIR)/$(am__dirstamp) +ntfs/$(am__dirstamp): + @$(MKDIR_P) ntfs + @: > ntfs/$(am__dirstamp) +ntfs/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) ntfs/$(DEPDIR) + @: > ntfs/$(DEPDIR)/$(am__dirstamp) +ntfs/ntfs.lo: ntfs/$(am__dirstamp) ntfs/$(DEPDIR)/$(am__dirstamp) +reiserfs/$(am__dirstamp): + @$(MKDIR_P) reiserfs + @: > reiserfs/$(am__dirstamp) +reiserfs/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) reiserfs/$(DEPDIR) + @: > reiserfs/$(DEPDIR)/$(am__dirstamp) +reiserfs/reiserfs.lo: reiserfs/$(am__dirstamp) \ + reiserfs/$(DEPDIR)/$(am__dirstamp) +udf/$(am__dirstamp): + @$(MKDIR_P) udf + @: > udf/$(am__dirstamp) +udf/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) udf/$(DEPDIR) + @: > udf/$(DEPDIR)/$(am__dirstamp) +udf/udf.lo: udf/$(am__dirstamp) udf/$(DEPDIR)/$(am__dirstamp) +ufs/$(am__dirstamp): + @$(MKDIR_P) ufs + @: > ufs/$(am__dirstamp) +ufs/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) ufs/$(DEPDIR) + @: > ufs/$(DEPDIR)/$(am__dirstamp) +ufs/ufs.lo: ufs/$(am__dirstamp) ufs/$(DEPDIR)/$(am__dirstamp) +xfs/$(am__dirstamp): + @$(MKDIR_P) xfs + @: > xfs/$(am__dirstamp) +xfs/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) xfs/$(DEPDIR) + @: > xfs/$(DEPDIR)/$(am__dirstamp) +xfs/xfs.lo: xfs/$(am__dirstamp) xfs/$(DEPDIR)/$(am__dirstamp) + +libfs.la: $(libfs_la_OBJECTS) $(libfs_la_DEPENDENCIES) $(EXTRA_libfs_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libfs_la_OBJECTS) $(libfs_la_LIBADD) $(LIBS) +r/$(am__dirstamp): + @$(MKDIR_P) r + @: > r/$(am__dirstamp) +r/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) r/$(DEPDIR) + @: > r/$(DEPDIR)/$(am__dirstamp) +r/filesys.lo: r/$(am__dirstamp) r/$(DEPDIR)/$(am__dirstamp) +r/fat/$(am__dirstamp): + @$(MKDIR_P) r/fat + @: > r/fat/$(am__dirstamp) +r/fat/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) r/fat/$(DEPDIR) + @: > r/fat/$(DEPDIR)/$(am__dirstamp) +r/fat/bootsector.lo: r/fat/$(am__dirstamp) \ + r/fat/$(DEPDIR)/$(am__dirstamp) +r/fat/calc.lo: r/fat/$(am__dirstamp) r/fat/$(DEPDIR)/$(am__dirstamp) +r/fat/clstdup.lo: r/fat/$(am__dirstamp) \ + r/fat/$(DEPDIR)/$(am__dirstamp) +r/fat/context.lo: r/fat/$(am__dirstamp) \ + r/fat/$(DEPDIR)/$(am__dirstamp) +r/fat/count.lo: r/fat/$(am__dirstamp) r/fat/$(DEPDIR)/$(am__dirstamp) +r/fat/fat.lo: r/fat/$(am__dirstamp) r/fat/$(DEPDIR)/$(am__dirstamp) +r/fat/fatio.lo: r/fat/$(am__dirstamp) r/fat/$(DEPDIR)/$(am__dirstamp) +r/fat/resize.lo: r/fat/$(am__dirstamp) r/fat/$(DEPDIR)/$(am__dirstamp) +r/fat/table.lo: r/fat/$(am__dirstamp) r/fat/$(DEPDIR)/$(am__dirstamp) +r/fat/traverse.lo: r/fat/$(am__dirstamp) \ + r/fat/$(DEPDIR)/$(am__dirstamp) +r/hfs/$(am__dirstamp): + @$(MKDIR_P) r/hfs + @: > r/hfs/$(am__dirstamp) +r/hfs/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) r/hfs/$(DEPDIR) + @: > r/hfs/$(DEPDIR)/$(am__dirstamp) +r/hfs/advfs.lo: r/hfs/$(am__dirstamp) r/hfs/$(DEPDIR)/$(am__dirstamp) +r/hfs/advfs_plus.lo: r/hfs/$(am__dirstamp) \ + r/hfs/$(DEPDIR)/$(am__dirstamp) +r/hfs/cache.lo: r/hfs/$(am__dirstamp) r/hfs/$(DEPDIR)/$(am__dirstamp) +r/hfs/file.lo: r/hfs/$(am__dirstamp) r/hfs/$(DEPDIR)/$(am__dirstamp) +r/hfs/file_plus.lo: r/hfs/$(am__dirstamp) \ + r/hfs/$(DEPDIR)/$(am__dirstamp) +r/hfs/hfs.lo: r/hfs/$(am__dirstamp) r/hfs/$(DEPDIR)/$(am__dirstamp) +r/hfs/journal.lo: r/hfs/$(am__dirstamp) \ + r/hfs/$(DEPDIR)/$(am__dirstamp) +r/hfs/probe.lo: r/hfs/$(am__dirstamp) r/hfs/$(DEPDIR)/$(am__dirstamp) +r/hfs/reloc.lo: r/hfs/$(am__dirstamp) r/hfs/$(DEPDIR)/$(am__dirstamp) +r/hfs/reloc_plus.lo: r/hfs/$(am__dirstamp) \ + r/hfs/$(DEPDIR)/$(am__dirstamp) + +libparted-fs-resize.la: $(libparted_fs_resize_la_OBJECTS) $(libparted_fs_resize_la_DEPENDENCIES) $(EXTRA_libparted_fs_resize_la_DEPENDENCIES) + $(AM_V_CCLD)$(libparted_fs_resize_la_LINK) -rpath $(libdir) $(libparted_fs_resize_la_OBJECTS) $(libparted_fs_resize_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f amiga/*.$(OBJEXT) + -rm -f amiga/*.lo + -rm -f btrfs/*.$(OBJEXT) + -rm -f btrfs/*.lo + -rm -f ext2/*.$(OBJEXT) + -rm -f ext2/*.lo + -rm -f f2fs/*.$(OBJEXT) + -rm -f f2fs/*.lo + -rm -f fat/*.$(OBJEXT) + -rm -f fat/*.lo + -rm -f hfs/*.$(OBJEXT) + -rm -f hfs/*.lo + -rm -f jfs/*.$(OBJEXT) + -rm -f jfs/*.lo + -rm -f linux_swap/*.$(OBJEXT) + -rm -f linux_swap/*.lo + -rm -f nilfs2/*.$(OBJEXT) + -rm -f nilfs2/*.lo + -rm -f ntfs/*.$(OBJEXT) + -rm -f ntfs/*.lo + -rm -f r/*.$(OBJEXT) + -rm -f r/*.lo + -rm -f r/fat/*.$(OBJEXT) + -rm -f r/fat/*.lo + -rm -f r/hfs/*.$(OBJEXT) + -rm -f r/hfs/*.lo + -rm -f reiserfs/*.$(OBJEXT) + -rm -f reiserfs/*.lo + -rm -f udf/*.$(OBJEXT) + -rm -f udf/*.lo + -rm -f ufs/*.$(OBJEXT) + -rm -f ufs/*.lo + -rm -f xfs/*.$(OBJEXT) + -rm -f xfs/*.lo + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@amiga/$(DEPDIR)/a-interface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@amiga/$(DEPDIR)/affs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@amiga/$(DEPDIR)/amiga.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@amiga/$(DEPDIR)/apfs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@amiga/$(DEPDIR)/asfs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@btrfs/$(DEPDIR)/btrfs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@ext2/$(DEPDIR)/interface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@f2fs/$(DEPDIR)/f2fs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@fat/$(DEPDIR)/bootsector.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@fat/$(DEPDIR)/fat.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@hfs/$(DEPDIR)/hfs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@hfs/$(DEPDIR)/probe.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@jfs/$(DEPDIR)/jfs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@linux_swap/$(DEPDIR)/linux_swap.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@nilfs2/$(DEPDIR)/nilfs2.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@ntfs/$(DEPDIR)/ntfs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/$(DEPDIR)/filesys.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/fat/$(DEPDIR)/bootsector.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/fat/$(DEPDIR)/calc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/fat/$(DEPDIR)/clstdup.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/fat/$(DEPDIR)/context.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/fat/$(DEPDIR)/count.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/fat/$(DEPDIR)/fat.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/fat/$(DEPDIR)/fatio.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/fat/$(DEPDIR)/resize.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/fat/$(DEPDIR)/table.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/fat/$(DEPDIR)/traverse.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/hfs/$(DEPDIR)/advfs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/hfs/$(DEPDIR)/advfs_plus.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/hfs/$(DEPDIR)/cache.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/hfs/$(DEPDIR)/file.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/hfs/$(DEPDIR)/file_plus.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/hfs/$(DEPDIR)/hfs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/hfs/$(DEPDIR)/journal.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/hfs/$(DEPDIR)/probe.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/hfs/$(DEPDIR)/reloc.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@r/hfs/$(DEPDIR)/reloc_plus.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@reiserfs/$(DEPDIR)/reiserfs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@udf/$(DEPDIR)/udf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@ufs/$(DEPDIR)/ufs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@xfs/$(DEPDIR)/xfs.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 + -rm -rf amiga/.libs amiga/_libs + -rm -rf btrfs/.libs btrfs/_libs + -rm -rf ext2/.libs ext2/_libs + -rm -rf f2fs/.libs f2fs/_libs + -rm -rf fat/.libs fat/_libs + -rm -rf hfs/.libs hfs/_libs + -rm -rf jfs/.libs jfs/_libs + -rm -rf linux_swap/.libs linux_swap/_libs + -rm -rf nilfs2/.libs nilfs2/_libs + -rm -rf ntfs/.libs ntfs/_libs + -rm -rf r/.libs r/_libs + -rm -rf r/fat/.libs r/fat/_libs + -rm -rf r/hfs/.libs r/hfs/_libs + -rm -rf reiserfs/.libs reiserfs/_libs + -rm -rf udf/.libs udf/_libs + -rm -rf ufs/.libs ufs/_libs + -rm -rf xfs/.libs xfs/_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: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: 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) + -rm -f amiga/$(DEPDIR)/$(am__dirstamp) + -rm -f amiga/$(am__dirstamp) + -rm -f btrfs/$(DEPDIR)/$(am__dirstamp) + -rm -f btrfs/$(am__dirstamp) + -rm -f ext2/$(DEPDIR)/$(am__dirstamp) + -rm -f ext2/$(am__dirstamp) + -rm -f f2fs/$(DEPDIR)/$(am__dirstamp) + -rm -f f2fs/$(am__dirstamp) + -rm -f fat/$(DEPDIR)/$(am__dirstamp) + -rm -f fat/$(am__dirstamp) + -rm -f hfs/$(DEPDIR)/$(am__dirstamp) + -rm -f hfs/$(am__dirstamp) + -rm -f jfs/$(DEPDIR)/$(am__dirstamp) + -rm -f jfs/$(am__dirstamp) + -rm -f linux_swap/$(DEPDIR)/$(am__dirstamp) + -rm -f linux_swap/$(am__dirstamp) + -rm -f nilfs2/$(DEPDIR)/$(am__dirstamp) + -rm -f nilfs2/$(am__dirstamp) + -rm -f ntfs/$(DEPDIR)/$(am__dirstamp) + -rm -f ntfs/$(am__dirstamp) + -rm -f r/$(DEPDIR)/$(am__dirstamp) + -rm -f r/$(am__dirstamp) + -rm -f r/fat/$(DEPDIR)/$(am__dirstamp) + -rm -f r/fat/$(am__dirstamp) + -rm -f r/hfs/$(DEPDIR)/$(am__dirstamp) + -rm -f r/hfs/$(am__dirstamp) + -rm -f reiserfs/$(DEPDIR)/$(am__dirstamp) + -rm -f reiserfs/$(am__dirstamp) + -rm -f udf/$(DEPDIR)/$(am__dirstamp) + -rm -f udf/$(am__dirstamp) + -rm -f ufs/$(DEPDIR)/$(am__dirstamp) + -rm -f ufs/$(am__dirstamp) + -rm -f xfs/$(DEPDIR)/$(am__dirstamp) + -rm -f xfs/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-noinstLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f amiga/$(DEPDIR)/a-interface.Plo + -rm -f amiga/$(DEPDIR)/affs.Plo + -rm -f amiga/$(DEPDIR)/amiga.Plo + -rm -f amiga/$(DEPDIR)/apfs.Plo + -rm -f amiga/$(DEPDIR)/asfs.Plo + -rm -f btrfs/$(DEPDIR)/btrfs.Plo + -rm -f ext2/$(DEPDIR)/interface.Plo + -rm -f f2fs/$(DEPDIR)/f2fs.Plo + -rm -f fat/$(DEPDIR)/bootsector.Plo + -rm -f fat/$(DEPDIR)/fat.Plo + -rm -f hfs/$(DEPDIR)/hfs.Plo + -rm -f hfs/$(DEPDIR)/probe.Plo + -rm -f jfs/$(DEPDIR)/jfs.Plo + -rm -f linux_swap/$(DEPDIR)/linux_swap.Plo + -rm -f nilfs2/$(DEPDIR)/nilfs2.Plo + -rm -f ntfs/$(DEPDIR)/ntfs.Plo + -rm -f r/$(DEPDIR)/filesys.Plo + -rm -f r/fat/$(DEPDIR)/bootsector.Plo + -rm -f r/fat/$(DEPDIR)/calc.Plo + -rm -f r/fat/$(DEPDIR)/clstdup.Plo + -rm -f r/fat/$(DEPDIR)/context.Plo + -rm -f r/fat/$(DEPDIR)/count.Plo + -rm -f r/fat/$(DEPDIR)/fat.Plo + -rm -f r/fat/$(DEPDIR)/fatio.Plo + -rm -f r/fat/$(DEPDIR)/resize.Plo + -rm -f r/fat/$(DEPDIR)/table.Plo + -rm -f r/fat/$(DEPDIR)/traverse.Plo + -rm -f r/hfs/$(DEPDIR)/advfs.Plo + -rm -f r/hfs/$(DEPDIR)/advfs_plus.Plo + -rm -f r/hfs/$(DEPDIR)/cache.Plo + -rm -f r/hfs/$(DEPDIR)/file.Plo + -rm -f r/hfs/$(DEPDIR)/file_plus.Plo + -rm -f r/hfs/$(DEPDIR)/hfs.Plo + -rm -f r/hfs/$(DEPDIR)/journal.Plo + -rm -f r/hfs/$(DEPDIR)/probe.Plo + -rm -f r/hfs/$(DEPDIR)/reloc.Plo + -rm -f r/hfs/$(DEPDIR)/reloc_plus.Plo + -rm -f reiserfs/$(DEPDIR)/reiserfs.Plo + -rm -f udf/$(DEPDIR)/udf.Plo + -rm -f ufs/$(DEPDIR)/ufs.Plo + -rm -f xfs/$(DEPDIR)/xfs.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-libLTLIBRARIES + +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 amiga/$(DEPDIR)/a-interface.Plo + -rm -f amiga/$(DEPDIR)/affs.Plo + -rm -f amiga/$(DEPDIR)/amiga.Plo + -rm -f amiga/$(DEPDIR)/apfs.Plo + -rm -f amiga/$(DEPDIR)/asfs.Plo + -rm -f btrfs/$(DEPDIR)/btrfs.Plo + -rm -f ext2/$(DEPDIR)/interface.Plo + -rm -f f2fs/$(DEPDIR)/f2fs.Plo + -rm -f fat/$(DEPDIR)/bootsector.Plo + -rm -f fat/$(DEPDIR)/fat.Plo + -rm -f hfs/$(DEPDIR)/hfs.Plo + -rm -f hfs/$(DEPDIR)/probe.Plo + -rm -f jfs/$(DEPDIR)/jfs.Plo + -rm -f linux_swap/$(DEPDIR)/linux_swap.Plo + -rm -f nilfs2/$(DEPDIR)/nilfs2.Plo + -rm -f ntfs/$(DEPDIR)/ntfs.Plo + -rm -f r/$(DEPDIR)/filesys.Plo + -rm -f r/fat/$(DEPDIR)/bootsector.Plo + -rm -f r/fat/$(DEPDIR)/calc.Plo + -rm -f r/fat/$(DEPDIR)/clstdup.Plo + -rm -f r/fat/$(DEPDIR)/context.Plo + -rm -f r/fat/$(DEPDIR)/count.Plo + -rm -f r/fat/$(DEPDIR)/fat.Plo + -rm -f r/fat/$(DEPDIR)/fatio.Plo + -rm -f r/fat/$(DEPDIR)/resize.Plo + -rm -f r/fat/$(DEPDIR)/table.Plo + -rm -f r/fat/$(DEPDIR)/traverse.Plo + -rm -f r/hfs/$(DEPDIR)/advfs.Plo + -rm -f r/hfs/$(DEPDIR)/advfs_plus.Plo + -rm -f r/hfs/$(DEPDIR)/cache.Plo + -rm -f r/hfs/$(DEPDIR)/file.Plo + -rm -f r/hfs/$(DEPDIR)/file_plus.Plo + -rm -f r/hfs/$(DEPDIR)/hfs.Plo + -rm -f r/hfs/$(DEPDIR)/journal.Plo + -rm -f r/hfs/$(DEPDIR)/probe.Plo + -rm -f r/hfs/$(DEPDIR)/reloc.Plo + -rm -f r/hfs/$(DEPDIR)/reloc_plus.Plo + -rm -f reiserfs/$(DEPDIR)/reiserfs.Plo + -rm -f udf/$(DEPDIR)/udf.Plo + -rm -f ufs/$(DEPDIR)/ufs.Plo + -rm -f xfs/$(DEPDIR)/xfs.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: uninstall-libLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libLTLIBRARIES 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-libLTLIBRARIES 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 uninstall-libLTLIBRARIES + +.PRECIOUS: Makefile + + +# 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/fs/amiga/a-interface.c b/libparted/fs/amiga/a-interface.c new file mode 100644 index 0000000..0ae84dd --- /dev/null +++ b/libparted/fs/amiga/a-interface.c @@ -0,0 +1,88 @@ +/* + interface.c -- parted support amiga file systems + Copyright (C) 1998-2000, 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 <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +extern PedFileSystemType _affs0_type; +extern PedFileSystemType _affs1_type; +extern PedFileSystemType _affs2_type; +extern PedFileSystemType _affs3_type; +extern PedFileSystemType _affs4_type; +extern PedFileSystemType _affs5_type; +extern PedFileSystemType _affs6_type; +extern PedFileSystemType _affs7_type; +extern PedFileSystemType _amufs_type; +extern PedFileSystemType _amufs0_type; +extern PedFileSystemType _amufs1_type; +extern PedFileSystemType _amufs2_type; +extern PedFileSystemType _amufs3_type; +extern PedFileSystemType _amufs4_type; +extern PedFileSystemType _amufs5_type; +extern PedFileSystemType _asfs_type; +extern PedFileSystemType _apfs1_type; +extern PedFileSystemType _apfs2_type; + +void ped_file_system_amiga_init () +{ + ped_file_system_type_register (&_affs0_type); + ped_file_system_type_register (&_affs1_type); + ped_file_system_type_register (&_affs2_type); + ped_file_system_type_register (&_affs3_type); + ped_file_system_type_register (&_affs4_type); + ped_file_system_type_register (&_affs5_type); + ped_file_system_type_register (&_affs6_type); + ped_file_system_type_register (&_affs7_type); + ped_file_system_type_register (&_amufs_type); + ped_file_system_type_register (&_amufs0_type); + ped_file_system_type_register (&_amufs1_type); + ped_file_system_type_register (&_amufs2_type); + ped_file_system_type_register (&_amufs3_type); + ped_file_system_type_register (&_amufs4_type); + ped_file_system_type_register (&_amufs5_type); + ped_file_system_type_register (&_asfs_type); + ped_file_system_type_register (&_apfs1_type); + ped_file_system_type_register (&_apfs2_type); +} + +void ped_file_system_amiga_done () +{ + ped_file_system_type_unregister (&_affs0_type); + ped_file_system_type_unregister (&_affs1_type); + ped_file_system_type_unregister (&_affs2_type); + ped_file_system_type_unregister (&_affs3_type); + ped_file_system_type_unregister (&_affs4_type); + ped_file_system_type_unregister (&_affs5_type); + ped_file_system_type_unregister (&_affs6_type); + ped_file_system_type_unregister (&_affs7_type); + ped_file_system_type_unregister (&_amufs_type); + ped_file_system_type_unregister (&_amufs0_type); + ped_file_system_type_unregister (&_amufs1_type); + ped_file_system_type_unregister (&_amufs2_type); + ped_file_system_type_unregister (&_amufs3_type); + ped_file_system_type_unregister (&_amufs4_type); + ped_file_system_type_unregister (&_amufs5_type); + ped_file_system_type_unregister (&_asfs_type); + ped_file_system_type_unregister (&_apfs1_type); + ped_file_system_type_unregister (&_apfs2_type); +} diff --git a/libparted/fs/amiga/affs.c b/libparted/fs/amiga/affs.c new file mode 100644 index 0000000..750eab2 --- /dev/null +++ b/libparted/fs/amiga/affs.c @@ -0,0 +1,292 @@ +/* + affs.c -- parted support for affs file systems + Copyright (C) 1998-2000, 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 <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#include "amiga.h" +#include "affs.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +static int +_affs_probe_root (uint32_t *block, int blocksize) { + int i; + uint32_t sum; + + if (PED_BE32_TO_CPU (block[0]) != 2) return 0; + if (PED_BE32_TO_CPU (block[128*blocksize-1]) != 1) return 0; + for (i = 0, sum = 0; i < 128*blocksize; i++) + sum += PED_BE32_TO_CPU (block[i]); + if (sum) return 0; + return 1; +} + +static PedGeometry* +_generic_affs_probe (PedGeometry* geom, uint32_t kind) +{ + uint32_t *block; + PedSector root, len, pos; + struct PartitionBlock * part; + int blocksize = 1, reserved = 2; + + PED_ASSERT (geom != NULL); + PED_ASSERT (geom->dev != NULL); + if (geom->dev->sector_size != 512) + return NULL; + /* Finds the blocksize and reserved values of the partition block */ + if (!(part = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate partition block\n"), __func__); + goto error_part; + } + if (amiga_find_part(geom, part) != NULL) { + reserved = PED_BE32_TO_CPU (part->de_Reserved); + reserved = reserved == 0 ? 1 : reserved; + blocksize = PED_BE32_TO_CPU (part->de_SizeBlock) + * PED_BE32_TO_CPU (part->de_SectorPerBlock) / 128; + } + free (part); + + /* Test boot block */ + if (!(block = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate block\n"), __func__); + goto error_block; + } + if (!ped_device_read (geom->dev, block, geom->start, blocksize)) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Couldn't read boot block %llu\n"), __func__, geom->start); + goto error; + } + if (PED_BE32_TO_CPU (block[0]) != kind) { + goto error; + } + + /* Find and test the root block */ + len = geom->length / blocksize - reserved; + pos = (len - 1) / 2; + root = geom->start + (pos + reserved) * blocksize; + + if (!ped_device_read (geom->dev, block, root, blocksize)) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Couldn't read root block %llu\n"), __func__, root); + goto error; + } + if (_affs_probe_root(block, blocksize) == 1) { + free (block); + return ped_geometry_duplicate (geom); + } + +error: + free (block); +error_block: +error_part: + return NULL; +} +static PedGeometry* +_affs0_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5300); +} +static PedGeometry* +_affs1_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5301); +} +static PedGeometry* +_affs2_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5302); +} +static PedGeometry* +_affs3_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5303); +} +static PedGeometry* +_affs4_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5304); +} +static PedGeometry* +_affs5_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5305); +} +static PedGeometry* +_affs6_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5306); +} +static PedGeometry* +_affs7_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x444f5307); +} +static PedGeometry* +_amufs_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x6d754653); +} +static PedGeometry* +_amufs0_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x6d754600); +} +static PedGeometry* +_amufs1_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x6d754601); +} +static PedGeometry* +_amufs2_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x6d754602); +} +static PedGeometry* +_amufs3_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x6d754603); +} +static PedGeometry* +_amufs4_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x6d754604); +} +static PedGeometry* +_amufs5_probe (PedGeometry* geom) { + return _generic_affs_probe (geom, 0x6d754605); +} + +static PedFileSystemOps _affs0_ops = { + probe: _affs0_probe, +}; +static PedFileSystemOps _affs1_ops = { + probe: _affs1_probe, +}; +static PedFileSystemOps _affs2_ops = { + probe: _affs2_probe, +}; +static PedFileSystemOps _affs3_ops = { + probe: _affs3_probe, +}; +static PedFileSystemOps _affs4_ops = { + probe: _affs4_probe, +}; +static PedFileSystemOps _affs5_ops = { + probe: _affs5_probe, +}; +static PedFileSystemOps _affs6_ops = { + probe: _affs6_probe, +}; +static PedFileSystemOps _affs7_ops = { + probe: _affs7_probe, +}; +static PedFileSystemOps _amufs_ops = { + probe: _amufs_probe, +}; +static PedFileSystemOps _amufs0_ops = { + probe: _amufs0_probe, +}; +static PedFileSystemOps _amufs1_ops = { + probe: _amufs1_probe, +}; +static PedFileSystemOps _amufs2_ops = { + probe: _amufs2_probe, +}; +static PedFileSystemOps _amufs3_ops = { + probe: _amufs3_probe, +}; +static PedFileSystemOps _amufs4_ops = { + probe: _amufs4_probe, +}; +static PedFileSystemOps _amufs5_ops = { + probe: _amufs5_probe, +}; + +PedFileSystemType _affs0_type = { + next: NULL, + ops: &_affs0_ops, + name: "affs0", +}; +PedFileSystemType _affs1_type = { + next: NULL, + ops: &_affs1_ops, + name: "affs1", +}; +PedFileSystemType _affs2_type = { + next: NULL, + ops: &_affs2_ops, + name: "affs2", +}; +PedFileSystemType _affs3_type = { + next: NULL, + ops: &_affs3_ops, + name: "affs3", +}; +PedFileSystemType _affs4_type = { + next: NULL, + ops: &_affs4_ops, + name: "affs4", +}; +PedFileSystemType _affs5_type = { + next: NULL, + ops: &_affs5_ops, + name: "affs5", +}; +PedFileSystemType _affs6_type = { + next: NULL, + ops: &_affs6_ops, + name: "affs6", +}; +PedFileSystemType _affs7_type = { + next: NULL, + ops: &_affs7_ops, + name: "affs7", +}; +PedFileSystemType _amufs_type = { + next: NULL, + ops: &_amufs_ops, + name: "amufs", +}; +PedFileSystemType _amufs0_type = { + next: NULL, + ops: &_amufs0_ops, + name: "amufs0", +}; +PedFileSystemType _amufs1_type = { + next: NULL, + ops: &_amufs1_ops, + name: "amufs1", +}; +PedFileSystemType _amufs2_type = { + next: NULL, + ops: &_amufs2_ops, + name: "amufs2", +}; +PedFileSystemType _amufs3_type = { + next: NULL, + ops: &_amufs3_ops, + name: "amufs3", +}; +PedFileSystemType _amufs4_type = { + next: NULL, + ops: &_amufs4_ops, + name: "amufs4", +}; +PedFileSystemType _amufs5_type = { + next: NULL, + ops: &_amufs5_ops, + name: "amufs5", +}; diff --git a/libparted/fs/amiga/affs.h b/libparted/fs/amiga/affs.h new file mode 100644 index 0000000..d1650f2 --- /dev/null +++ b/libparted/fs/amiga/affs.h @@ -0,0 +1,19 @@ + +/* + affs.h -- parted suppoer for affs filesystems header files + Copyright (C) 1998-2000, 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/>. +*/ diff --git a/libparted/fs/amiga/amiga.c b/libparted/fs/amiga/amiga.c new file mode 100644 index 0000000..1a909fc --- /dev/null +++ b/libparted/fs/amiga/amiga.c @@ -0,0 +1,351 @@ +/* + libparted/fs_amiga - amiga file system support. + Copyright (C) 2000-2001, 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/>. + + Contributor: Sven Luther <luther@debian.org> +*/ + +#include <config.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#include "amiga.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#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 * +_amiga_add_id (uint32_t id, struct AmigaIds *ids) { + struct AmigaIds *newid; + + if ((newid=ped_malloc(sizeof (struct AmigaIds)))==NULL) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate id list element\n"), __func__); + return 0; + } + newid->ID = id; + newid->next = ids; + return newid; +} + +void +_amiga_free_ids (struct AmigaIds *ids) { + struct AmigaIds *current, *next; + + for (current = ids; current != NULL; current = next) { + next = current->next; + free (current); + } +} +int +_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; +} + +#define AMIGA_RDB_NOT_FOUND ((uint32_t)0xffffffff) + +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)) + +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 AMIGA_MAX_PARTITIONS 128 +#define RDB_LOCATION_LIMIT 16 +#define RDSK(pos) ((struct RigidDiskBlock *)(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 (PedDevice *dev, struct AmigaBlock *blk, PedSector block, struct AmigaIds *ids) { + if (!ped_device_read (dev, blk, block, 1)) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Couldn't read block %llu\n"), __func__, block)) + { + case PED_EXCEPTION_CANCEL : + case PED_EXCEPTION_UNHANDLED : + default : + 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\n"), + __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 (dev, blk, block, 1)) { + switch (ped_exception_throw(PED_EXCEPTION_FATAL, + PED_EXCEPTION_CANCEL, + _("%s : Couldn't write block %d\n"), __func__, block)) + { + case PED_EXCEPTION_CANCEL : + case PED_EXCEPTION_UNHANDLED : + default : + return NULL; + } + } + /* FALLTHROUGH */ + case PED_EXCEPTION_IGNORE : + case PED_EXCEPTION_UNHANDLED : + default : + return blk; + } + } + return blk; +} + +static uint32_t +_amiga_find_rdb (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; +} + +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 */ +struct PartitionBlock * +amiga_find_part (PedGeometry *geom, struct PartitionBlock *part) +{ + struct RigidDiskBlock *rdb; + uint32_t partblock; + uint32_t partlist[AMIGA_MAX_PARTITIONS]; + int i; + + PED_ASSERT(geom!= NULL); + PED_ASSERT(geom->dev!= NULL); + + if (!(rdb = ped_malloc (PED_SECTOR_SIZE_DEFAULT))) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate disk_specific rdb block\n"), __func__)) + { + case PED_EXCEPTION_CANCEL : + case PED_EXCEPTION_UNHANDLED : + default : + return NULL; + } + } + if (_amiga_find_rdb (geom->dev, rdb) == AMIGA_RDB_NOT_FOUND) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Didn't find rdb block, should never happen\n"), __func__)) + { + case PED_EXCEPTION_CANCEL : + case PED_EXCEPTION_UNHANDLED : + default : + free(rdb); + return NULL; + } + } + + /* We initialize the hardblock free list to detect loops */ + for (i = 0; i < AMIGA_MAX_PARTITIONS; i++) partlist[i] = IDNAME_FREE; + + for (i = 1, partblock = PED_BE32_TO_CPU(rdb->rdb_PartitionList); + i < AMIGA_MAX_PARTITIONS && partblock != IDNAME_FREE; + i++, partblock = PED_BE32_TO_CPU(part->pb_Next)) + { + PedSector start, end; + PedSector cylblocks; + + /* Let's look for loops in the partition table */ + if (_amiga_loop_check(partblock, partlist, i)) { + free (rdb); + return NULL; + } + /* Let's read a partition block to get its geometry*/ + if (!ped_device_read (geom->dev, part, (PedSector)partblock, 1)) { + switch (ped_exception_throw(PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s : Failed to read partition block %llu\n"), + __func__, (PedSector)partblock)) + { + case PED_EXCEPTION_CANCEL : + case PED_EXCEPTION_UNHANDLED : + default : + free(rdb); + return NULL; + } + } + + /* Current block is not a Partition Block */ + if (part->pb_ID != IDNAME_PARTITION) { + free (rdb); + return NULL; + } + + /* Calculate the geometry of the partition */ + cylblocks = ((PedSector) PED_BE32_TO_CPU (part->de_Surfaces)) * + ((PedSector) PED_BE32_TO_CPU (part->de_BlocksPerTrack)); + start = ((PedSector) PED_BE32_TO_CPU (part->de_LowCyl)) * cylblocks; + end = ((((PedSector) PED_BE32_TO_CPU (part->de_HighCyl))+1) * (cylblocks))-1; + + /* And check if it is the one we are searching for */ + if (start == geom->start && end == geom->end) { + free (rdb); + return part; + } + } + + free (rdb); + return NULL; +} diff --git a/libparted/fs/amiga/amiga.h b/libparted/fs/amiga/amiga.h new file mode 100644 index 0000000..30f5b82 --- /dev/null +++ b/libparted/fs/amiga/amiga.h @@ -0,0 +1,70 @@ +/* + util.h -- amiga partition table headers. + Copyright (C) 1998-2000, 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/>. +*/ + +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 */ + uint8_t pb_DriveName[32]; /* Preferred DOS device name: BSTR form */ + uint32_t pb_Reserved2[15]; + uint32_t de_TableSize; /* Size of Environment vector */ + uint32_t de_SizeBlock; /* Size of the blocks in 32 bit words, usually 128 */ + uint32_t de_SecOrg; /* Not used; must be 0 */ + uint32_t de_Surfaces; /* Number of heads (surfaces) */ + uint32_t de_SectorPerBlock; /* Disk sectors per block, used with SizeBlock, usually 1 */ + 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) */ + +struct PartitionBlock * amiga_find_part (PedGeometry *geom, struct PartitionBlock *part); + +struct AmigaIds { + uint32_t ID; + struct AmigaIds *next; +}; + +struct AmigaIds * _amiga_add_id (uint32_t id, struct AmigaIds *ids); +void _amiga_free_ids (struct AmigaIds *ids); +int _amiga_id_in_list (uint32_t id, struct AmigaIds *ids) _GL_ATTRIBUTE_PURE; diff --git a/libparted/fs/amiga/apfs.c b/libparted/fs/amiga/apfs.c new file mode 100644 index 0000000..aeaa1f3 --- /dev/null +++ b/libparted/fs/amiga/apfs.c @@ -0,0 +1,128 @@ +/* + apfs.c -- parted support for apfs file systems + Copyright (C) 1998-2000, 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 <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#include "amiga.h" +#include "apfs.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +static int +_apfs_probe_root (uint32_t *block, uint32_t blocksize, uint32_t kind) { + if (PED_BE32_TO_CPU (block[0]) != kind) return 0; + return 1; +} + +static PedGeometry* +_generic_apfs_probe (PedGeometry* geom, uint32_t kind) +{ + uint32_t *block; + PedSector root; + struct PartitionBlock * part; + uint32_t blocksize = 1, reserved = 2; + + PED_ASSERT (geom != NULL); + PED_ASSERT (geom->dev != NULL); + if (geom->dev->sector_size != 512) + return NULL; + + /* Finds the blocksize and reserved values of the partition block */ + if (!(part = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate partition block\n"), __func__); + goto error_part; + } + if (amiga_find_part(geom, part) != NULL) { + reserved = PED_BE32_TO_CPU (part->de_Reserved); + blocksize = PED_BE32_TO_CPU (part->de_SizeBlock) + * PED_BE32_TO_CPU (part->de_SectorPerBlock) / 128; + } + free (part); + + /* Test boot block */ + if (!(block = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate block\n"), __func__); + goto error_block; + } + if (!ped_device_read (geom->dev, block, geom->start, blocksize)) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Couldn't read boot block %llu\n"), __func__, geom->start); + goto error; + } + if (PED_BE32_TO_CPU (block[0]) != kind) { + goto error; + } + + /* Find and test the root block */ + root = geom->start+reserved*blocksize; + if (!ped_device_read (geom->dev, block, root, blocksize)) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Couldn't read root block %llu\n"), __func__, root); + goto error; + } + if (_apfs_probe_root(block, blocksize, kind) == 1) { + free(block); + return ped_geometry_duplicate (geom); + } + +error: + free (block); +error_block: +error_part: + return NULL; +} + +static PedGeometry* +_apfs1_probe (PedGeometry* geom) { + return _generic_apfs_probe (geom, 0x50463101); +} + +static PedGeometry* +_apfs2_probe (PedGeometry* geom) { + return _generic_apfs_probe (geom, 0x50463102); +} + +static PedFileSystemOps _apfs1_ops = { + probe: _apfs1_probe, +}; +static PedFileSystemOps _apfs2_ops = { + probe: _apfs2_probe, +}; + +PedFileSystemType _apfs1_type = { + next: NULL, + ops: &_apfs1_ops, + name: "apfs1", +}; +PedFileSystemType _apfs2_type = { + next: NULL, + ops: &_apfs2_ops, + name: "apfs2", +}; diff --git a/libparted/fs/amiga/apfs.h b/libparted/fs/amiga/apfs.h new file mode 100644 index 0000000..972941b --- /dev/null +++ b/libparted/fs/amiga/apfs.h @@ -0,0 +1,18 @@ +/* + apfs.h -- parted support for apfs file systems header files + Copyright (C) 1998-2000, 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/>. +*/ diff --git a/libparted/fs/amiga/asfs.c b/libparted/fs/amiga/asfs.c new file mode 100644 index 0000000..c4c65e5 --- /dev/null +++ b/libparted/fs/amiga/asfs.c @@ -0,0 +1,130 @@ +/* + asfs.c -- parted asfs filesystem support + Copyright (C) 1998-2000, 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 <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> + +#include "amiga.h" +#include "asfs.h" + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +static int +_asfs_probe_root (PedGeometry *geom, uint32_t *block, int blocksize, PedSector root) { + int i, sum; + PedSector start, end; + + if (PED_BE32_TO_CPU (block[0]) != 0x53465300) return 0; + for (i = 0, sum = 1; i < 128*blocksize; i++) sum += PED_BE32_TO_CPU (block[i]); + if (sum != 0) return 0; + if (PED_BE32_TO_CPU (block[2]) * blocksize + geom->start != root) { + return 0; + } + start = ((((PedSector) PED_BE32_TO_CPU (block[8])) << 32) + + (PedSector) PED_BE32_TO_CPU (block[9])) / 512; + end = (((((PedSector) PED_BE32_TO_CPU (block[10])) << 32) + + (PedSector) PED_BE32_TO_CPU (block[11])) / 512) - 1; + if (start != geom->start || end != geom->end) return 0; + return 1; +} + +static PedGeometry* +_asfs_probe (PedGeometry* geom) +{ + uint32_t *block; + struct PartitionBlock * part; + int blocksize = 1; + PedSector root; + int found = 0; + + PED_ASSERT (geom != NULL); + PED_ASSERT (geom->dev != NULL); + if (geom->dev->sector_size != 512) + return NULL; + + /* Finds the blocksize of the partition block */ + if (!(part = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate partition block\n"), __func__); + goto error_part; + } + if (amiga_find_part(geom, part) != NULL) { + blocksize = PED_BE32_TO_CPU (part->de_SizeBlock) + * PED_BE32_TO_CPU (part->de_SectorPerBlock) / 128; + } + free (part); + + /* Test boot block */ + if (!(block = ped_malloc (PED_SECTOR_SIZE_DEFAULT*blocksize))) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Failed to allocate block\n"), __func__); + goto error_block; + } + root = geom->start; + if (!ped_device_read (geom->dev, block, root, blocksize)) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Couldn't read root block %llu\n"), __func__, root); + goto error; + } + if (PED_BE32_TO_CPU (block[0]) != 0x53465300) { + goto error; + } + + /* Find and test the root blocks */ + if (_asfs_probe_root(geom, block, blocksize, root)) { + found++; + } + root = geom->end - blocksize - (geom->length % blocksize) + 1; + if (!ped_device_read (geom->dev, block, root, 1)) { + ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("%s : Couldn't read root block %llu\n"), __func__, root); + goto error; + } + if (_asfs_probe_root(geom, block, blocksize, root)) { + found++; + } + if (found != 0) { + free (block); + return ped_geometry_duplicate (geom); + } + +error: + free (block); +error_block: +error_part: + return NULL; +} + +static PedFileSystemOps _asfs_ops = { + probe: _asfs_probe, +}; + +PedFileSystemType _asfs_type = { + next: NULL, + ops: &_asfs_ops, + name: "asfs", +}; diff --git a/libparted/fs/amiga/asfs.h b/libparted/fs/amiga/asfs.h new file mode 100644 index 0000000..2b70a94 --- /dev/null +++ b/libparted/fs/amiga/asfs.h @@ -0,0 +1,18 @@ +/* + asfs.h -- parted asfs filesystem support header files + Copyright (C) 1998-2000, 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/>. +*/ diff --git a/libparted/fs/btrfs/btrfs.c b/libparted/fs/btrfs/btrfs.c new file mode 100644 index 0000000..6eded8b --- /dev/null +++ b/libparted/fs/btrfs/btrfs.c @@ -0,0 +1,77 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2013-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/endian.h> + +/* Located 64k inside the partition (start of the first btrfs superblock) */ +#define BTRFS_MAGIC 0x4D5F53665248425FULL /* ascii _BHRfS_M, no null */ +#define BTRFS_CSUM_SIZE 32 +#define BTRFS_FSID_SIZE 16 + + +static PedGeometry* +btrfs_probe (PedGeometry* geom) +{ + union { + struct { + /* Just enough of the btrfs_super_block to get the magic */ + uint8_t csum[BTRFS_CSUM_SIZE]; + uint8_t fsid[BTRFS_FSID_SIZE]; + uint64_t bytenr; + uint64_t flags; + uint64_t magic; + } sb; + int8_t sector[8192]; + } buf; + PedSector offset = (64*1024)/geom->dev->sector_size; + + if (geom->length < offset+1) + return 0; + if (!ped_geometry_read (geom, &buf, offset, 1)) + return 0; + + if (PED_LE64_TO_CPU(buf.sb.magic) == BTRFS_MAGIC) { + return ped_geometry_new (geom->dev, geom->start, geom->length); + } + return NULL; +} + +static PedFileSystemOps btrfs_ops = { + probe: btrfs_probe, +}; + +static PedFileSystemType btrfs_type = { + next: NULL, + ops: &btrfs_ops, + name: "btrfs", +}; + +void +ped_file_system_btrfs_init () +{ + ped_file_system_type_register (&btrfs_type); +} + +void +ped_file_system_btrfs_done () +{ + ped_file_system_type_unregister (&btrfs_type); +} diff --git a/libparted/fs/ext2/ext2.h b/libparted/fs/ext2/ext2.h new file mode 100644 index 0000000..d23f63e --- /dev/null +++ b/libparted/fs/ext2/ext2.h @@ -0,0 +1,79 @@ +/* + ext2.h -- ext2 header + Copyright (C) 1998-2000, 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/>. +*/ + +#ifndef _EXT2_H +#define _EXT2_H + +#include <parted/parted.h> +#include <parted/debug.h> +#include <sys/types.h> + +#include <inttypes.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +typedef u_int32_t blk_t; + +#ifdef HAVE_LINUX_EXT2_FS_H__FAILS_TO_COMPILE +#include <linux/ext2_fs.h> +#else +#include "ext2_fs.h" +#endif + +struct ext2_fs +{ + struct ext2_dev_handle *devhandle; + + struct ext2_super_block sb; + struct ext2_group_desc *gd; + struct ext2_buffer_cache *bc; + int metadirty; /* 0:all sb&gd copies clean + 1:all sb&gd copies dirty + 2:only first sb&gd copy clean */ + + int dynamic_version; + int sparse; /* sparse superblocks */ + int has_journal; /* journal */ + int has_internal_journal; + + int blocksize; + int logsize; + blk_t adminblocks; + blk_t gdblocks; + blk_t itoffset; + blk_t inodeblocks; + int numgroups; + int r_frac; /* reserved % of blocks */ + + unsigned char *relocator_pool; + unsigned char *relocator_pool_end; + + int opt_debug; + int opt_safe; + int opt_verbose; + + void *journal; +}; + +#endif diff --git a/libparted/fs/ext2/ext2_fs.h b/libparted/fs/ext2/ext2_fs.h new file mode 100644 index 0000000..1eca7ab --- /dev/null +++ b/libparted/fs/ext2/ext2_fs.h @@ -0,0 +1,329 @@ +/* + * linux/include/linux/ext2_fs.h + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/include/linux/minix_fs.h + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * EXT2_*_*() convienience macros added by Andrew Clausen <clausen@gnu.org> + * Copyright (C) 2000, 2009-2014, 2019-2023 Free Software Foundation, Inc. + */ + +#ifndef _EXT2_FS_H +#define _EXT2_FS_H + +#include <parted/endian.h> +#include <stdint.h> + +/* + * The second extended file system constants/structures + */ + +#define EXT2_SUPER_MAGIC_CONST 0xEF53 +#define EXT2_MIN_BLOCK_SIZE 1024 +#define EXT2_NDIR_BLOCKS 12 +#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS +#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) +#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) +#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) +#define EXT2_VALID_FS 0x0001 +#define EXT2_ERROR_FS 0x0002 +#define EXT2_RESERVED_INODE_COUNT 11 + +/* + * Codes for operating systems + */ +#define EXT2_OS_LINUX 0 +#define EXT2_OS_HURD 1 +#define EXT2_OS_MASIX 2 +#define EXT2_OS_FREEBSD 3 +#define EXT2_OS_LITES 4 + +/* + * Feature set definitions + */ +#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 +#define EXT2_FEATURE_COMPAT_HAS_DIR_INDEX 0x0020 + +#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 +#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 +#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 + +#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 +#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 +#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 +#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 + +/* + * Special inodes numbers + */ +#define EXT2_BAD_INO 1 /* Bad blocks inode */ +#define EXT2_ROOT_INO 2 /* Root inode */ +#define EXT2_ACL_IDX_INO 3 /* ACL inode */ +#define EXT2_ACL_DATA_INO 4 /* ACL inode */ +#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +#define EXT2_FT_UNKNOWN 0 +#define EXT2_FT_REG_FILE 1 +#define EXT2_FT_DIR 2 +#define EXT2_FT_CHRDEV 3 +#define EXT2_FT_BLKDEV 4 +#define EXT2_FT_FIFO 5 +#define EXT2_FT_SOCK 6 +#define EXT2_FT_SYMLINK 7 + +/* + * Behaviour when detecting errors + */ +#define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ +#define EXT2_ERRORS_RO 2 /* Remount fs read-only */ +#define EXT2_ERRORS_PANIC 3 /* Panic */ +#define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE + +struct ext2_dir_entry_2 +{ + uint32_t inode; + uint16_t rec_len; + uint8_t name_len; + uint8_t file_type; + char name[255]; +}; + +struct ext2_group_desc +{ + uint32_t bg_block_bitmap; + uint32_t bg_inode_bitmap; + uint32_t bg_inode_table; + uint16_t bg_free_blocks_count; + uint16_t bg_free_inodes_count; + uint16_t bg_used_dirs_count; + uint16_t bg_pad; + uint32_t bg_reserved[3]; +}; + +struct ext2_inode +{ + uint16_t i_mode; /* File mode */ + uint16_t i_uid; /* Owner Uid */ + uint32_t i_size; /* Size in bytes */ + uint32_t i_atime; /* Access time */ + uint32_t i_ctime; /* Creation time */ + uint32_t i_mtime; /* Modification time */ + uint32_t i_dtime; /* Deletion Time */ + uint16_t i_gid; /* Group Id */ + uint16_t i_links_count; /* Links count */ + uint32_t i_blocks; /* Blocks count */ + uint32_t i_flags; /* File flags */ + union { + struct { + uint32_t l_i_reserved1; + } linux1; + struct { + uint32_t h_i_translator; + } hurd1; + struct { + uint32_t m_i_reserved1; + } masix1; + } osd1; /* OS dependent 1 */ + uint32_t i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ + uint32_t i_generation; /* File version (for NFS) */ + uint32_t i_file_acl; /* File ACL */ + uint32_t i_dir_acl; /* Directory ACL */ + uint32_t i_faddr; /* Fragment address */ + union { + struct { + uint8_t l_i_frag; /* Fragment number */ + uint8_t l_i_fsize; /* Fragment size */ + uint16_t i_pad1; + uint32_t l_i_reserved2[2]; + } linux2; + struct { + uint8_t h_i_frag; /* Fragment number */ + uint8_t h_i_fsize; /* Fragment size */ + uint16_t h_i_mode_high; + uint16_t h_i_uid_high; + uint16_t h_i_gid_high; + uint32_t h_i_author; + } hurd2; + struct { + uint8_t m_i_frag; /* Fragment number */ + uint8_t m_i_fsize; /* Fragment size */ + uint16_t m_pad1; + uint32_t m_i_reserved2[2]; + } masix2; + } osd2; /* OS dependent 2 */ +}; + +#define i_size_high i_dir_acl + +struct __attribute__ ((packed)) ext2_super_block +{ + uint32_t s_inodes_count; /* Inodes count */ + uint32_t s_blocks_count; /* Blocks count */ + uint32_t s_r_blocks_count; /* Reserved blocks count */ + uint32_t s_free_blocks_count; /* Free blocks count */ + uint32_t s_free_inodes_count; /* Free inodes count */ + uint32_t s_first_data_block; /* First Data Block */ + uint32_t s_log_block_size; /* Block size */ + int32_t s_log_frag_size; /* Fragment size */ + uint32_t s_blocks_per_group; /* # Blocks per group */ + uint32_t s_frags_per_group; /* # Fragments per group */ + uint32_t s_inodes_per_group; /* # Inodes per group */ + uint32_t s_mtime; /* Mount time */ + uint32_t s_wtime; /* Write time */ + uint16_t s_mnt_count; /* Mount count */ + int16_t s_max_mnt_count; /* Maximal mount count */ + uint16_t s_magic; /* Magic signature */ + uint16_t s_state; /* File system state */ + uint16_t s_errors; /* Behaviour when detecting errors */ + uint16_t s_minor_rev_level; /* minor revision level */ + uint32_t s_lastcheck; /* time of last check */ + uint32_t s_checkinterval; /* max. time between checks */ + uint32_t s_creator_os; /* OS */ + uint32_t s_rev_level; /* Revision level */ + uint16_t s_def_resuid; /* Default uid for reserved blocks */ + uint16_t s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the file system. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + uint32_t s_first_ino; /* First non-reserved inode */ + uint16_t s_inode_size; /* size of inode structure */ + uint16_t s_block_group_nr; /* block group # of this superblock */ + uint32_t s_feature_compat; /* compatible feature set */ + uint32_t s_feature_incompat; /* incompatible feature set */ + uint32_t s_feature_ro_compat; /* readonly-compatible feature set */ + uint8_t s_uuid[16]; /* 128-bit uuid for volume */ + char s_volume_name[16]; /* volume name */ + char s_last_mounted[64]; /* directory where last mounted */ + uint32_t s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_COMPAT_PREALLOC flag is on. + */ + uint8_t s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + uint8_t s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + uint16_t s_padding1; + /* + * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set. + */ + uint8_t s_journal_uuid[16]; /* uuid of journal superblock */ + uint32_t s_journal_inum; /* inode number of journal file */ + uint32_t s_journal_dev; /* device number of journal file */ + uint32_t s_last_orphan; /* start of list of inodes to delete */ + + uint32_t s_reserved[197]; /* Padding to the end of the block */ +}; + +#define EXT2_DIRENT_INODE(dir_ent) (PED_LE32_TO_CPU((dir_ent).inode)) +#define EXT2_DIRENT_REC_LEN(dir_ent) (PED_LE16_TO_CPU((dir_ent).rec_len)) +#define EXT2_DIRENT_NAME_LEN(dir_ent) ((dir_ent).name_len) +#define EXT2_DIRENT_FILE_TYPE(dir_ent) ((dir_ent).file_type) + +#define EXT2_GROUP_BLOCK_BITMAP(gd) (PED_LE32_TO_CPU((gd).bg_block_bitmap)) +#define EXT2_GROUP_INODE_BITMAP(gd) (PED_LE32_TO_CPU((gd).bg_inode_bitmap)) +#define EXT2_GROUP_INODE_TABLE(gd) (PED_LE32_TO_CPU((gd).bg_inode_table)) +#define EXT2_GROUP_FREE_BLOCKS_COUNT(gd) \ + (PED_LE16_TO_CPU((gd).bg_free_blocks_count)) +#define EXT2_GROUP_FREE_INODES_COUNT(gd) \ + (PED_LE16_TO_CPU((gd).bg_free_inodes_count)) +#define EXT2_GROUP_USED_DIRS_COUNT(gd) \ + (PED_LE16_TO_CPU((gd).bg_used_dirs_count)) + +#define EXT2_INODE_MODE(inode) (PED_LE16_TO_CPU((inode).i_mode)) +#define EXT2_INODE_UID(inode) (PED_LE16_TO_CPU((inode).i_uid)) +#define EXT2_INODE_SIZE(inode) \ + ((uint64_t) PED_LE32_TO_CPU((inode).i_size) \ + + ((uint64_t) PED_LE32_TO_CPU((inode).i_size_high) << 32)) +#define EXT2_INODE_ATIME(inode) (PED_LE32_TO_CPU((inode).i_atime)) +#define EXT2_INODE_CTIME(inode) (PED_LE32_TO_CPU((inode).i_ctime)) +#define EXT2_INODE_MTIME(inode) (PED_LE32_TO_CPU((inode).i_mtime)) +#define EXT2_INODE_DTIME(inode) (PED_LE32_TO_CPU((inode).i_dtime)) +#define EXT2_INODE_GID(inode) (PED_LE16_TO_CPU((inode).i_gid)) +#define EXT2_INODE_LINKS_COUNT(inode) (PED_LE16_TO_CPU((inode).i_links_count)) +#define EXT2_INODE_BLOCKS(inode) (PED_LE32_TO_CPU((inode).i_blocks)) +#define EXT2_INODE_FLAGS(inode) (PED_LE32_TO_CPU((inode).i_flags)) +#define EXT2_INODE_TRANSLATOR(inode) (PED_LE32_TO_CPU((inode).osd1.hurd1.h_i_translator)) +#define EXT2_INODE_BLOCK(inode, blk) (PED_LE32_TO_CPU((inode).i_block[blk])) + +#define EXT2_SUPER_INODES_COUNT(sb) (PED_LE32_TO_CPU((sb).s_inodes_count)) +#define EXT2_SUPER_BLOCKS_COUNT(sb) (PED_LE32_TO_CPU((sb).s_blocks_count)) +#define EXT2_SUPER_R_BLOCKS_COUNT(sb) (PED_LE32_TO_CPU((sb).s_r_blocks_count)) +#define EXT2_SUPER_FREE_BLOCKS_COUNT(sb) \ + (PED_LE32_TO_CPU((sb).s_free_blocks_count)) +#define EXT2_SUPER_FREE_INODES_COUNT(sb) \ + (PED_LE32_TO_CPU((sb).s_free_inodes_count)) +#define EXT2_SUPER_FIRST_DATA_BLOCK(sb) \ + (PED_LE32_TO_CPU((sb).s_first_data_block)) +#define EXT2_SUPER_LOG_BLOCK_SIZE(sb) (PED_LE32_TO_CPU((sb).s_log_block_size)) +#define EXT2_SUPER_LOG_FRAG_SIZE(sb) \ + ((int32_t) PED_LE32_TO_CPU((sb).s_log_frag_size)) +#define EXT2_SUPER_BLOCKS_PER_GROUP(sb) \ + (PED_LE32_TO_CPU((sb).s_blocks_per_group)) +#define EXT2_SUPER_FRAGS_PER_GROUP(sb) \ + (PED_LE32_TO_CPU((sb).s_frags_per_group)) +#define EXT2_SUPER_INODES_PER_GROUP(sb) \ + (PED_LE32_TO_CPU((sb).s_inodes_per_group)) +#define EXT2_SUPER_MTIME(sb) (PED_LE32_TO_CPU((sb).s_mtime)) +#define EXT2_SUPER_WTIME(sb) (PED_LE32_TO_CPU((sb).s_wtime)) +#define EXT2_SUPER_MNT_COUNT(sb) (PED_LE16_TO_CPU((sb).s_mnt_count)) +#define EXT2_SUPER_MAX_MNT_COUNT(sb) \ + ((int16_t) PED_LE16_TO_CPU((sb).s_max_mnt_count)) +#define EXT2_SUPER_MAGIC(sb) (PED_LE16_TO_CPU((sb).s_magic)) +#define EXT2_SUPER_STATE(sb) (PED_LE16_TO_CPU((sb).s_state)) +#define EXT2_SUPER_ERRORS(sb) (PED_LE16_TO_CPU((sb).s_errors)) +#define EXT2_SUPER_MINOR_REV_LEVEL(sb) \ + (PED_LE16_TO_CPU((sb).s_minor_rev_level)) +#define EXT2_SUPER_LASTCHECK(sb) (PED_LE32_TO_CPU((sb).s_lastcheck)) +#define EXT2_SUPER_CHECKINTERVAL(sb) (PED_LE32_TO_CPU((sb).s_checkinterval)) +#define EXT2_SUPER_CREATOR_OS(sb) (PED_LE32_TO_CPU((sb).s_creator_os)) +#define EXT2_SUPER_REV_LEVEL(sb) (PED_LE32_TO_CPU((sb).s_rev_level)) +#define EXT2_SUPER_DEF_RESUID(sb) (PED_LE16_TO_CPU((sb).s_def_resuid)) +#define EXT2_SUPER_DEF_RESGID(sb) (PED_LE16_TO_CPU((sb).s_def_resgid)) + +#define EXT2_SUPER_FIRST_INO(sb) (PED_LE32_TO_CPU((sb).s_first_ino)) +#define EXT2_SUPER_INODE_SIZE(sb) (PED_LE16_TO_CPU((sb).s_inode_size)) +#define EXT2_SUPER_BLOCK_GROUP_NR(sb) (PED_LE16_TO_CPU((sb).s_block_group_nr)) +#define EXT2_SUPER_FEATURE_COMPAT(sb) (PED_LE32_TO_CPU((sb).s_feature_compat)) +#define EXT2_SUPER_FEATURE_INCOMPAT(sb) \ + (PED_LE32_TO_CPU((sb).s_feature_incompat)) +#define EXT2_SUPER_FEATURE_RO_COMPAT(sb) \ + (PED_LE32_TO_CPU((sb).s_feature_ro_compat)) +#define EXT2_SUPER_UUID(sb) ((sb).s_uuid) +#define EXT2_SUPER_VOLUME_NAME(sb) ((sb).s_volume_name) +#define EXT2_SUPER_LAST_MOUNTED(sb) ((sb).s_last_mounted) +#define EXT2_SUPER_ALGORITHM_USAGE_BITMAP(sb) \ + (PED_LE32_TO_CPU((sb).s_algorithm_usage_bitmap)) + +#define EXT2_SUPER_JOURNAL_UUID(sb) ((sb).s_journal_uuid) +#define EXT2_SUPER_JOURNAL_INUM(sb) (PED_LE32_TO_CPU((sb).s_journal_inum)) +#define EXT2_SUPER_JOURNAL_DEV(sb) (PED_LE32_TO_CPU((sb).s_journal_dev)) +#define EXT2_SUPER_LAST_ORPHAN(sb) (PED_LE32_TO_CPU((sb).s_last_orphan)) + +#endif diff --git a/libparted/fs/ext2/interface.c b/libparted/fs/ext2/interface.c new file mode 100644 index 0000000..7e0b197 --- /dev/null +++ b/libparted/fs/ext2/interface.c @@ -0,0 +1,163 @@ +/* + interface.c -- parted binding glue to libext2resize + Copyright (C) 1998-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/>. +*/ + +/* VERSION: libext2resize 1.1.6 (by Lennert) + * merged 1.1.11 changes (by Andrew) + */ + +#include <config.h> + +#include <parted/parted.h> +#include "ext2.h" + +static PedFileSystemType _ext2_type; +static PedFileSystemType _ext3_type; + +struct ext2_dev_handle* ext2_make_dev_handle_from_parted_geometry(PedGeometry* geom); + +static PedGeometry* +_ext2_generic_probe (PedGeometry* geom, int expect_ext_ver) +{ + struct ext2_super_block *sb; + const int sectors = (4096 + geom->dev->sector_size - 1) / + geom->dev->sector_size; + uint8_t *buf = alloca (sectors * geom->dev->sector_size); + if (!ped_geometry_read(geom, buf, 0, sectors)) + return NULL; + sb = (struct ext2_super_block *)(buf+1024); + + if (EXT2_SUPER_MAGIC(*sb) == EXT2_SUPER_MAGIC_CONST) { + PedSector block_size = (EXT2_MIN_BLOCK_SIZE << (EXT2_SUPER_LOG_BLOCK_SIZE(*sb))) / geom->dev->sector_size; + PedSector block_count = EXT2_SUPER_BLOCKS_COUNT(*sb); + PedSector group_blocks = EXT2_SUPER_BLOCKS_PER_GROUP(*sb); + PedSector group_nr = EXT2_SUPER_BLOCK_GROUP_NR(*sb); + PedSector first_data_block = EXT2_SUPER_FIRST_DATA_BLOCK(*sb); + int version = EXT2_SUPER_REV_LEVEL(*sb); + int is_ext3 = 0; + int is_ext4 = 0; + + is_ext3 = (EXT2_SUPER_FEATURE_COMPAT (*sb) + & EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0; + if (is_ext3) { + is_ext4 = ((EXT2_SUPER_FEATURE_RO_COMPAT (*sb) + & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) + || (EXT2_SUPER_FEATURE_RO_COMPAT (*sb) + & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) + || (EXT2_SUPER_FEATURE_RO_COMPAT (*sb) + & EXT4_FEATURE_RO_COMPAT_DIR_NLINK) + || (EXT2_SUPER_FEATURE_INCOMPAT (*sb) + & EXT4_FEATURE_INCOMPAT_EXTENTS) + || (EXT2_SUPER_FEATURE_INCOMPAT (*sb) + & EXT4_FEATURE_INCOMPAT_64BIT) + || (EXT2_SUPER_FEATURE_INCOMPAT (*sb) + & EXT4_FEATURE_INCOMPAT_FLEX_BG)); + if (is_ext4) + is_ext3 = 0; + } + if (expect_ext_ver == 2 && (is_ext3 || is_ext4)) + return NULL; + if (expect_ext_ver == 3 && !is_ext3) + return NULL; + else if (expect_ext_ver == 4 && !is_ext4) + return NULL; + + if (version > 0 && group_nr > 0) { + PedSector start; + PedGeometry probe_geom; + + start = geom->start + - group_blocks * group_nr + - first_data_block; + + if (start < 0) + return NULL; + ped_geometry_init (&probe_geom, geom->dev, + start, block_count * block_size); + return _ext2_generic_probe (&probe_geom, + expect_ext_ver); + } else { + return ped_geometry_new (geom->dev, geom->start, + block_count * block_size); + } + } + + return NULL; +} + +static PedGeometry* +_ext2_probe (PedGeometry* geom) +{ + return _ext2_generic_probe (geom, 2); +} + +static PedGeometry* +_ext3_probe (PedGeometry* geom) +{ + return _ext2_generic_probe (geom, 3); +} + +static PedGeometry* +_ext4_probe (PedGeometry* geom) +{ + return _ext2_generic_probe (geom, 4); +} + +static PedFileSystemOps _ext2_ops = { + probe: _ext2_probe, +}; + +static PedFileSystemOps _ext3_ops = { + probe: _ext3_probe, +}; + +static PedFileSystemOps _ext4_ops = { + probe: _ext4_probe, +}; + +static PedFileSystemType _ext2_type = { + next: NULL, + ops: &_ext2_ops, + name: "ext2", +}; + +static PedFileSystemType _ext3_type = { + next: NULL, + ops: &_ext3_ops, + name: "ext3", +}; + +static PedFileSystemType _ext4_type = { + next: NULL, + ops: &_ext4_ops, + name: "ext4", +}; + +void ped_file_system_ext2_init () +{ + ped_file_system_type_register (&_ext2_type); + ped_file_system_type_register (&_ext3_type); + ped_file_system_type_register (&_ext4_type); +} + +void ped_file_system_ext2_done () +{ + ped_file_system_type_unregister (&_ext2_type); + ped_file_system_type_unregister (&_ext3_type); + ped_file_system_type_unregister (&_ext4_type); +} diff --git a/libparted/fs/f2fs/f2fs.c b/libparted/fs/f2fs/f2fs.c new file mode 100644 index 0000000..68ed092 --- /dev/null +++ b/libparted/fs/f2fs/f2fs.c @@ -0,0 +1,60 @@ +/* + libparted/fs/f2fs - Flash-Friendly File System + Copyright (C) 2020-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/endian.h> + +#include "f2fs.h" + +static PedGeometry* +f2fs_probe (PedGeometry* geom) +{ + struct f2fs_super_block *sb = alloca(geom->dev->sector_size); + + if (!ped_geometry_read (geom, sb, F2FS_SB_OFFSET, 1)) + return NULL; + + if (PED_LE32_TO_CPU(sb->magic) == F2FS_MAGIC) + return ped_geometry_new (geom->dev, geom->start, geom->length); + + return NULL; +} + +static PedFileSystemOps f2fs_ops = { + probe: f2fs_probe, +}; + +static PedFileSystemType f2fs_type = { + next: NULL, + ops: &f2fs_ops, + name: "f2fs", +}; + +void +ped_file_system_f2fs_init () +{ + ped_file_system_type_register (&f2fs_type); +} + +void +ped_file_system_f2fs_done () +{ + ped_file_system_type_unregister (&f2fs_type); +} diff --git a/libparted/fs/f2fs/f2fs.h b/libparted/fs/f2fs/f2fs.h new file mode 100644 index 0000000..9341337 --- /dev/null +++ b/libparted/fs/f2fs/f2fs.h @@ -0,0 +1,57 @@ +/* + libparted/fs/f2fs - Flash-Friendly File System + Copyright (C) 2020-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/>. +*/ +#ifndef _F2FS_H +#define _F2FS_H + +#define F2FS_MAGIC 0xF2F52010 +#define F2FS_MAX_VOLUME_NAME 512 +#define F2FS_SB_OFFSET 0x02 + +struct f2fs_super_block { + uint32_t magic; /* Magic Number */ + uint16_t major_ver; /* Major Version */ + uint16_t minor_ver; /* Minor Version */ + uint32_t log_sectorsize; /* log2 sector size in bytes */ + uint32_t log_sectors_per_block; /* log2 # of sectors per block */ + uint32_t log_blocksize; /* log2 block size in bytes */ + uint32_t log_blocks_per_seg; /* log2 # of blocks per segment */ + uint32_t segs_per_sec; /* # of segments per section */ + uint32_t secs_per_zone; /* # of sections per zone */ + uint32_t checksum_offset; /* checksum offset inside super block */ + uint64_t block_count; /* total # of user blocks */ + uint32_t section_count; /* total # of sections */ + uint32_t segment_count; /* total # of segments */ + uint32_t segment_count_ckpt; /* # of segments for checkpoint */ + uint32_t segment_count_sit; /* # of segments for SIT */ + uint32_t segment_count_nat; /* # of segments for NAT */ + uint32_t segment_count_ssa; /* # of segments for SSA */ + uint32_t segment_count_main; /* # of segments for main area */ + uint32_t segment0_blkaddr; /* start block address of segment 0 */ + uint32_t cp_blkaddr; /* start block address of checkpoint */ + uint32_t sit_blkaddr; /* start block address of SIT */ + uint32_t nat_blkaddr; /* start block address of NAT */ + uint32_t ssa_blkaddr; /* start block address of SSA */ + uint32_t main_blkaddr; /* start block address of main area */ + uint32_t root_ino; /* root inode number */ + uint32_t node_ino; /* node inode number */ + uint32_t meta_ino; /* meta inode number */ + uint8_t uuid[16]; /* 128-bit uuid for volume */ + uint16_t volume_name[F2FS_MAX_VOLUME_NAME]; /* volume name */ +} __attribute__((packed)); + +#endif diff --git a/libparted/fs/fat/bootsector.c b/libparted/fs/fat/bootsector.c new file mode 100644 index 0000000..f02685b --- /dev/null +++ b/libparted/fs/fat/bootsector.c @@ -0,0 +1,271 @@ +/* + libparted + Copyright (C) 1998-2000, 2002, 2004, 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 <config.h> +#include "fat.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +/* Reads in the boot sector (superblock), and does a minimum of sanity + * checking. The goals are: + * - to detect fat file systems, even if they are damaged [i.e. not + * return an error / throw an exception] + * - to fail detection if there's not enough information for + * fat_boot_sector_probe_type() to work (or possibly crash on a divide-by-zero) + */ +int +fat_boot_sector_read (FatBootSector** bsp, const PedGeometry *geom) +{ + PED_ASSERT (bsp != NULL); + PED_ASSERT (geom != NULL); + + if (!ped_geometry_read_alloc (geom, (void **)bsp, 0, 1)) + return 0; + FatBootSector *bs = *bsp; + + if (PED_LE16_TO_CPU (bs->boot_sign) != 0xAA55) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid signature for a FAT " + "file system.")); + return 0; + } + + if (!bs->sector_size + || PED_LE16_TO_CPU (bs->sector_size) % PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid sector size for a FAT " + "file system.")); + return 0; + } + + if (!bs->cluster_size) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid cluster size for a FAT " + "file system.")); + return 0; + } + + if (!bs->reserved) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid number of reserved " + "sectors for a FAT file system.")); + return 0; + } + + if (bs->fats < 1 || bs->fats > 4) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid number of FATs.")); + return 0; + } + + return 1; +} + +/* + Don't trust the FAT12, FAT16 or FAT32 label string. + */ +FatType _GL_ATTRIBUTE_PURE +fat_boot_sector_probe_type (const FatBootSector* bs, const PedGeometry* geom) +{ + PedSector logical_sector_size; + PedSector first_cluster_sector; + FatCluster cluster_count; + + if (!PED_LE16_TO_CPU (bs->dir_entries)) + return FAT_TYPE_FAT32; + + logical_sector_size = PED_LE16_TO_CPU (bs->sector_size) / 512; + + first_cluster_sector + = PED_LE16_TO_CPU (bs->reserved) * logical_sector_size + + 2 * PED_LE16_TO_CPU (bs->fat_length) * logical_sector_size + + PED_LE16_TO_CPU (bs->dir_entries) + / (512 / sizeof (FatDirEntry)); + cluster_count = (geom->length - first_cluster_sector) + / bs->cluster_size / logical_sector_size; + if (cluster_count > MAX_FAT12_CLUSTERS) + return FAT_TYPE_FAT16; + else + return FAT_TYPE_FAT12; +} + +static int +_fat_table_entry_size (FatType fat_type) +{ + switch (fat_type) { + case FAT_TYPE_FAT12: + return 2; /* FIXME: how? */ + + case FAT_TYPE_FAT16: + return 2; + + case FAT_TYPE_FAT32: + return 4; + } + + return 0; +} + +/* Analyses the boot sector, and sticks appropriate numbers in + fs->type_specific. + + Note: you need to subtract (2 * cluster_sectors) off cluster offset, + because the first cluster is number 2. (0 and 1 are not real clusters, + and referencing them is a bug) + */ +int +fat_boot_sector_analyse (FatBootSector* bs, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int fat_entry_size; + + PED_ASSERT (bs != NULL); + + fs_info->logical_sector_size = PED_LE16_TO_CPU (bs->sector_size) / 512; + + fs_info->sectors_per_track = PED_LE16_TO_CPU (bs->secs_track); + fs_info->heads = PED_LE16_TO_CPU (bs->heads); + if (fs_info->sectors_per_track < 1 || fs_info->sectors_per_track > 63 + || fs_info->heads < 1 || fs_info->heads > 255) { + PedCHSGeometry* bios_geom = &fs->geom->dev->bios_geom; + int cyl_count = 0; + + if (fs_info->heads > 0 && fs_info->sectors_per_track > 0) + cyl_count = fs->geom->dev->length / fs_info->heads + / fs_info->sectors_per_track; + + switch (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("The file system's CHS geometry is (%d, %d, %d), " + "which is invalid. The partition table's CHS " + "geometry is (%d, %d, %d)."), + cyl_count, fs_info->heads, fs_info->sectors_per_track, + bios_geom->cylinders, bios_geom->heads, + bios_geom->sectors)) { + + case PED_EXCEPTION_CANCEL: + return 0; + + case PED_EXCEPTION_IGNORE: + break; + + default: + break; + } + } + + if (bs->sectors) + fs_info->sector_count = PED_LE16_TO_CPU (bs->sectors) + * fs_info->logical_sector_size; + else + fs_info->sector_count = PED_LE32_TO_CPU (bs->sector_count) + * fs_info->logical_sector_size; + + fs_info->fat_table_count = bs->fats; + fs_info->root_dir_entry_count = PED_LE16_TO_CPU (bs->dir_entries); + fs_info->fat_offset = PED_LE16_TO_CPU (bs->reserved) + * fs_info->logical_sector_size; + fs_info->cluster_sectors = bs->cluster_size + * fs_info->logical_sector_size; + fs_info->cluster_size = fs_info->cluster_sectors * 512; + + if (fs_info->logical_sector_size == 0) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("FAT boot sector says logical sector size is 0. " + "This is weird. ")); + return 0; + } + if (fs_info->fat_table_count == 0) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("FAT boot sector says there are no FAT tables. This " + "is weird. ")); + return 0; + } + if (fs_info->cluster_sectors == 0) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("FAT boot sector says clusters are 0 sectors. This " + "is weird. ")); + return 0; + } + + fs_info->fat_type = fat_boot_sector_probe_type (bs, fs->geom); + if (fs_info->fat_type == FAT_TYPE_FAT12) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("File system is FAT12, which is unsupported.")); + return 0; + } + if (fs_info->fat_type == FAT_TYPE_FAT16) { + fs_info->fat_sectors = PED_LE16_TO_CPU (bs->fat_length) + * fs_info->logical_sector_size; + fs_info->serial_number + = PED_LE32_TO_CPU (bs->u.fat16.serial_number); + fs_info->root_cluster = 0; + fs_info->root_dir_offset + = fs_info->fat_offset + + fs_info->fat_sectors * fs_info->fat_table_count; + fs_info->root_dir_sector_count + = fs_info->root_dir_entry_count * sizeof (FatDirEntry) + / (512 * fs_info->logical_sector_size); + fs_info->cluster_offset + = fs_info->root_dir_offset + + fs_info->root_dir_sector_count; + } + if (fs_info->fat_type == FAT_TYPE_FAT32) { + fs_info->fat_sectors = PED_LE32_TO_CPU (bs->u.fat32.fat_length) + * fs_info->logical_sector_size; + fs_info->serial_number + = PED_LE32_TO_CPU (bs->u.fat32.serial_number); + fs_info->info_sector_offset + = PED_LE16_TO_CPU (fs_info->boot_sector->u.fat32.info_sector) + * fs_info->logical_sector_size; + fs_info->boot_sector_backup_offset + = PED_LE16_TO_CPU (fs_info->boot_sector->u.fat32.backup_sector) + * fs_info->logical_sector_size; + fs_info->root_cluster + = PED_LE32_TO_CPU (bs->u.fat32.root_dir_cluster); + fs_info->root_dir_offset = 0; + fs_info->root_dir_sector_count = 0; + fs_info->cluster_offset + = fs_info->fat_offset + + fs_info->fat_sectors * fs_info->fat_table_count; + } + + fs_info->cluster_count + = (fs_info->sector_count - fs_info->cluster_offset) + / fs_info->cluster_sectors; + + fat_entry_size = _fat_table_entry_size (fs_info->fat_type); + if (fs_info->cluster_count + 2 + > fs_info->fat_sectors * 512 / fat_entry_size) + fs_info->cluster_count + = fs_info->fat_sectors * 512 / fat_entry_size - 2; + + fs_info->dir_entries_per_cluster + = fs_info->cluster_size / sizeof (FatDirEntry); + return 1; +} diff --git a/libparted/fs/fat/bootsector.h b/libparted/fs/fat/bootsector.h new file mode 100644 index 0000000..328ba2f --- /dev/null +++ b/libparted/fs/fat/bootsector.h @@ -0,0 +1,126 @@ +/* + libparted + Copyright (C) 1998-2000, 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/>. +*/ + +#ifndef PED_FAT_BOOTSECTOR_H +#define PED_FAT_BOOTSECTOR_H + +typedef struct _FatBootSector FatBootSector; +typedef struct _FatInfoSector FatInfoSector; + +#include "fat.h" + +#define FAT32_INFO_MAGIC1 0x41615252 +#define FAT32_INFO_MAGIC2 0x61417272 +#define FAT32_INFO_MAGIC3 0xaa55 + +/* stolen from mkdosfs, by Dave Hudson */ + +#define FAT_BOOT_MESSAGE \ +"This partition does not have an operating system loader installed on it.\n\r"\ +"Press a key to reboot..." + +#define FAT_BOOT_JUMP "\xeb\x58\x90" /* jmp +5a */ + +#define FAT_BOOT_CODE "\x0e" /* push cs */ \ + "\x1f" /* pop ds */ \ + "\xbe\x74\x7e" /* mov si, offset message */ \ + /* write_msg_loop: */ \ + "\xac" /* lodsb */ \ + "\x22\xc0" /* and al, al */ \ + "\x74\x06" /* jz done (+8) */ \ + "\xb4\x0e" /* mov ah, 0x0e */ \ + "\xcd\x10" /* int 0x10 */ \ + "\xeb\xf5" /* jmp write_msg_loop */ \ + /* done: */ \ + "\xb4\x00" /* mov ah, 0x00 */ \ + "\xcd\x16" /* int 0x16 */ \ + "\xb4\x00" /* mov ah, 0x00 */ \ + "\xcd\x19" /* int 0x19 */ \ + "\xeb\xfe" /* jmp +0 - in case int 0x19 */ \ + /* doesn't work */ \ + /* message: */ \ + FAT_BOOT_MESSAGE + +#define FAT_BOOT_CODE_LENGTH 128 + +struct __attribute__ ((packed)) _FatBootSector { + uint8_t boot_jump[3]; /* 00: Boot strap short or near jump */ + uint8_t system_id[8]; /* 03: system name */ + uint16_t sector_size; /* 0b: bytes per logical sector */ + uint8_t cluster_size; /* 0d: sectors/cluster */ + uint16_t reserved; /* 0e: reserved sectors */ + uint8_t fats; /* 10: number of FATs */ + uint16_t dir_entries; /* 11: number of root directory entries */ + uint16_t sectors; /* 13: if 0, total_sect supersedes */ + uint8_t media; /* 15: media code */ + uint16_t fat_length; /* 16: sectors/FAT for FAT12/16 */ + uint16_t secs_track; /* 18: sectors per track */ + uint16_t heads; /* 1a: number of heads */ + uint32_t hidden; /* 1c: hidden sectors (partition start) */ + uint32_t sector_count; /* 20: no. of sectors (if sectors == 0) */ + + union __attribute__ ((packed)) { + /* FAT16 fields */ + struct __attribute__ ((packed)) { + uint8_t drive_num; /* 24: */ + uint8_t empty_1; /* 25: */ + uint8_t ext_signature; /* 26: always 0x29 */ + uint32_t serial_number; /* 27: */ + uint8_t volume_name [11]; /* 2b: */ + uint8_t fat_name [8]; /* 36: */ + uint8_t boot_code[448]; /* 3f: Boot code (or message) */ + } fat16; + /* FAT32 fields */ + struct __attribute__ ((packed)) { + uint32_t fat_length; /* 24: size of FAT in sectors */ + uint16_t flags; /* 28: bit8: fat mirroring, low4: active fat */ + uint16_t version; /* 2a: minor * 256 + major */ + uint32_t root_dir_cluster; /* 2c: */ + uint16_t info_sector; /* 30: */ + uint16_t backup_sector; /* 32: */ + uint8_t empty_1 [12]; /* 34: */ + uint16_t drive_num; /* 40: */ + uint8_t ext_signature; /* 42: always 0x29 */ + uint32_t serial_number; /* 43: */ + uint8_t volume_name [11]; /* 47: */ + uint8_t fat_name [8]; /* 52: */ + uint8_t boot_code[420]; /* 5a: Boot code (or message) */ + } fat32; + } u; + + uint16_t boot_sign; /* 1fe: always 0xAA55 */ +}; + +struct __attribute__ ((packed)) _FatInfoSector { + uint32_t signature_1; /* should be 0x41615252 */ + uint8_t unused [480]; + uint32_t signature_2; /* should be 0x61417272 */ + uint32_t free_clusters; + uint32_t next_cluster; /* most recently allocated cluster */ + uint8_t unused2 [0xe]; + uint16_t signature_3; /* should be 0xaa55 */ +}; + +int fat_boot_sector_read (FatBootSector** bs, const PedGeometry* geom); +FatType fat_boot_sector_probe_type (const FatBootSector* bs, + const PedGeometry* geom); +int fat_boot_sector_analyse (FatBootSector* bs, PedFileSystem* fs); + + +#endif /* PED_FAT_BOOTSECTOR_H */ diff --git a/libparted/fs/fat/count.h b/libparted/fs/fat/count.h new file mode 100644 index 0000000..bb7d6af --- /dev/null +++ b/libparted/fs/fat/count.h @@ -0,0 +1,46 @@ +/* + libparted + 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/>. +*/ + +#ifndef COUNT_H_INCLUDED +#define COUNT_H_INCLUDED + +typedef enum _FatClusterFlag FatClusterFlag; +typedef struct _FatClusterInfo FatClusterInfo; + +enum _FatClusterFlag { + FAT_FLAG_FREE=0, + FAT_FLAG_FILE=1, + FAT_FLAG_DIRECTORY=2, + FAT_FLAG_BAD=3 +}; + +struct __attribute__ ((packed)) _FatClusterInfo { + unsigned int units_used:6; /* 1 unit = cluster_size / 64 */ + FatClusterFlag flag:2; +}; + +extern int fat_collect_cluster_info (PedFileSystem *fs); +extern FatClusterFlag fat_get_cluster_flag (PedFileSystem* fs, + FatCluster cluster); +extern PedSector fat_get_cluster_usage (PedFileSystem* fs, FatCluster cluster); +extern FatClusterFlag fat_get_fragment_flag (PedFileSystem* fs, + FatFragment frag); +extern int fat_is_fragment_active (PedFileSystem* fs, FatFragment frag); + +#endif /* COUNT_H_INCLUDED */ diff --git a/libparted/fs/fat/fat.c b/libparted/fs/fat/fat.c new file mode 100644 index 0000000..c04f178 --- /dev/null +++ b/libparted/fs/fat/fat.c @@ -0,0 +1,162 @@ +/* + libparted + Copyright (C) 1998-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 <string.h> +#include <uuid/uuid.h> + +#include "fat.h" + +PedFileSystem* +fat_alloc (const PedGeometry* geom) +{ + PedFileSystem* fs; + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) + goto error; + + fs->type_specific = (FatSpecific*) ped_malloc (sizeof (FatSpecific)); + if (!fs->type_specific) + goto error_free_fs; + FatSpecific* fs_info = (FatSpecific*) fs->type_specific; + fs_info->boot_sector = NULL; + fs_info->info_sector = NULL; + fs->geom = ped_geometry_duplicate (geom); + if (!fs->geom) + goto error_free_type_specific; + + fs->checked = 0; + return fs; + +error_free_type_specific: + free (fs->type_specific); +error_free_fs: + free (fs); +error: + return NULL; +} + +void +fat_free (PedFileSystem* fs) +{ + FatSpecific* fs_info = (FatSpecific*) fs->type_specific; + free (fs_info->boot_sector); + ped_geometry_destroy (fs->geom); + free (fs->type_specific); + free (fs); +} + +PedGeometry* +fat_probe (PedGeometry* geom, FatType* fat_type) +{ + PedFileSystem* fs; + FatSpecific* fs_info; + PedGeometry* result; + + fs = fat_alloc (geom); + if (!fs) + goto error; + fs_info = (FatSpecific*) fs->type_specific; + + if (!fat_boot_sector_read (&fs_info->boot_sector, geom)) + goto error_free_fs; + if (!fat_boot_sector_analyse (fs_info->boot_sector, fs)) + goto error_free_fs; + + *fat_type = fs_info->fat_type; + result = ped_geometry_new (geom->dev, geom->start, + fs_info->sector_count); + + fat_free (fs); + return result; + +error_free_fs: + fat_free (fs); +error: + return NULL; +} + +PedGeometry* +fat_probe_fat16 (PedGeometry* geom) +{ + FatType fat_type; + PedGeometry* probed_geom = fat_probe (geom, &fat_type); + + if (probed_geom) { + if (fat_type == FAT_TYPE_FAT16) + return probed_geom; + ped_geometry_destroy (probed_geom); + } + return NULL; +} + +PedGeometry* +fat_probe_fat32 (PedGeometry* geom) +{ + FatType fat_type; + PedGeometry* probed_geom = fat_probe (geom, &fat_type); + + if (probed_geom) { + if (fat_type == FAT_TYPE_FAT32) + return probed_geom; + ped_geometry_destroy (probed_geom); + } + return NULL; +} + +static PedFileSystemOps fat16_ops = { + probe: fat_probe_fat16, +}; + +static PedFileSystemOps fat32_ops = { + probe: fat_probe_fat32, +}; + +PedFileSystemType fat16_type = { + next: NULL, + ops: &fat16_ops, + name: "fat16", +}; + +PedFileSystemType fat32_type = { + next: NULL, + ops: &fat32_ops, + name: "fat32", +}; + +void +ped_file_system_fat_init () +{ + if (sizeof (FatBootSector) != 512) { + ped_exception_throw (PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, + _("GNU Parted was miscompiled: the FAT boot sector " + "should be 512 bytes. FAT support will be disabled.")); + } else { + ped_file_system_type_register (&fat16_type); + ped_file_system_type_register (&fat32_type); + } +} + +void +ped_file_system_fat_done () +{ + ped_file_system_type_unregister (&fat16_type); + ped_file_system_type_unregister (&fat32_type); +} diff --git a/libparted/fs/fat/fat.h b/libparted/fs/fat/fat.h new file mode 100644 index 0000000..f5302d9 --- /dev/null +++ b/libparted/fs/fat/fat.h @@ -0,0 +1,163 @@ +/* + libparted + Copyright (C) 1998-2001, 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/>. +*/ + +#ifndef FAT_H_INCLUDED +#define FAT_H_INCLUDED + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#define BUFFER_SIZE 1024 /* buffer size in sectors (512 bytes) */ + +typedef uint32_t FatCluster; +typedef int32_t FatFragment; + +enum _FatType { + FAT_TYPE_FAT12, + FAT_TYPE_FAT16, + FAT_TYPE_FAT32 +}; +typedef enum _FatType FatType; + +typedef struct _FatSpecific FatSpecific; +typedef struct _FatDirEntry FatDirEntry; + +#include "bootsector.h" +#include "count.h" + +struct _FatTable { + void* table; + FatCluster size; + int raw_size; + + FatType fat_type; + FatCluster cluster_count; + FatCluster free_cluster_count; + FatCluster bad_cluster_count; + + FatCluster last_alloc; +}; +typedef struct _FatTable FatTable; + +struct __attribute__ ((packed)) _FatDirEntry { + char name[8]; + uint8_t extension[3]; + uint8_t attributes; + uint8_t is_upper_case_name; + uint8_t creation_time_low; /* milliseconds */ + uint16_t creation_time_high; + uint16_t creation_date; + uint16_t access_date; + uint16_t first_cluster_high; /* for FAT32 */ + uint16_t time; + uint16_t date; + uint16_t first_cluster; + uint32_t length; +}; + +struct _FatSpecific { + FatBootSector *boot_sector; /* structure of boot sector */ + FatInfoSector *info_sector; /* fat32-only information sector */ + + int logical_sector_size; /* illogical sector size :-) */ + PedSector sector_count; + + int sectors_per_track; /* BIOS CHS stuff (S) */ + int heads; /* BIOS CHS stuff (H) */ + + int cluster_size; + PedSector cluster_sectors; + FatCluster cluster_count; + int dir_entries_per_cluster; + + FatType fat_type; + int fat_table_count; + PedSector fat_sectors; + + uint32_t serial_number; + + PedSector info_sector_offset; /* FAT32 only */ + PedSector fat_offset; + PedSector root_dir_offset; /* non-FAT32 */ + PedSector cluster_offset; + PedSector boot_sector_backup_offset; + + FatCluster root_cluster; /* FAT32 only */ + int root_dir_entry_count; /* non-FAT32 */ + PedSector root_dir_sector_count; /* non-FAT32 */ + FatCluster total_dir_clusters; + + FatTable* fat; + FatClusterInfo* cluster_info; + + PedSector buffer_sectors; + char* buffer; + + int frag_size; + PedSector frag_sectors; + FatFragment frag_count; + FatFragment buffer_frags; + FatFragment cluster_frags; +}; + +#define FAT_SPECIFIC(fs) ((FatSpecific*) fs->type_specific) + +#define FAT_ROOT 0 + +#define DELETED_FLAG 0xe5 + +#define READONLY_ATTR 0x01 +#define HIDDEN_ATTR 0x02 +#define SYSTEM_ATTR 0x04 +#define VOLUME_LABEL_ATTR 0x08 +#define VFAT_ATTR 0x0f +#define DIRECTORY_ATTR 0x10 +#define ARCH_ATTR 0x20 + +#define MAX_FAT12_CLUSTERS 4086 +#define MAX_FAT16_CLUSTERS 65526 +#define MAX_FAT32_CLUSTERS 2000000 + +#define FAT_ROOT_DIR_ENTRY_COUNT 512 + +extern PedFileSystemType fat16_type; +extern PedFileSystemType fat32_type; + +extern void fat_print (const PedFileSystem* fs); + +extern PedFileSystem* fat_alloc (const PedGeometry* geom); +extern void fat_free (PedFileSystem* fs); +extern int fat_alloc_buffers (PedFileSystem* fs); + +extern int fat_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer); + +#endif /* FAT_H_INCLUDED */ diff --git a/libparted/fs/fsresize.sym b/libparted/fs/fsresize.sym new file mode 100644 index 0000000..86829d1 --- /dev/null +++ b/libparted/fs/fsresize.sym @@ -0,0 +1,13 @@ +# This is an LD linker script. +# Expose only the functions named below. + +LIBPARTED_FS_RESIZE_0.0.0 { + global: + ped_file_system_close; + ped_file_system_get_resize_constraint; + ped_file_system_open; + ped_file_system_resize; + + local: + *; +}; diff --git a/libparted/fs/hfs/DOC b/libparted/fs/hfs/DOC new file mode 100644 index 0000000..9ee8b4d --- /dev/null +++ b/libparted/fs/hfs/DOC @@ -0,0 +1,92 @@ +WARNING : Both HFS and HFS+ implementations of Linux 2.4 are buggy, at +least when used before or after this implementation. Some workarounds +are used in this implementation, but there can still be incompatibilities. +Try Linux 2.6 if you want to play with HFS(+) resizing (though some bugs +might also be there in 2.6, there is of course no warranty) + +--- + + Technical doc about Apple HFS and HFS+ file systems is available at : + * For HFS, section "Data Organization on Volumes", + "Chapter 2 - File Manager" + of the book "Inside Macintosh: Files" + http://developer.apple.com/documentation/mac/Files/Files-99.html + * For HFS+, "Technical Note TN1150", "HFS Plus Volume Format" + http://developer.apple.com/technotes/tn/tn1150.html + + Some useful HFS precisions concerning alignement, bit ordering, and + order of fields for extent key comparaisons are only in the HFS+ TN + + These Apple Creator Codes are reserved for us : + Shnk traP GP16 GnuP PH+x Xpnd Resz GP17 GP18 GP19 GP20 + +--- + +* Cache design * + +Versions before HFS Patch 15 were very slow when data relocation was needed, +because every extent to relocate involved scanning the whole file system, +looking for a reference to its physical position on the volume (this was a +dummy algorithm, I know :) + +HFS Patch 16 introduced a cache that allows to efficiently retrieve the place +of the reference in the file system given the physical position of an extent. +The cache is designed for : - efficiency + - scaling + - simplicity + - avoiding memory allocation while resizing + +This cache involves quite big worst case memory consumption, but without it +the time needed to complete the operation in the worst case would be huge +anyway (maybe several years...) so this isn't really an issue. The cache size +is nearly proportional to the number of files you have, or if you have very few +files, to the size of your volume, so worst cases situations occure when you +fill a drive with millions of < 4 ko files :p For this very special usage you +will just need a very special amount of RAM (on typical systems about +(FS size) / 256 )... On a more "normal" volume it's about +(# of files) * 20 bytes. With very few files it's about (FS Size) / 1024 / 256. + +At the beginning of the resize process, the cache is filed by scanning the FS. +The position of each extent is cut into 2 parts : high order is used as +an index into a table of pointer to a linked list which contains : +- the next ptr (sizeof struct *) +- the extent start (4 bytes) +- the extent size (4 bytes) +- number of BTree block or 0 if in prim (4 bytes) +- offset of the extent start reference + from the block beginning (2 bytes) +- sectors by BTree block, or + 1 for VH/MDB (1 byte) +- FS special file / primary structure + where the extent reference is stored (1 byte) + (3 bits for the extent index, 5 for + the actual ref) + + 0 : dont exists --- reserved + 1 : mdb / vh : catalog --- + 2 : mdb / vh : extent --- + 3 : vh : attributes X+- + 4 : vh : allocation X+- + 5 : vh : startup X+- + 6 : catalog --- + 7 : attributes X+- + 8 : extent (nothing to update) --- + 9 : extent (update catalog) --- + 10 : extent (update extent !?!) --- should not exist + 11 : extent (update attributes) X+- + 12 : extent (update allocation) X+- + 13 : extent (update startup) X+- reserved + 14 : vh : journal info block X+J + 15 : jib: journal X+J + 16 - 31 : --- + +mdb : Master Directory Block +vh : Volume Header +X+ : HFSX or HFS+ only +J : Journaled only + +Large amount of memory is allocated at once (first enough memory to fit +every files if there isn't any fragmentation +6.25%, then this value / 4, +if this wasn't enough). On a typical FS, the first allocation should be enough. + +--- diff --git a/libparted/fs/hfs/HISTORY b/libparted/fs/hfs/HISTORY new file mode 100644 index 0000000..5e138a6 --- /dev/null +++ b/libparted/fs/hfs/HISTORY @@ -0,0 +1,115 @@ +## modifications dd-mm-yyyy +---------------------- PATCH FOR PARTED 1.6.5 ---------------------------- + 1 initial revision 07-04-2003 + 2 one pass resizing, removal of debug info 08-04-2003 + 3 safe abort if resize failed, code cleanups, timer, 10-04-2003 + source file split, won't resize if not unmounted, + only relocate data if needed, minimize disk operations + 4 memory leaks removal, code cleanups, resize hfs+ code, 17-04-2003 + more checks, minor hfs resize bugfix, probe code + returns real geometry + 5 hfs+ resize bugfixes : 19-04-2003 + * fragmented fs could be corrupted + * VH wasn't written on error during alloc map writing + * attributes file could be corrupted + 6 Use PedSector to be able to use 2To+ HD 23-04-2003 + Minor probe bugfix, Cleanups, HFS+ Timer tuning, + 7 80 columns indentation 23-04-2003 + 8 Bugfix free blocks calculation in wrapper + (makes Mac OS boot !) 28-04-2003 +---------------------- PATCH FOR PARTED 1.6.6 ---------------------------- + 9 Don't destroy the file being worked on in case of + interruption of Parted 28-10-2003 +---------------------- PATCH FOR PARTED 1.6.10 --------------------------- +10 Regression tests, URL correction, In effect_move_extent : + corrected memory leak & corrected a bug in precondition checks + Added error messages, Check ped_alloc results + Use macro for test / set / clear in the allocation bitmap + Light probe correction, Check return value of get_empty_end + Moved dynamic memory allocation out of effect_move_extent + Check HFS+ version, Set implementation creator code + Check journal absence, Corrected a bug in HFS+ block number + calculation 24-04-2004 +--------------------- PATCH FOR PARTED 1.6.11 ---------------------------- +11-Some pointer dereference moved after non nul assertion + -Error messages for HFS(+) file IO + -Memory leak correction in hfs(plus)_read_bad_blocks + -Mark out of volume blocks as used + (improve compatibility with broken HFS+ Linux + implementation) + WARNING : this fix is not 100% tn1150 compatible : + "The allocation file may be larger than the minimum + number of bits required for the given volume size. + Any unused bits in the bitmap must be set to _zero_." + Anyway neither is the Linux implementation, nor was my + previous implementations + Maybe I should ask Apple to change the specifications + -HISTORY, DOC and TODO files 29-04-2004 +12 Corrected a bug in hfsplus_volume_resize : size of alloc + bitmap could be miscalculated 29-04-2004 +--------------------- PATCH FOR PARTED 1.6.12 ---------------------------- +13-Finally partial rewrite of *_search_move_* + Easier to maintain and prepare for extent search and + relocation algorithm changes for better ones. + -"An extent has not been relocated!" message now only when + relocation requested + -Slightly better and simpler relocation algorithm + -Update of Makefile.in and Makefile.am in fs_hfs + -Sign correction for some 8bits HFS integers + -Added the option --enable-hfs-extract-fs in 'configure' + -Added every ped_geometry_sync where needed + -Bugfix : "A root node does not need to exist + (if the tree is empty)." + - now handled correctly in btree_search + -Bugfix : failure wasn't detected in some cases + during 2 pass relocation (*_search_move_*) + -Bugfix : The extent key comparaison was done in a wrong order + and a pad field was used during the comparaison + -Bugfix : in hfs_file_find_sector and hfsplus_file_find_sector + the absolute position of a file sector could be + miscalculated in case of fragmentation, resulting + in potential data corruption, or various errors + -Bugfix : The end of the HFS bitmap compatibility block was + miscalculated ( (1<<16)/8 instead of (1<<16) ) + in hfs_resize + 07-09-2004 +--------------------- PATCH FOR PARTED 1.6.14 ---------------------------- +14 Port of Patch 13 for Parted 1.6.14 (update timestamps) + 08-09-2004 +--------------------- PATCH FOR PARTED 1.6.15 ---------------------------- +15-hfsplus_open : added a warning message if the "attributes" + special file exists + -hfsplus_open : added a test to check if the "allocation" + special file has been correctly opened + -optimisation of hfs+ block access : don't recalculate + the address of each sector, and avoid checking the cache if + obviously not useful + ( hfsplus_file_read && hfsplus_file_write + && hfsplus_file_find_extent && hfs_file_find_sector) + -cut the "hfs.c" file in several parts + -Bugfix: in hfsplus_do_move_primary, hfs_effect_move_extent + was called instead of hfsplus_effect_move_extent !!! + This could not produce data corruption, because of a welcome + ASSERT in *_effect_move_extent that would detect the bug :) + -Bugfix: in hfs_effect_move_extent, do + PED_ASSERT(*ptr_to_fblock <= *ptr_fblock, return -1); + instead of + PED_ASSERT(*ptr_to_fblock < *ptr_fblock, return -1); + and added that assertion to hfsplus_effect_move_extent + -Bugfix: bugs introduced in rewrite of hfsplus_file_read + && hfsplus_file_write : last sector was incorrectly detected + as out of file. + -Cache the extent references (speed improvement ?) + 23-09-2004 +16-Bugfix: in hfsplus_do_move (reloc_plus.c), case CR_BTREE_EXT_ATTR + incorrectly updated the cached part of priv_data->catalog_file + instead of priv_data->attributes_file + -Bugfix: in hfs_read_bad_blocks && hfsplus_read_bad_blocks, + now generate an error if file_ID or type mismatch after the + first pass + Also check return value of ped_malloc + -Bugfix: in hfsplus_btree_search, check return value of ped_malloc + 29-09-2004 +---------------- INTEGRATION IN PARTED 1.6.22 (cvs) ---------------------- +Futur changes will be described in ../../ChangeLog + 02-02-2005 diff --git a/libparted/fs/hfs/TODO b/libparted/fs/hfs/TODO new file mode 100644 index 0000000..6e408e3 --- /dev/null +++ b/libparted/fs/hfs/TODO @@ -0,0 +1,27 @@ +--- TODO --- + + * Continue to write regressions tests and try on 2.6 kernel -- (high) + * Fix timer progression calculation, according to the new + caching code -- (high) + * write doc, website, ... -- (high) + * Check block allocation in linux 2.4 and remove + compatibility code if possible -- (high) + + * In hfs(plus)_btree_search , use a static variable to detect + illegal recursion and abort in that case. (find the right + number of recursion before reporting bug) -- easy -- (medium) + * Finish the HFS Extractor -- (medium) + (add mdb & vh extraction, and maybe journal) + + * Write code to allow enlarging and moving HFS[+x] -- (low) + * Use a bitmap to internaly store the bad blocks -- (low) + * Less bitmap saves ? -- (low) + * Continue to change the relocation algorithm + for a better one :) -- (low) + +--- NOT todo --- + + * debug HFS(+) Linux implementation (block allocation for HFS+, + hard and sym links for HFS+, filename length for HFS, ...) -- (dont) + /// Linux 2.6 contains HFS(+) implementations with less bugs + /// Linux 2.4 should not be used anymore to access HFS(+) diff --git a/libparted/fs/hfs/hfs.c b/libparted/fs/hfs/hfs.c new file mode 100644 index 0000000..3684646 --- /dev/null +++ b/libparted/fs/hfs/hfs.c @@ -0,0 +1,92 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2003-2005, 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/>. +*/ + +/* + Author : Guillaume Knispel <k_guillaume@libertysurf.fr> + Report bug to <bug-parted@gnu.org> +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "probe.h" + +uint8_t* hfs_block = NULL; +uint8_t* hfsp_block = NULL; +unsigned hfs_block_count; +unsigned hfsp_block_count; + +static PedFileSystemOps hfs_ops = { + probe: hfs_probe, +}; + +static PedFileSystemOps hfsplus_ops = { + probe: hfsplus_probe, +}; + +static PedFileSystemOps hfsx_ops = { + probe: hfsx_probe, +}; + + +static PedFileSystemType hfs_type = { + next: NULL, + ops: &hfs_ops, + name: "hfs", +}; + +static PedFileSystemType hfsplus_type = { + next: NULL, + ops: &hfsplus_ops, + name: "hfs+", +}; + +static PedFileSystemType hfsx_type = { + next: NULL, + ops: &hfsx_ops, + name: "hfsx", +}; + +void +ped_file_system_hfs_init () +{ + ped_file_system_type_register (&hfs_type); + ped_file_system_type_register (&hfsplus_type); + ped_file_system_type_register (&hfsx_type); +} + +void +ped_file_system_hfs_done () +{ + ped_file_system_type_unregister (&hfs_type); + ped_file_system_type_unregister (&hfsplus_type); + ped_file_system_type_unregister (&hfsx_type); +} diff --git a/libparted/fs/hfs/hfs.h b/libparted/fs/hfs/hfs.h new file mode 100644 index 0000000..5b9138c --- /dev/null +++ b/libparted/fs/hfs/hfs.h @@ -0,0 +1,648 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2003-2005, 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/>. +*/ + +#ifndef _HFS_H +#define _HFS_H + +/* WARNING : bn is used 2 times in theses macro */ +/* so _never_ use side effect operators when using them */ +#define TST_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) & (1<<(7-((bn)&7)))) +#define SET_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) |= (1<<(7-((bn)&7)))) +#define CLR_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) &= ~(1<<(7-((bn)&7)))) + +/* Maximum number of blocks for the copy buffers */ +#define BLOCK_MAX_BUFF 256 +/* Maximum size of the copy buffers, in bytes */ +#define BYTES_MAX_BUFF 8388608 + +/* Apple Creator Codes follow */ +#define HFSP_IMPL_Shnk 0x53686e6b /* in use */ +#define HFSP_IMPL_Xpnd 0x58706e64 /* reserved */ +#define HFSP_IMPL_Resz 0x5265737a /* reserved */ +#define HFSP_IMPL_PHpx 0x50482b78 /* reserved */ +#define HFSP_IMPL_traP 0x74726150 /* reserved */ +#define HFSP_IMPL_GnuP 0x476e7550 /* reserved */ + +#define HFS_SIGNATURE 0x4244 /* 'BD' */ +#define HFSP_SIGNATURE 0x482B /* 'H+' */ +#define HFSX_SIGNATURE 0x4858 /* 'HX' */ + +#define HFSP_VERSION 4 +#define HFSX_VERSION 5 + +#define HFS_HARD_LOCK 7 +#define HFS_UNMOUNTED 8 +#define HFS_BAD_SPARED 9 +#define HFS_SOFT_LOCK 15 +#define HFSP_NO_CACHE 10 +#define HFSP_INCONSISTENT 11 +#define HFSP_REUSE_CNID 12 +#define HFSP_JOURNALED 13 + +#define HFS_IDX_NODE 0x00 +#define HFS_HDR_NODE 0x01 +#define HFS_MAP_NODE 0x02 +#define HFS_LEAF_NODE 0xFF + +#define HFS_FIRST_REC 0x0E +#define HFS_NSD_HD_REC 0x78 +#define HFS_MAP_REC 0xF8 + +#define HFS_DATA_FORK 0x00 +#define HFS_RES_FORK 0xFF + +#define HFS_CAT_DIR 0x01 +#define HFS_CAT_FILE 0x02 +#define HFS_CAT_DIR_TH 0x03 +#define HFS_CAT_FILE_TH 0x04 + +#define HFSP_ATTR_INLINE 0x10 +#define HFSP_ATTR_FORK 0x20 +#define HFSP_ATTR_EXTENTS 0x30 + +#define HFS_ROOT_PAR_ID 0x01 +#define HFS_ROOT_DIR_ID 0x02 +#define HFS_XTENT_ID 0x03 +#define HFS_CATALOG_ID 0x04 +#define HFS_BAD_BLOCK_ID 0x05 +#define HFSP_ALLOC_ID 0x06 +#define HFSP_STARTUP_ID 0x07 +#define HFSP_ATTRIB_ID 0x08 +#define HFSP_BOGUS_ID 0x0F +#define HFSP_FIRST_AV_ID 0x10 + +#define HFSJ_JOURN_IN_FS 0x00 +#define HFSJ_JOURN_OTHER_DEV 0x01 +#define HFSJ_JOURN_NEED_INIT 0x02 + +#define HFSJ_HEADER_MAGIC 0x4a4e4c78 +#define HFSJ_ENDIAN_MAGIC 0x12345678 + +#define HFSX_CASE_FOLDING 0xCF /* case insensitive HFSX */ +#define HFSX_BINARY_COMPARE 0xBC /* case sensitive HFSX */ + +#define HFS_EXT_NB 3 +#define HFSP_EXT_NB 8 + +/* Define the filenames used by the FS extractor */ +#ifdef HFS_EXTRACT_FS + +#define HFS_MDB_FILENAME "mdb.hfs" +#define HFS_CATALOG_FILENAME "catalog.hfs" +#define HFS_EXTENTS_FILENAME "extents.hfs" +#define HFS_BITMAP_FILENAME "bitmap.hfs" + +#define HFSP_VH_FILENAME "vh.hfsplus" +#define HFSP_CATALOG_FILENAME "catalog.hfsplus" +#define HFSP_EXTENTS_FILENAME "extents.hfsplus" +#define HFSP_BITMAP_FILENAME "bitmap.hfsplus" +#define HFSP_ATTRIB_FILENAME "attributes.hfsplus" +#define HFSP_STARTUP_FILENAME "startup.hfsplus" + +#endif /* HFS_EXTRACT_FS */ + + + +/* ----------------------------------- */ +/* -- HFS DATA STRUCTURES -- */ +/* ----------------------------------- */ + +/* Extent descriptor */ +struct __attribute__ ((packed)) _HfsExtDescriptor { + uint16_t start_block; + uint16_t block_count; +}; +typedef struct _HfsExtDescriptor HfsExtDescriptor; +typedef HfsExtDescriptor HfsExtDataRec[HFS_EXT_NB]; + +/* Volume header */ +struct __attribute__ ((packed)) _HfsMasterDirectoryBlock { + uint16_t signature; + uint32_t create_date; + uint32_t modify_date; + uint16_t volume_attributes; + uint16_t files_in_root; + uint16_t volume_bitmap_block; /* in sectors */ + uint16_t next_allocation; + uint16_t total_blocks; + uint32_t block_size; /* in bytes */ + uint32_t def_clump_size; /* in bytes */ + uint16_t start_block; /* in sectors */ + uint32_t next_free_node; + uint16_t free_blocks; + uint8_t name_length; + char name[27]; + uint32_t backup_date; + uint16_t backup_number; + uint32_t write_count; + uint32_t extents_clump; + uint32_t catalog_clump; + uint16_t dirs_in_root; + uint32_t file_count; + uint32_t dir_count; + uint32_t finder_info[8]; + union __attribute__ ((packed)) { + struct __attribute__ ((packed)) { + uint16_t volume_cache_size; /* in blocks */ + uint16_t bitmap_cache_size; /* in blocks */ + uint16_t common_cache_size; /* in blocks */ + } legacy; + struct __attribute__ ((packed)) { + uint16_t signature; + HfsExtDescriptor location; + } embedded; + } old_new; + uint32_t extents_file_size; /* in bytes, block size multiple */ + HfsExtDataRec extents_file_rec; + uint32_t catalog_file_size; /* in bytes, block size multiple */ + HfsExtDataRec catalog_file_rec; +}; +typedef struct _HfsMasterDirectoryBlock HfsMasterDirectoryBlock; + +/* B*-Tree Node Descriptor */ +struct __attribute__ ((packed)) _HfsNodeDescriptor { + uint32_t next; + uint32_t previous; + int8_t type; + uint8_t height; + uint16_t rec_nb; + uint16_t reserved; +}; +typedef struct _HfsNodeDescriptor HfsNodeDescriptor; + +/* Header record of a whole B*-Tree */ +struct __attribute__ ((packed)) _HfsHeaderRecord { + uint16_t depth; + uint32_t root_node; + uint32_t leaf_records; + uint32_t first_leaf_node; + uint32_t last_leaf_node; + uint16_t node_size; + uint16_t max_key_len; + uint32_t total_nodes; + uint32_t free_nodes; + int8_t reserved[76]; +}; +typedef struct _HfsHeaderRecord HfsHeaderRecord; + +/* Catalog key for B*-Tree lookup in the catalog file */ +struct __attribute__ ((packed)) _HfsCatalogKey { + uint8_t key_length; /* length of the key without key_length */ + uint8_t reserved; + uint32_t parent_ID; + uint8_t name_length; + char name[31]; /* in fact physicaly 1 upto 31 */ +}; +typedef struct _HfsCatalogKey HfsCatalogKey; + +/* Extents overflow key for B*-Tree lookup */ +struct __attribute__ ((packed)) _HfsExtentKey { + uint8_t key_length; /* length of the key without key_length */ + uint8_t type; /* data or ressource fork */ + uint32_t file_ID; + uint16_t start; +}; +typedef struct _HfsExtentKey HfsExtentKey; + +/* Catalog subdata case directory */ +struct __attribute__ ((packed)) _HfsDir { + uint16_t flags; + uint16_t valence; /* number of files in this directory */ + uint32_t dir_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + int8_t DInfo[16]; /* used by Finder, handle as reserved */ + int8_t DXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t reserved[4]; +}; +typedef struct _HfsDir HfsDir; + +/* Catalog subdata case file */ +struct __attribute__ ((packed)) _HfsFile { + int8_t flags; + int8_t type; /* should be 0 */ + int8_t FInfo[16]; /* used by Finder, handle as reserved */ + uint32_t file_ID; + uint16_t data_start_block; + uint32_t data_sz_byte; + uint32_t data_sz_block; + uint16_t res_start_block; + uint32_t res_sz_byte; + uint32_t res_sz_block; + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + int8_t FXInfo[16]; /* used by Finder, handle as reserved */ + uint16_t clump_size; + HfsExtDataRec extents_data; + HfsExtDataRec extents_res; + uint32_t reserved; +}; +typedef struct _HfsFile HfsFile; + +/* Catalog subdata case directory thread */ +struct __attribute__ ((packed)) _HfsDirTh { + uint32_t reserved[2]; + uint32_t parent_ID; + int8_t name_length; + char name[31]; +}; +typedef struct _HfsDirTh HfsDirTh; + +/* Catalog subdata case file thread */ +typedef struct _HfsDirTh HfsFileTh; /* same as directory thread */ + +/* Catalog data */ +struct __attribute__ ((packed)) _HfsCatalog { + int8_t type; + int8_t reserved; + union { + HfsDir dir; + HfsFile file; + HfsDirTh dir_th; + HfsFileTh file_th; + } sel; +}; +typedef struct _HfsCatalog HfsCatalog; + + + +/* ------------------------------------ */ +/* -- HFS+ DATA STRUCTURES -- */ +/* ------------------------------------ */ + +/* documented since 2004 in tn1150 */ +struct __attribute__ ((packed)) _HfsPPerms { + uint32_t owner_ID; + uint32_t group_ID; + uint32_t permissions; + uint32_t special_devices; +}; +typedef struct _HfsPPerms HfsPPerms; + +/* HFS+ extent descriptor*/ +struct __attribute__ ((packed)) _HfsPExtDescriptor { + uint32_t start_block; + uint32_t block_count; +}; +typedef struct _HfsPExtDescriptor HfsPExtDescriptor; +typedef HfsPExtDescriptor HfsPExtDataRec[HFSP_EXT_NB]; + +/* HFS+ fork data structure */ +struct __attribute__ ((packed)) _HfsPForkData { + uint64_t logical_size; + uint32_t clump_size; + uint32_t total_blocks; + HfsPExtDataRec extents; +}; +typedef struct _HfsPForkData HfsPForkData; + +/* HFS+ catalog node ID */ +typedef uint32_t HfsPNodeID; + +/* HFS+ file names */ +typedef uint16_t unichar; +struct __attribute__ ((packed)) _HfsPUniStr255 { + uint16_t length; + unichar unicode[255]; /* 1 upto 255 */ +}; +typedef struct _HfsPUniStr255 HfsPUniStr255; + +/* HFS+ volume header */ +struct __attribute__ ((packed)) _HfsPVolumeHeader { + uint16_t signature; + uint16_t version; + uint32_t attributes; + uint32_t last_mounted_version; + uint32_t journal_info_block; + + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + uint32_t checked_date; + + uint32_t file_count; + uint32_t dir_count; + + uint32_t block_size; + uint32_t total_blocks; + uint32_t free_blocks; + + uint32_t next_allocation; + uint32_t res_clump_size; + uint32_t data_clump_size; + HfsPNodeID next_catalog_ID; + + uint32_t write_count; + uint64_t encodings_bitmap; + + uint8_t finder_info[32]; + + HfsPForkData allocation_file; + HfsPForkData extents_file; + HfsPForkData catalog_file; + HfsPForkData attributes_file; + HfsPForkData startup_file; +}; +typedef struct _HfsPVolumeHeader HfsPVolumeHeader; + +/* HFS+ B-Tree Node Descriptor. Same as HFS btree. */ +struct __attribute__ ((packed)) _HfsPNodeDescriptor { + uint32_t next; + uint32_t previous; + int8_t type; + uint8_t height; + uint16_t rec_nb; + uint16_t reserved; +}; +typedef struct _HfsPNodeDescriptor HfsPNodeDescriptor; + +/* Header record of a whole HFS+ B-Tree. */ +struct __attribute__ ((packed)) _HfsPHeaderRecord { + uint16_t depth; + uint32_t root_node; + uint32_t leaf_records; + uint32_t first_leaf_node; + uint32_t last_leaf_node; + uint16_t node_size; + uint16_t max_key_len; + uint32_t total_nodes; + uint32_t free_nodes; /* same as hfs btree until here */ + uint16_t reserved1; + + uint32_t clump_size; + uint8_t btree_type; /* must be 0 for HFS+ B-Tree */ + uint8_t key_compare_type; /* hfsx => 0xCF = case folding */ + /* 0xBC = binary compare */ + /* otherwise, reserved */ + uint32_t attributes; + uint32_t reserved3[16]; +}; +typedef struct _HfsPHeaderRecord HfsPHeaderRecord; + +/* Catalog key for B-Tree lookup in the HFS+ catalog file */ +struct __attribute__ ((packed)) _HfsPCatalogKey { + uint16_t key_length; + HfsPNodeID parent_ID; + HfsPUniStr255 node_name; +}; +typedef struct _HfsPCatalogKey HfsPCatalogKey; + +/* HFS+ catalog subdata case dir */ +struct __attribute__ ((packed)) _HfsPDir { + uint16_t flags; + uint32_t valence; + HfsPNodeID dir_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t attrib_mod_date; + uint32_t access_date; + uint32_t backup_date; + HfsPPerms permissions; + int8_t DInfo[16]; /* used by Finder, handle as reserved */ + int8_t DXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t text_encoding; + uint32_t reserved; +}; +typedef struct _HfsPDir HfsPDir; + +/* HFS+ catalog subdata case file */ +struct __attribute__ ((packed)) _HfsPFile { + uint16_t flags; + uint32_t reserved1; + HfsPNodeID file_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t attrib_mod_date; + uint32_t access_date; + uint32_t backup_date; + HfsPPerms permissions; + int8_t FInfo[16]; /* used by Finder, handle as reserved */ + int8_t FXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t text_encoding; + uint32_t reserved2; + + HfsPForkData data_fork; + HfsPForkData res_fork; +}; +typedef struct _HfsPFile HfsPFile; + +/* HFS+ catalog subdata case thread */ +struct __attribute__ ((packed)) _HfsPThread { + int16_t reserved; + HfsPNodeID parent_ID; + HfsPUniStr255 node_name; +}; +typedef struct _HfsPThread HfsPDirTh; +typedef struct _HfsPThread HfsPFileTh; + +/* HFS+ Catalog leaf data */ +struct __attribute__ ((packed)) _HfsPCatalog { + int16_t type; + union { + HfsPDir dir; + HfsPFile file; + HfsPDirTh dir_th; + HfsPFileTh file_th; + } sel; +}; +typedef struct _HfsPCatalog HfsPCatalog; + +/* HFS+ extents file key */ +struct __attribute__ ((packed)) _HfsPExtentKey { + uint16_t key_length; + uint8_t type; + uint8_t pad; + HfsPNodeID file_ID; + uint32_t start; +}; +typedef struct _HfsPExtentKey HfsPExtentKey; + +/* extent file data is HfsPExtDataRec */ + +/* Fork data attribute file */ +struct __attribute__ ((packed)) _HfsPForkDataAttr { + uint32_t record_type; + uint32_t reserved; + union __attribute__ ((packed)) { + HfsPForkData fork; + HfsPExtDataRec extents; + } fork_res; +}; +typedef struct _HfsPForkDataAttr HfsPForkDataAttr; + + +/* ----------- Journal data structures ----------- */ + +/* Info block : stored in a block # defined in the VH */ +struct __attribute__ ((packed)) _HfsJJournalInfoBlock { + uint32_t flags; + uint32_t device_signature[8]; + uint64_t offset; + uint64_t size; + uint32_t reserved[32]; +}; +typedef struct _HfsJJournalInfoBlock HfsJJournalInfoBlock; + +struct __attribute__ ((packed)) _HfsJJournalHeader { + uint32_t magic; + uint32_t endian; + uint64_t start; + uint64_t end; + uint64_t size; + uint32_t blhdr_size; + uint32_t checksum; + uint32_t jhdr_size; +}; +typedef struct _HfsJJournalHeader HfsJJournalHeader; + +struct __attribute__ ((packed)) _HfsJBlockInfo { + uint64_t bnum; /* sector number */ + uint32_t bsize; /* size in bytes */ + uint32_t next; +}; +typedef struct _HfsJBlockInfo HfsJBlockInfo; + +struct __attribute__ ((packed)) _HfsJBlockListHeader { + uint16_t max_blocks; /* reserved */ + uint16_t num_blocks; + uint32_t bytes_used; + uint32_t checksum; + uint32_t pad; + HfsJBlockInfo binfo[]; +}; +typedef struct _HfsJBlockListHeader HfsJBlockListHeader; + + + +/* ---------------------------------------- */ +/* -- INTERNAL DATA STRUCTURES -- */ +/* ---------------------------------------- */ + +/* Data of an opened HFS file */ +struct _HfsPrivateFile { + PedSector sect_nb; + PedFileSystem* fs; + uint32_t CNID; /* disk order (BE) */ + HfsExtDataRec first; /* disk order (BE) */ + HfsExtDataRec cache; /* disk order (BE) */ + uint16_t start_cache; /* CPU order */ +}; +typedef struct _HfsPrivateFile HfsPrivateFile; + +/* To store bad block list */ +struct _HfsPrivateLinkExtent { + HfsExtDescriptor extent; + struct _HfsPrivateLinkExtent* next; +}; +typedef struct _HfsPrivateLinkExtent HfsPrivateLinkExtent; + +/* HFS Filesystem specific data */ +struct _HfsPrivateFSData { + uint8_t alloc_map[(1<<16) / 8]; + HfsMasterDirectoryBlock* mdb; + HfsPrivateFile* extent_file; + HfsPrivateFile* catalog_file; + HfsPrivateLinkExtent* bad_blocks_xtent_list; + unsigned int bad_blocks_xtent_nb; + char bad_blocks_loaded; +}; +typedef struct _HfsPrivateFSData HfsPrivateFSData; + +/* Generic btree key */ +struct __attribute__ ((packed)) _HfsPrivateGenericKey { + uint8_t key_length; + uint8_t key_content[1]; /* we use 1 as a minimum size */ +}; +typedef struct _HfsPrivateGenericKey HfsPrivateGenericKey; + +/* ----- HFS+ ----- */ + +/* Data of an opened HFS file */ +struct _HfsPPrivateFile { + PedSector sect_nb; + PedFileSystem* fs; + HfsPNodeID CNID; /* disk order (BE) */ + HfsPExtDataRec first; /* disk order (BE) */ + HfsPExtDataRec cache; /* disk order (BE) */ + uint32_t start_cache; /* CPU order */ +}; +typedef struct _HfsPPrivateFile HfsPPrivateFile; + +struct _HfsPPrivateExtent { + PedSector start_sector; + PedSector sector_count; +}; +typedef struct _HfsPPrivateExtent HfsPPrivateExtent; + +/* To store bad block list */ +struct _HfsPPrivateLinkExtent { + HfsPExtDescriptor extent; + struct _HfsPPrivateLinkExtent* next; +}; +typedef struct _HfsPPrivateLinkExtent HfsPPrivateLinkExtent; + +/* HFS+ file system specific data */ +struct _HfsPPrivateFSData { + PedFileSystem* wrapper; /* NULL if hfs+ is not embedded */ + PedGeometry* plus_geom; /* Geometry of HFS+ _volume_ */ + uint8_t* alloc_map; + uint8_t* dirty_alloc_map; + HfsPVolumeHeader* vh; + HfsPPrivateFile* extents_file; + HfsPPrivateFile* catalog_file; + HfsPPrivateFile* attributes_file; + HfsPPrivateFile* allocation_file; + HfsPPrivateLinkExtent* bad_blocks_xtent_list; + uint32_t jib_start_block; + uint32_t jl_start_block; + unsigned int bad_blocks_xtent_nb; + char bad_blocks_loaded; + char free_geom; /* 1 = plus_geom must be freed */ +}; +typedef struct _HfsPPrivateFSData HfsPPrivateFSData; + +/* Generic + btree key */ +struct __attribute__ ((packed)) _HfsPPrivateGenericKey { + uint16_t key_length; + uint8_t key_content[1]; /* we use 1 as a minimum size */ +}; +typedef struct _HfsPPrivateGenericKey HfsPPrivateGenericKey; + +/* ---- common ---- */ + +/* node and lead record reference for a BTree search */ +struct _HfsCPrivateLeafRec { + unsigned int node_size; /* in sectors */ + unsigned int node_number; + unsigned int record_pos; + unsigned int record_number; +}; +typedef struct _HfsCPrivateLeafRec HfsCPrivateLeafRec; + +extern uint8_t* hfs_block; +extern uint8_t* hfsp_block; +extern unsigned hfs_block_count; +extern unsigned hfsp_block_count; + +#endif /* _HFS_H */ diff --git a/libparted/fs/hfs/probe.c b/libparted/fs/hfs/probe.c new file mode 100644 index 0000000..d02ca28 --- /dev/null +++ b/libparted/fs/hfs/probe.c @@ -0,0 +1,238 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 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 <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" + +#include "probe.h" + +int +hfsc_can_use_geom (PedGeometry* geom) +{ + PedDevice* dev; + + dev = geom->dev; + PED_ASSERT (geom != NULL); + PED_ASSERT (dev != NULL); + + if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Parted can't use HFS file systems on disks " + "with a sector size not equal to %d bytes."), + (int)PED_SECTOR_SIZE_DEFAULT ); + return 0; + } + + return 1; +} + +/* Probe an HFS volume, detecting it even if +it is in fact a wrapper to an HFS+ volume */ +/* Used by hfsplus_probe and hfs_probe */ +PedGeometry* +hfs_and_wrapper_probe (PedGeometry* geom) +{ + HfsMasterDirectoryBlock *mdb; + PedGeometry* geom_ret; + PedSector search, max; + + PED_ASSERT (geom != NULL); + PED_ASSERT (hfsc_can_use_geom (geom)); + + const int sectors = ((3 * 512) + geom->dev->sector_size - 1) / + geom->dev->sector_size; + char * buf = alloca (sectors * geom->dev->sector_size); + + mdb = (HfsMasterDirectoryBlock *)(buf+1024); + + /* is 5 an intelligent value ? */ + if ((geom->length < 5) + || (!ped_geometry_read (geom, buf, 0, sectors)) + || (mdb->signature != PED_CPU_TO_BE16 (HFS_SIGNATURE)) ) + return NULL; + + search = ((PedSector) PED_BE16_TO_CPU (mdb->start_block) + + ((PedSector) PED_BE16_TO_CPU (mdb->total_blocks) + * (PED_BE32_TO_CPU (mdb->block_size) / geom->dev->sector_size))); + max = search + (PED_BE32_TO_CPU (mdb->block_size) / geom->dev->sector_size); + if ((search < 0) + || !(geom_ret = ped_geometry_new (geom->dev, geom->start, search + 2))) + return NULL; + + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (mdb->signature == PED_CPU_TO_BE16 (HFS_SIGNATURE)) + return geom_ret; + } + + ped_geometry_destroy (geom_ret); + return NULL; +} + +PedGeometry* +hfsplus_probe (PedGeometry* geom) +{ + PedGeometry* geom_ret; + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + + PED_ASSERT (geom != NULL); + + if (!hfsc_can_use_geom (geom)) + return NULL; + + if ((geom_ret = hfs_and_wrapper_probe(geom))) { + /* HFS+ is embedded in an HFS volume ? */ + HfsMasterDirectoryBlock *mdb; + mdb = (HfsMasterDirectoryBlock *) buf; + + if (!ped_geometry_read (geom, buf, 2, 1) + || (mdb->old_new.embedded.signature + != PED_CPU_TO_BE16 (HFSP_SIGNATURE))) { + ped_geometry_destroy (geom_ret); + return NULL; + } else + return geom_ret; + } else { + /* This is a standalone HFS+ volume ? */ + PedSector search, max; + HfsPVolumeHeader *vh; + vh = (HfsPVolumeHeader *) buf; + + if ((geom->length < 5) + || !ped_geometry_read (geom, buf, 2, 1) + || (vh->signature != PED_CPU_TO_BE16 (HFSP_SIGNATURE))) + return NULL; + + /* Correct range is indeed [ blocks*sz-2;(blocs+1)*sz-2 ( */ + /* But previous versions of my implementation used to */ + /* assume range is [(blocks-1)*sz-1;(blocks*sz) ( */ + /* (blocks-1)*sz-1 has to be scanned last, because */ + /* it can belong to a regular file */ + max = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) + 1) + * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ) + - 2; + search = max - 2 * ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ) + 2; + if ((search < 0) + || !(geom_ret = ped_geometry_new (geom->dev, geom->start, + search + 2))) + return NULL; + + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, + search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (vh->signature == PED_CPU_TO_BE16 (HFSP_SIGNATURE)) + return geom_ret; + } + search = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) - 1) + * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ) + - 1; + if ((search < 0) + || !ped_geometry_set (geom_ret, geom_ret->start, + search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1) + || vh->signature != PED_CPU_TO_BE16 (HFSP_SIGNATURE)) { + ped_geometry_destroy (geom_ret); + return NULL; + } else + return geom_ret; + } +} + +PedGeometry* +hfs_probe (PedGeometry* geom) +{ + PedGeometry* geom_base; + PedGeometry* geom_plus = NULL; + + PED_ASSERT (geom != NULL); + + if (!hfsc_can_use_geom (geom)) + return NULL; + + if ((geom_base = hfs_and_wrapper_probe(geom)) + && (!(geom_plus = hfsplus_probe(geom_base)))) + return geom_base; + else { + if (geom_base) ped_geometry_destroy (geom_base); + if (geom_plus) ped_geometry_destroy (geom_plus); + return NULL; + } +} + +PedGeometry* +hfsx_probe (PedGeometry* geom) +{ + PedGeometry* geom_ret; + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedSector search, max; + HfsPVolumeHeader *vh = (HfsPVolumeHeader *) buf; + + PED_ASSERT (geom != NULL); + + if (!hfsc_can_use_geom (geom)) + return NULL; + + if ((geom->length < 5) + || !ped_geometry_read (geom, buf, 2, 1) + || (vh->signature != PED_CPU_TO_BE16 (HFSX_SIGNATURE))) + return NULL; + + /* unlike the hfs+ code, which should be kept compatible + with my old previous implementations, we only care here + about legal alternate VH positions, like TN1150 describes them */ + max = ((PedSector) PED_BE32_TO_CPU (vh->total_blocks) + 1) + * ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ) + - 2; + search = max - ( PED_BE32_TO_CPU (vh->block_size) / PED_SECTOR_SIZE_DEFAULT ); + if ((search < 0) + || !(geom_ret = ped_geometry_new (geom->dev, geom->start, + search + 2))) + return NULL; + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, + search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (vh->signature == PED_CPU_TO_BE16 (HFSX_SIGNATURE)) + return geom_ret; + } + + ped_geometry_destroy (geom_ret); + return NULL; +} diff --git a/libparted/fs/hfs/probe.h b/libparted/fs/hfs/probe.h new file mode 100644 index 0000000..29ce880 --- /dev/null +++ b/libparted/fs/hfs/probe.h @@ -0,0 +1,44 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 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/>. +*/ + +#ifndef _PROBE_H +#define _PROBE_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfsc_can_use_geom (PedGeometry* geom); + +PedGeometry* +hfs_and_wrapper_probe (PedGeometry* geom); + +PedGeometry* +hfsplus_probe (PedGeometry* geom); + +PedGeometry* +hfs_probe (PedGeometry* geom); + +PedGeometry* +hfsx_probe (PedGeometry* geom); + +#endif /* _PROBE_H */ diff --git a/libparted/fs/jfs/jfs.c b/libparted/fs/jfs/jfs.c new file mode 100644 index 0000000..f00bd9f --- /dev/null +++ b/libparted/fs/jfs/jfs.c @@ -0,0 +1,80 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2001, 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 <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> + +#define _JFS_UTILITY +#include "jfs_types.h" +#include "jfs_superblock.h" + +#define JFS_SUPER_OFFSET 32768 + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +static PedGeometry* +jfs_probe (PedGeometry* geom) +{ + struct superblock *sb = alloca (geom->dev->sector_size); + + if (geom->length * geom->dev->sector_size < JFS_SUPER_OFFSET) + return NULL; + if (!ped_geometry_read (geom, sb, JFS_SUPER_OFFSET / geom->dev->sector_size, 1)) + return NULL; + + if (strncmp (sb->s_magic, JFS_MAGIC, 4) == 0) { + PedSector block_size = PED_LE32_TO_CPU (sb->s_pbsize); + PedSector block_count = PED_LE64_TO_CPU (sb->s_size); + /* apparently jfs is retarded and always claims 512 byte + sectors, with the block count as a multiple of that */ + return ped_geometry_new (geom->dev, geom->start, + block_size * block_count / geom->dev->sector_size); + } else { + return NULL; + } +} + +static PedFileSystemOps jfs_ops = { + probe: jfs_probe, +}; + +static PedFileSystemType jfs_type = { + next: NULL, + ops: &jfs_ops, + name: "jfs", +}; + +void +ped_file_system_jfs_init () +{ + ped_file_system_type_register (&jfs_type); +} + +void +ped_file_system_jfs_done () +{ + ped_file_system_type_unregister (&jfs_type); +} diff --git a/libparted/fs/jfs/jfs_superblock.h b/libparted/fs/jfs/jfs_superblock.h new file mode 100644 index 0000000..ea13dfb --- /dev/null +++ b/libparted/fs/jfs/jfs_superblock.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 _H_JFS_SUPERBLOCK +#define _H_JFS_SUPERBLOCK +/* + * jfs_superblock.h + */ + +/* + * make the magic number something a human could read + */ +#define JFS_MAGIC "JFS1" /* Magic word: Version 1 */ + +#define JFS_VERSION 1 /* Version number: Version 1 */ + +#define LV_NAME_SIZE 11 /* MUST BE 11 for OS/2 boot sector */ + +/* + * aggregate superblock + * + * The name superblock is too close to super_block, so the name has been + * changed to jfs_superblock. The utilities are still using the old name. + */ +#ifdef _JFS_UTILITY +struct superblock +#else +struct jfs_superblock +#endif +{ + char s_magic[4]; /* 4: magic number */ + u32 s_version; /* 4: version number */ + + s64 s_size; /* 8: aggregate size in hardware/LVM blocks; + * VFS: number of blocks + */ + s32 s_bsize; /* 4: aggregate block size in bytes; + * VFS: fragment size + */ + s16 s_l2bsize; /* 2: log2 of s_bsize */ + s16 s_l2bfactor; /* 2: log2(s_bsize/hardware block size) */ + s32 s_pbsize; /* 4: hardware/LVM block size in bytes */ + s16 s_l2pbsize; /* 2: log2 of s_pbsize */ + s16 pad; /* 2: padding necessary for alignment */ + + u32 s_agsize; /* 4: allocation group size in aggr. blocks */ + + u32 s_flag; /* 4: aggregate attributes: + * see jfs_filsys.h + */ + u32 s_state; /* 4: mount/unmount/recovery state: + * see jfs_filsys.h + */ + s32 s_compress; /* 4: > 0 if data compression */ + + pxd_t s_ait2; /* 8: first extent of secondary + * aggregate inode table + */ + + pxd_t s_aim2; /* 8: first extent of secondary + * aggregate inode map + */ + u32 s_logdev; /* 4: device address of log */ + s32 s_logserial; /* 4: log serial number at aggregate mount */ + pxd_t s_logpxd; /* 8: inline log extent */ + + pxd_t s_fsckpxd; /* 8: inline fsck work space extent */ + + struct timestruc_t s_time; /* 8: time last updated */ + + s32 s_fsckloglen; /* 4: Number of file system blocks reserved for + * the fsck service log. + * N.B. These blocks are divided among the + * versions kept. This is not a per + * version size. + * N.B. These blocks are included in the + * length field of s_fsckpxd. + */ + s8 s_fscklog; /* 1: which fsck service log is most recent + * 0 => no service log data yet + * 1 => the first one + * 2 => the 2nd one + */ + char s_fpack[11]; /* 11: file system volume name + * N.B. This must be 11 bytes to + * conform with the OS/2 BootSector + * requirements + */ + + /* extendfs() parameter under s_state & FM_EXTENDFS */ + s64 s_xsize; /* 8: extendfs s_size */ + pxd_t s_xfsckpxd; /* 8: extendfs fsckpxd */ + pxd_t s_xlogpxd; /* 8: extendfs logpxd */ + /* - 128 byte boundary - */ + + /* + * DFS VFS support (preliminary) + */ + char s_attach; /* 1: VFS: flag: set when aggregate is attached + */ + u8 rsrvd4[7]; /* 7: reserved - set to 0 */ + + u64 totalUsable; /* 8: VFS: total of 1K blocks which are + * available to "normal" (non-root) users. + */ + u64 minFree; /* 8: VFS: # of 1K blocks held in reserve for + * exclusive use of root. This value can be 0, + * and if it is then totalUsable will be equal + * to # of blocks in aggregate. I believe this + * means that minFree + totalUsable = # blocks. + * In that case, we don't need to store both + * totalUsable and minFree since we can compute + * one from the other. I would guess minFree + * would be the one we should store, and + * totalUsable would be the one we should + * compute. (Just a guess...) + */ + + u64 realFree; /* 8: VFS: # of free 1K blocks can be used by + * "normal" users. It may be this is something + * we should compute when asked for instead of + * storing in the superblock. I don't know how + * often this information is needed. + */ + /* + * graffiti area + */ +}; + +#endif /*_H_JFS_SUPERBLOCK */ diff --git a/libparted/fs/jfs/jfs_types.h b/libparted/fs/jfs/jfs_types.h new file mode 100644 index 0000000..0366ebd --- /dev/null +++ b/libparted/fs/jfs/jfs_types.h @@ -0,0 +1,527 @@ +/* + * Copyright (c) International Business Machines Corp., 2000 + * + * 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 _H_JFS_TYPES +#define _H_JFS_TYPES + +/* + * jfs_types.h: + * + * basic type/utility definitions + * + * note: this header file must be the 1st include file + * of JFS include list in all JFS .c file. + */ + +#ifdef _JFS_UTILITY +/* this is defined in asm/byteorder.h for i386, but + * is NOT defined in asm/byteorder.h for ppc (non-kernel). + * Until that is changed, we'll define it here. */ +#define __BYTEORDER_HAS_U64__ + +#include <sys/types.h> +//#include <asm/byteorder.h> +typedef unsigned short UniChar; +#else +#include <linux/types.h> +#include <linux/jfs_fs.h> +#include <linux/nls.h> + +#ifndef _ULS_UNICHAR_DEFINED +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)) +typedef wchar_t UniChar; +#else +typedef unsigned short UniChar; +#endif +#define _ULS_UNICHAR_DEFINED +#endif +#endif +/* #include "endian24.h" */ + +/* + * primitive types + */ +#ifdef _JFS_UTILITY +typedef int8_t s8; +typedef uint8_t u8; +typedef int16_t s16; +typedef uint16_t u16; +typedef int32_t s32; +typedef uint32_t u32; +typedef int64_t s64; +typedef uint64_t u64; + +#ifndef _UINT_TYPES + /* unicode includes also define these */ +typedef u16 uint16; +typedef u32 uint32; +#define _UINT_TYPES +#endif + +typedef s8 int8; +typedef u8 uint8; +typedef s16 int16; +typedef s32 int32; +typedef s64 int64; +typedef u64 uint64; + +#endif /* _JFS_UTILITY */ +/* + * Holdovers from OS/2. Try to get away from using these altogether. + */ +typedef unsigned long ULONG; +typedef unsigned short USHORT; +typedef unsigned char UCHAR; +typedef void *PVOID; +#define MAXPATHLEN 255 + + +/* + * Almost identical to Linux's timespec, but not quite + */ +struct timestruc_t { + u32 tv_sec; + u32 tv_nsec; +}; + +/* + * handy + */ +#undef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#undef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#undef ROUNDUP +#define ROUNDUP(x, y) ( ((x) + ((y) - 1)) & ~((y) - 1) ) + +#define LEFTMOSTONE 0x80000000 +#define HIGHORDER 0x80000000u /* high order bit on */ +#define ONES 0xffffffffu /* all bit on */ + +typedef int boolean_t; +#define TRUE 1 +#define FALSE 0 + +/* + * logical xd (lxd) + */ +typedef struct { + unsigned len:24; + unsigned off1:8; + u32 off2; +} lxd_t; + +/* lxd_t field construction */ +#define LXDlength(lxd, length32) ( (lxd)->len = length32 ) +#define LXDoffset(lxd, offset64)\ +{\ + (lxd)->off1 = ((s64)offset64) >> 32;\ + (lxd)->off2 = (offset64) & 0xffffffff;\ +} + +/* lxd_t field extraction */ +#define lengthLXD(lxd) ( (lxd)->len ) +#define offsetLXD(lxd)\ + ( ((s64)((lxd)->off1)) << 32 | (lxd)->off2 ) + +/* lxd list */ +typedef struct { + s16 maxnlxd; + s16 nlxd; + lxd_t *lxd; +} lxdlist_t; + +/* + * physical xd (pxd) + */ +typedef struct { + unsigned len:24; + unsigned addr1:8; + u32 addr2; +} pxd_t; + +/* xd_t field construction */ + +#define PXDlength(pxd, length32) ((pxd)->len = __cpu_to_le24(length32)) +#define PXDaddress(pxd, address64)\ +{\ + (pxd)->addr1 = ((s64)address64) >> 32;\ + (pxd)->addr2 = __cpu_to_le32((address64) & 0xffffffff);\ +} + +/* xd_t field extraction */ +#define lengthPXD(pxd) __le24_to_cpu((pxd)->len) +#define addressPXD(pxd)\ + ( ((s64)((pxd)->addr1)) << 32 | __le32_to_cpu((pxd)->addr2)) + +/* pxd list */ +typedef struct { + s16 maxnpxd; + s16 npxd; + pxd_t pxd[8]; +} pxdlist_t; + + +/* + * data extent descriptor (dxd) + */ +typedef struct { + unsigned flag:8; /* 1: flags */ + unsigned rsrvd:24; /* 3: */ + u32 size; /* 4: size in byte */ + unsigned len:24; /* 3: length in unit of fsblksize */ + unsigned addr1:8; /* 1: address in unit of fsblksize */ + u32 addr2; /* 4: address in unit of fsblksize */ +} dxd_t; /* - 16 - */ + +/* dxd_t flags */ +#define DXD_INDEX 0x80 /* B+-tree index */ +#define DXD_INLINE 0x40 /* in-line data extent */ +#define DXD_EXTENT 0x20 /* out-of-line single extent */ +#define DXD_FILE 0x10 /* out-of-line file (inode) */ +#define DXD_CORRUPT 0x08 /* Inconsistency detected */ + +/* dxd_t field construction + * Conveniently, the PXD macros work for DXD + */ +#define DXDlength PXDlength +#define DXDaddress PXDaddress +#define lengthDXD lengthPXD +#define addressDXD addressPXD + +/* + * directory entry argument + */ +typedef struct component_name { + int namlen; + UniChar *name; +} component_t; + + +/* + * DASD limit information - stored in directory inode + */ +typedef struct dasd { + u8 thresh; /* Alert Threshold (in percent) */ + u8 delta; /* Alert Threshold delta (in percent) */ + u8 rsrvd1; + u8 limit_hi; /* DASD limit (in logical blocks) */ + u32 limit_lo; /* DASD limit (in logical blocks) */ + u8 rsrvd2[3]; + u8 used_hi; /* DASD usage (in logical blocks) */ + u32 used_lo; /* DASD usage (in logical blocks) */ +} dasd_t; + +#define DASDLIMIT(dasdp) \ + (((u64)((dasdp)->limit_hi) << 32) + __le32_to_cpu((dasdp)->limit_lo)) +#define setDASDLIMIT(dasdp, limit)\ +{\ + (dasdp)->limit_hi = ((u64)limit) >> 32;\ + (dasdp)->limit_lo = __cpu_to_le32(limit);\ +} +#define DASDUSED(dasdp) \ + (((u64)((dasdp)->used_hi) << 32) + __le32_to_cpu((dasdp)->used_lo)) +#define setDASDUSED(dasdp, used)\ +{\ + (dasdp)->used_hi = ((u64)used) >> 32;\ + (dasdp)->used_lo = __cpu_to_le32(used);\ +} + +/* + * circular doubly-linked list (cdll) + * + * A circular doubly-linked list (cdll) is anchored by a pair of pointers, + * one to the head of the list and the other to the tail of the list. + * The elements are doubly linked so that an arbitrary element can be + * removed without a need to traverse the list. + * New elements can be added to the list before or after an existing element, + * at the head of the list, or at the tail of the list. + * A circle queue may be traversed in either direction. + * + * +----------+ +-------------------------------------+ + * | | | | + * +->+-----+ | +->+-----+ +->+-----+ +->+-----+ | + * | | h +-+ | | h +--+ | n +----+ | n +--+ + * | +-----+ | +-----+ | +-----+ | +-----+ + * | | t +-+ +-----+ t | | | p +--+ | | p +--+ + * | +-----+ | | | +-----+ | +-----+ | | +-----+ | + * +----------+ | +-----------------------+ | | + * | | | | + * | +-------------------------+ + * | | + * +----------------------------+ + */ +/* + * define header + * + * list header field definition in header element: + * + * type - type of list element struct embedding the link field + */ +#define CDLL_HEADER(type)\ +struct {\ + struct type *head;\ + struct type *tail;\ +} + +struct cdll_header { + struct cdll_header *head; + struct cdll_header *tail; +}; + +/* + * define link + * + * list link field definition in list element: + * + * type - type of parent list element struct embedding the link field + */ +#define CDLL_ENTRY(type)\ +struct {\ + struct type *next;\ + struct type *prev;\ +} + +struct cdll_entry { + struct cdll_entry *next; + struct cdll_entry *prev; +}; + +/* + * initialize header + * + * header - ptr to the header field in the header element + */ +#define CDLL_INIT(header) {\ + (header)->head = (void *)(header);\ + (header)->tail = (void *)(header);\ +} + +/* + * scan list + * + * header - ptr to the header field in the header element + * elm - ptr to the element to be inserted + * field - name of the link field in the list element + * + * struct header_container *container; + * struct header_type *header; + * struct element_type *elm; + * + * header = &container->header_field; + * for (elm = header->head; elm != (void *)header; elm = elm->field.next) + */ + +/* + * insert <elm> at head of list anchored at <header> + * + * header - ptr to the header field in the header element + * elm - ptr to the list element to be inserted + * field - name of the link field in the list element + */ +#define CDLL_INSERT_HEAD(header, elm, field) {\ + (elm)->field.next = (header)->head;\ + (elm)->field.prev = (void *)(header);\ + if ((header)->tail == (void *)(header))\ + (header)->tail = (elm);\ + else\ + (header)->head->field.prev = (elm);\ + (header)->head = (elm);\ +} + +/* + * insert <elm> at tail of list anchored at <header> + * + * header - ptr to the header field in the header element + * elm - ptr to the list element to be inserted + * field - name of the link field in the list element + */ +#define CDLL_INSERT_TAIL(header, elm, field) {\ + (elm)->field.next = (void *)(header);\ + (elm)->field.prev = (header)->tail;\ + if ((header)->head == (void *)(header))\ + (header)->head = (elm);\ + else\ + (header)->tail->field.next = (elm);\ + (header)->tail = (elm);\ +} + +/* + * insert <elm> after <listelm> of list anchored at <header> + * + * header - ptr to the header field in the header element + * listelm - ptr to the list element at insertion point + * elm - ptr to the list element to be inserted + * field - name of the link field in the list element + */ +#define CDLL_INSERT_AFTER(header, listelm, elm, field) {\ + (elm)->field.next = (listelm)->field.next;\ + (elm)->field.prev = (listelm);\ + if ((listelm)->field.next == (void *)(header))\ + (header)->tail = (elm);\ + else\ + (listelm)->field.next->field.prev = (elm);\ + (listelm)->field.next = (elm);\ +} + +/* + * insert <elm> before <listelm> of list anchored at <header> + * + * header - ptr to the header field in the header element + * listelm - ptr to list element at insertion point + * elm - ptr to the element to be inserted + * field - name of the link field in the list element + */ +#define CDLL_INSERT_BEFORE(header, listelm, elm, field) {\ + (elm)->field.next = (listelm);\ + (elm)->field.prev = (listelm)->field.prev;\ + if ((listelm)->field.prev == (void *)(header))\ + (header)->head = (elm);\ + else\ + (listelm)->field.prev->field.next = (elm);\ + (listelm)->field.prev = (elm);\ +} + +/* + * remove <elm> from list anchored at <header> + * + * header - ptr to the header field in the header element + * elm - ptr to the list element to be removed + * field - name of the link field in the list element + */ +#define CDLL_REMOVE(header, elm, field) {\ + if ((elm)->field.next == (void *)(header))\ + (header)->tail = (elm)->field.prev;\ + else\ + (elm)->field.next->field.prev = (elm)->field.prev;\ + if ((elm)->field.prev == (void *)(header))\ + (header)->head = (elm)->field.next;\ + else\ + (elm)->field.prev->field.next = (elm)->field.next;\ +} + +#define CDLL_MOVE_TO_HEAD(header, elm, field) {\ + if ((elm)->field.prev != (void *)(header))\ + {\ + if ((elm)->field.next == (void *)(header))\ + (header)->tail = (elm)->field.prev;\ + else\ + (elm)->field.next->field.prev = (elm)->field.prev;\ + (elm)->field.prev->field.next = (elm)->field.next;\ + (elm)->field.next = (header)->head;\ + (elm)->field.prev = (void *)(header);\ + (header)->head->field.prev = (elm);\ + (header)->head = (elm);\ + }\ +} + +#define CDLL_MOVE_TO_TAIL(header, elm, field) {\ + if ((elm)->field.next != (void *)(header))\ + {\ + (elm)->field.next->field.prev = (elm)->field.prev;\ + if ((elm)->field.prev == (void *)(header))\ + (header)->head = (elm)->field.next;\ + else\ + (elm)->field.prev->field.next = (elm)->field.next;\ + (elm)->field.next = (void *)(header);\ + (elm)->field.prev = (header)->tail;\ + (header)->tail->field.next = (elm);\ + (header)->tail = (elm);\ + }\ +} + +/* + * orphan list element + */ +#define CDLL_SELF(elm, field)\ + (elm)->field.next = (elm)->field.prev = (elm); + + +/* + * single head doubly-linked list + * + * A list is headed by a single head pointer. + * The elements are doubly linked so that an arbitrary element can be + * removed without a need to traverse the list. + * New elements can be added to the list at the head of the list, or + * after an existing element (NO insert at tail). + * A list may only be traversed in the forward direction. + * (note: the list is NULL terminated in next field.) + * + * +-----+ +->+-----+ +->+-----+ +->+-----+ + * | NULL| | | h +--+ | n +----+ | NULL| + * +-----+ | +-----+ | +-----+ +-----+ + * | | | p +--+ | p +--+ + * | | +-----+ | +-----+ | + * +-----------------------+ | + * | | + * +-------------------------+ + */ +#define LIST_HEADER(type)\ +struct {\ + struct type *head;\ +} + +#define LIST_ENTRY(type)\ +struct {\ + struct type *next;\ + struct type **prev;\ +} + +#define LIST_INIT(header) { (header)->head = NULL; } + +/* + * scan list + * + * header - ptr to the header (field in header element) + * elm - ptr to the element to be inserted + * field - name of the link field in list element + * + * struct header_container *container; + * struct header_type *header; + * struct element_type *elm; + * + * header = &container->header_field; + * for (elm = header->head; elm; elm = elm->field.next) + */ + +#define LIST_INSERT_HEAD(header, elm, field) {\ + if (((elm)->field.next = (header)->head) != NULL)\ + (header)->head->field.prev = &(elm)->field.next;\ + (header)->head = (elm);\ + (elm)->field.prev = &(header)->head;\ +} + +#define LIST_INSERT_AFTER(listelm, elm, field) {\ + if (((elm)->field.next = (listelm)->field.next) != NULL)\ + (listelm)->field.next->field.prev = &(elm)->field.next;\ + (listelm)->field.next = (elm);\ + (elm)->field.prev = &(listelm)->field.next;\ +} + +#define LIST_REMOVE(elm, field) {\ + if ((elm)->field.next != NULL)\ + (elm)->field.next->field.prev = (elm)->field.prev;\ + *(elm)->field.prev = (elm)->field.next;\ +} + +#define LIST_SELF(elm, field) {\ + (elm)->field.next = NULL;\ + (elm)->field.prev = &(elm)->field.next;\ +} + +#endif /* !_H_JFS_TYPES */ diff --git a/libparted/fs/linux_swap/linux_swap.c b/libparted/fs/linux_swap/linux_swap.c new file mode 100644 index 0000000..60100b0 --- /dev/null +++ b/libparted/fs/linux_swap/linux_swap.c @@ -0,0 +1,396 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-2000, 2002, 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/>. +*/ + +/* It's a bit silly calling a swap partition a file system. Oh well... */ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include <unistd.h> +#include <uuid/uuid.h> + +#define SWAP_SPECIFIC(fs) ((SwapSpecific*) (fs->type_specific)) +#define BUFFER_SIZE 128 + +#define LINUXSWAP_BLOCK_SIZES ((int[2]){512, 0}) + +typedef struct { + char page_map[1]; +} SwapOldHeader; + +/* ripped from mkswap */ +typedef struct { + char bootbits[1024]; /* Space for disklabel etc. */ + uint32_t version; + uint32_t last_page; + uint32_t nr_badpages; + unsigned char sws_uuid[16]; + unsigned char sws_volume[16]; + uint32_t padding[117]; + uint32_t badpages[1]; +} SwapNewHeader; + +typedef struct { + union { + SwapNewHeader new; + SwapOldHeader old; + }* header; + + void* buffer; + int buffer_size; + + PedSector page_sectors; + unsigned int page_count; + unsigned int version; + unsigned int max_bad_pages; +} SwapSpecific; + +static PedFileSystemType _swap_v0_type; +static PedFileSystemType _swap_v1_type; +static PedFileSystemType _swap_swsusp_type; + +static PedFileSystem* _swap_v0_open (PedGeometry* geom); +static PedFileSystem* _swap_v1_open (PedGeometry* geom); +static PedFileSystem* _swap_swsusp_open (PedGeometry* geom); +static int swap_close (PedFileSystem* fs); + +static PedGeometry* +_generic_swap_probe (PedGeometry* geom, int kind) +{ + PedFileSystem* fs; + SwapSpecific* fs_info; + PedGeometry* probed_geom; + PedSector length; + + switch (kind) { + /* Check for old style swap partitions. */ + case 0: + fs = _swap_v0_open(geom); + break; + /* Check for new style swap partitions. */ + case 1: + fs = _swap_v1_open(geom); + break; + /* Check for swap partitions containing swsusp data. */ + case -1: + fs = _swap_swsusp_open(geom); + break; + /* Not reached. */ + default: + goto error; + } + + if (!fs) + goto error; + fs_info = SWAP_SPECIFIC (fs); + + if (fs_info->version) + length = fs_info->page_sectors * fs_info->page_count; + else + length = geom->length; + + probed_geom = ped_geometry_new (geom->dev, geom->start, length); + if (!probed_geom) + goto error_close_fs; + swap_close (fs); + return probed_geom; + +error_close_fs: + swap_close (fs); +error: + return NULL; +} + + +static int +swap_init (PedFileSystem* fs) +{ + SwapSpecific* fs_info = SWAP_SPECIFIC (fs); + + fs_info->page_sectors = getpagesize () / fs->geom->dev->sector_size; + fs_info->page_count = fs->geom->length / fs_info->page_sectors; + fs_info->version = 1; + fs_info->max_bad_pages = (getpagesize() + - sizeof (SwapNewHeader)) / 4; + + return ped_geometry_read (fs->geom, fs_info->header, + 0, fs_info->page_sectors); +} + + +static PedFileSystem* +swap_alloc (PedGeometry* geom) +{ + PedFileSystem* fs; + SwapSpecific* fs_info; + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) + goto error; + + fs->type_specific = (SwapSpecific*) ped_malloc (sizeof (SwapSpecific)); + if (!fs->type_specific) + goto error_free_fs; + + fs_info = SWAP_SPECIFIC (fs); + fs_info->header = ped_malloc (PED_MAX(getpagesize(), geom->dev->sector_size)); + if (!fs_info->header) + goto error_free_type_specific; + + fs_info = SWAP_SPECIFIC (fs); + fs_info->buffer_size = getpagesize() * BUFFER_SIZE; + fs_info->buffer = ped_malloc (fs_info->buffer_size); + if (!fs_info->buffer) + goto error_free_header; + + fs->geom = ped_geometry_duplicate (geom); + if (!fs->geom) + goto error_free_buffer; + fs->type = &_swap_v1_type; + return fs; + +error_free_buffer: + free (fs_info->buffer); +error_free_header: + free (fs_info->header); +error_free_type_specific: + free (fs->type_specific); +error_free_fs: + free (fs); +error: + return NULL; +} + +static void +swap_free (PedFileSystem* fs) +{ + SwapSpecific* fs_info = SWAP_SPECIFIC (fs); + + free (fs_info->buffer); + free (fs_info->header); + free (fs->type_specific); + + ped_geometry_destroy (fs->geom); + free (fs); +} + +static PedFileSystem* +_swap_v0_open (PedGeometry* geom) +{ + PedFileSystem* fs; + SwapSpecific* fs_info; + const char* sig; + + fs = swap_alloc (geom); + if (!fs) + goto error; + swap_init (fs); + + fs_info = SWAP_SPECIFIC (fs); + if (!ped_geometry_read (fs->geom, fs_info->header, 0, + fs_info->page_sectors)) + goto error_free_fs; + + sig = ((char*) fs_info->header) + getpagesize() - 10; + if (strncmp (sig, "SWAP-SPACE", 10) == 0) { + fs_info->version = 0; + fs_info->page_count + = PED_MIN (fs->geom->length / fs_info->page_sectors, + 8 * (getpagesize() - 10)); + } else { + char _sig [11]; + + memcpy (_sig, sig, 10); + _sig [10] = 0; + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unrecognised old style linux swap signature '%10s'."), _sig); + goto error_free_fs; + } + + fs->checked = 1; + return fs; + +error_free_fs: + swap_free (fs); +error: + return NULL; +} + +static PedFileSystem* +_swap_v1_open (PedGeometry* geom) +{ + PedFileSystem* fs; + SwapSpecific* fs_info; + const char* sig; + + fs = swap_alloc (geom); + if (!fs) + goto error; + if (!swap_init(fs)) + goto error_free_fs; + + fs_info = SWAP_SPECIFIC (fs); + + sig = ((char*) fs_info->header) + getpagesize() - 10; + if (strncmp (sig, "SWAPSPACE2", 10) == 0) { + fs_info->version = 1; + fs_info->page_count = fs_info->header->new.last_page; + } else { + char _sig [11]; + + memcpy (_sig, sig, 10); + _sig [10] = 0; + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unrecognised new style linux swap signature '%10s'."), _sig); + goto error_free_fs; + } + + fs->checked = 1; + return fs; + +error_free_fs: + swap_free (fs); +error: + return NULL; +} + +static PedFileSystem* +_swap_swsusp_open (PedGeometry* geom) +{ + PedFileSystem* fs; + SwapSpecific* fs_info; + const char* sig; + + fs = swap_alloc (geom); + if (!fs) + goto error; + fs->type = &_swap_swsusp_type; + swap_init (fs); + + fs_info = SWAP_SPECIFIC (fs); + if (!ped_geometry_read (fs->geom, fs_info->header, 0, + fs_info->page_sectors)) + goto error_free_fs; + + sig = ((char*) fs_info->header) + getpagesize() - 10; + if (strncmp (sig, "S1SUSPEND", 9) == 0) { + fs_info->version = -1; + } else { + char _sig [10]; + + memcpy (_sig, sig, 9); + _sig [9] = 0; + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Unrecognised swsusp linux swap signature '%9s'."), _sig); + goto error_free_fs; + } + + fs->checked = 1; + return fs; + +error_free_fs: + swap_free (fs); +error: + return NULL; +} + +static int +swap_close (PedFileSystem* fs) +{ + swap_free (fs); + return 1; +} + +static PedGeometry* +_swap_v0_probe (PedGeometry* geom) { + return _generic_swap_probe (geom, 0); +} + +static PedGeometry* +_swap_v1_probe (PedGeometry* geom) { + return _generic_swap_probe (geom, 1); +} + +static PedGeometry* +_swap_swsusp_probe (PedGeometry* geom) { + return _generic_swap_probe (geom, -1); +} + +static PedFileSystemOps _swap_v0_ops = { + probe: _swap_v0_probe, +}; + +static PedFileSystemOps _swap_v1_ops = { + probe: _swap_v1_probe, +}; + +static PedFileSystemOps _swap_swsusp_ops = { + probe: _swap_swsusp_probe, +}; + +static PedFileSystemType _swap_v0_type = { + next: NULL, + ops: &_swap_v0_ops, + name: "linux-swap(v0)", +}; + +static PedFileSystemType _swap_v1_type = { + next: NULL, + ops: &_swap_v1_ops, + name: "linux-swap(v1)", +}; + +static PedFileSystemType _swap_swsusp_type = { + next: NULL, + ops: &_swap_swsusp_ops, + name: "swsusp", +}; + +void +ped_file_system_linux_swap_init () +{ + ped_file_system_type_register (&_swap_v0_type); + ped_file_system_type_register (&_swap_v1_type); + ped_file_system_type_register (&_swap_swsusp_type); + + ped_file_system_alias_register (&_swap_v0_type, "linux-swap(old)", 1); + ped_file_system_alias_register (&_swap_v1_type, "linux-swap(new)", 1); + ped_file_system_alias_register (&_swap_v1_type, "linux-swap", 0); +} + +void +ped_file_system_linux_swap_done () +{ + ped_file_system_alias_unregister (&_swap_v0_type, "linux-swap(old)"); + ped_file_system_alias_unregister (&_swap_v1_type, "linux-swap(new)"); + ped_file_system_alias_unregister (&_swap_v1_type, "linux-swap"); + + ped_file_system_type_unregister (&_swap_v0_type); + ped_file_system_type_unregister (&_swap_v1_type); + ped_file_system_type_unregister (&_swap_swsusp_type); +} diff --git a/libparted/fs/nilfs2/nilfs2.c b/libparted/fs/nilfs2/nilfs2.c new file mode 100644 index 0000000..6204542 --- /dev/null +++ b/libparted/fs/nilfs2/nilfs2.c @@ -0,0 +1,155 @@ +/* + * nilfs2.c - New Implementation of Log filesystem + * + * Written by Jiro SEKIBA <jir@unicus.jp> + * + * Copyright (C) 2011-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/crc32.h> +#include <parted/endian.h> + +/* Magic value for nilfs2 superblock. */ +#define NILFS2_SUPER_MAGIC 0x3434 + +/* primariy superblock offset in 512bytes blocks. */ +#define NILFS_SB_OFFSET 2 + +/* secondary superblock offset in 512byte blocks. */ +#define NILFS_SB2_OFFSET(devsize) ((((devsize)>>3) - 1) << 3) + +struct __attribute__ ((packed)) nilfs2_super_block { + uint32_t s_rev_level; + uint16_t s_minor_rev_level; + uint16_t s_magic; + uint16_t s_bytes; + uint16_t s_flags; + uint32_t s_crc_seed; + uint32_t s_sum; + uint32_t s_log_block_size; + uint64_t s_nsegments; + uint64_t s_dev_size; + uint64_t s_first_data_block; + uint32_t s_blocks_per_segment; + uint32_t s_r_segments_percentage; + uint64_t s_last_cno; + uint64_t s_last_pseg; + uint64_t s_last_seq; + uint64_t s_free_blocks_count; + uint64_t s_ctime; + uint64_t s_mtime; + uint64_t s_wtime; + uint16_t s_mnt_count; + uint16_t s_max_mnt_count; + uint16_t s_state; + uint16_t s_errors; + uint64_t s_lastcheck; + uint32_t s_checkinterval; + uint32_t s_creator_os; + uint16_t s_def_resuid; + uint16_t s_def_resgid; + uint32_t s_first_ino; + uint16_t s_inode_size; + uint16_t s_dat_entry_size; + uint16_t s_checkpoint_size; + uint16_t s_segment_usage_size; + uint8_t s_uuid[16]; + char s_volume_name[80]; + uint32_t s_c_interval; + uint32_t s_c_block_max; + uint32_t s_reserved[192]; +}; + +static int +is_valid_nilfs_sb(struct nilfs2_super_block *sb) +{ + static unsigned char sum[4]; + const int sumoff = offsetof (struct nilfs2_super_block, s_sum); + size_t bytes; + uint32_t crc; + + if (PED_LE16_TO_CPU(sb->s_magic) != NILFS2_SUPER_MAGIC) + return 0; + + bytes = PED_LE16_TO_CPU(sb->s_bytes); + if (bytes > 1024 || bytes < sumoff - 4) + return 0; + + crc = __efi_crc32(sb, sumoff, PED_LE32_TO_CPU(sb->s_crc_seed)); + crc = __efi_crc32(sum, 4, crc); + crc = __efi_crc32((unsigned char *)sb + sumoff + 4, + bytes - sumoff - 4, crc); + + return crc == PED_LE32_TO_CPU(sb->s_sum); +} + +PedGeometry* +nilfs2_probe (PedGeometry* geom) +{ + struct nilfs2_super_block *sb = NULL; + struct nilfs2_super_block *sb2 = NULL; + PedSector length = geom->length * (geom->dev->sector_size / 512); + + PedSector sb2off = NILFS_SB2_OFFSET(length) / (geom->dev->sector_size / 512); + if (sb2off <= 2) + return NULL; + const int sectors = (4096 + geom->dev->sector_size - 1) / + geom->dev->sector_size; + uint8_t *buf = alloca (sectors * geom->dev->sector_size); + const int sectors2 = (1024 + geom->dev->sector_size -1 ) / + geom->dev->sector_size; + void *buff2 = alloca (sectors2 * geom->dev->sector_size); + + if (ped_geometry_read(geom, buf, 0, sectors)) + sb = (struct nilfs2_super_block*)(buf + 1024); + if (ped_geometry_read(geom, buff2, sb2off, sectors2)) + sb2 = (struct nilfs2_super_block*)buff2; + + if ((!sb || !is_valid_nilfs_sb(sb)) && + (!sb2 || !is_valid_nilfs_sb(sb2))) + return NULL; + + /* reserve 4k bytes for secondary superblock */ + length = sb2off + ((4096 + geom->dev->sector_size - 1) / + geom->dev->sector_size); + + return ped_geometry_new(geom->dev, geom->start, length); +} + +static PedFileSystemOps nilfs2_ops = { + probe: nilfs2_probe, +}; + +static PedFileSystemType nilfs2_type = { + next: NULL, + ops: &nilfs2_ops, + name: "nilfs2", +}; + +void +ped_file_system_nilfs2_init () +{ + ped_file_system_type_register (&nilfs2_type); +} + +void +ped_file_system_nilfs2_done () +{ + ped_file_system_type_unregister (&nilfs2_type); +} diff --git a/libparted/fs/ntfs/ntfs.c b/libparted/fs/ntfs/ntfs.c new file mode 100644 index 0000000..9829f39 --- /dev/null +++ b/libparted/fs/ntfs/ntfs.c @@ -0,0 +1,73 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 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 <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include <unistd.h> + +#define NTFS_SIGNATURE "NTFS" + +PedGeometry* +ntfs_probe (PedGeometry* geom) +{ + uint8_t *buf = alloca(geom->dev->sector_size); + PedGeometry *newg = NULL; + + if (!ped_geometry_read(geom, buf, 0, 1)) + return 0; + + if (strncmp (NTFS_SIGNATURE, ((char *)buf + 3), strlen (NTFS_SIGNATURE)) == 0) { + uint64_t length; + memcpy(&length, buf + 0x28, sizeof(uint64_t)); + newg = ped_geometry_new (geom->dev, geom->start, length); + } + return newg; +} + +static PedFileSystemOps ntfs_ops = { + probe: ntfs_probe, +}; + +static PedFileSystemType ntfs_type = { + next: NULL, + ops: &ntfs_ops, + name: "ntfs", +}; + +void +ped_file_system_ntfs_init () +{ + ped_file_system_type_register (&ntfs_type); +} + +void +ped_file_system_ntfs_done () +{ + ped_file_system_type_unregister (&ntfs_type); +} diff --git a/libparted/fs/r/fat/bootsector.c b/libparted/fs/r/fat/bootsector.c new file mode 100644 index 0000000..85ccc0f --- /dev/null +++ b/libparted/fs/r/fat/bootsector.c @@ -0,0 +1,441 @@ +/* + libparted + Copyright (C) 1998-2000, 2002, 2004, 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 <config.h> +#include "fat.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +/* Reads in the boot sector (superblock), and does a minimum of sanity + * checking. The goals are: + * - to detect fat file systems, even if they are damaged [i.e. not + * return an error / throw an exception] + * - to fail detection if there's not enough information for + * fat_boot_sector_probe_type() to work (or possibly crash on a divide-by-zero) + */ +int +fat_boot_sector_read (FatBootSector** bsp, const PedGeometry *geom) +{ + PED_ASSERT (bsp != NULL); + PED_ASSERT (geom != NULL); + + if (!ped_geometry_read_alloc (geom, (void **)bsp, 0, 1)) + return 0; + FatBootSector *bs = *bsp; + if (PED_LE16_TO_CPU (bs->boot_sign) != 0xAA55) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid signature for a FAT " + "file system.")); + return 0; + } + + if (!bs->sector_size + || PED_LE16_TO_CPU (bs->sector_size) % PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid sector size for a FAT " + "file system.")); + return 0; + } + + if (!bs->cluster_size) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid cluster size for a FAT " + "file system.")); + return 0; + } + + if (!bs->reserved) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid number of reserved " + "sectors for a FAT file system.")); + return 0; + } + + if (bs->fats < 1 || bs->fats > 4) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("File system has an invalid number of FATs.")); + return 0; + } + + return 1; +} + +/* + Don't trust the FAT12, FAT16 or FAT32 label string. + */ +FatType _GL_ATTRIBUTE_PURE +fat_boot_sector_probe_type (const FatBootSector* bs, const PedGeometry* geom) +{ + PedSector logical_sector_size; + PedSector first_cluster_sector; + FatCluster cluster_count; + + if (!PED_LE16_TO_CPU (bs->dir_entries)) + return FAT_TYPE_FAT32; + + logical_sector_size = PED_LE16_TO_CPU (bs->sector_size) / 512; + + first_cluster_sector + = PED_LE16_TO_CPU (bs->reserved) * logical_sector_size + + 2 * PED_LE16_TO_CPU (bs->fat_length) * logical_sector_size + + PED_LE16_TO_CPU (bs->dir_entries) + / (512 / sizeof (FatDirEntry)); + cluster_count = (geom->length - first_cluster_sector) + / bs->cluster_size / logical_sector_size; + if (cluster_count > MAX_FAT12_CLUSTERS) + return FAT_TYPE_FAT16; + else + return FAT_TYPE_FAT12; +} + +/* Analyses the boot sector, and sticks appropriate numbers in + fs->type_specific. + + Note: you need to subtract (2 * cluster_sectors) off cluster offset, + because the first cluster is number 2. (0 and 1 are not real clusters, + and referencing them is a bug) + */ +int +fat_boot_sector_analyse (FatBootSector* bs, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int fat_entry_size; + + PED_ASSERT (bs != NULL); + + fs_info->logical_sector_size = PED_LE16_TO_CPU (bs->sector_size) / 512; + + fs_info->sectors_per_track = PED_LE16_TO_CPU (bs->secs_track); + fs_info->heads = PED_LE16_TO_CPU (bs->heads); + if (fs_info->sectors_per_track < 1 || fs_info->sectors_per_track > 63 + || fs_info->heads < 1 || fs_info->heads > 255) { + PedCHSGeometry* bios_geom = &fs->geom->dev->bios_geom; + int cyl_count = 0; + + if (fs_info->heads > 0 && fs_info->sectors_per_track > 0) + cyl_count = fs->geom->dev->length / fs_info->heads + / fs_info->sectors_per_track; + + switch (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_FIX + PED_EXCEPTION_IGNORE + + PED_EXCEPTION_CANCEL, + _("The file system's CHS geometry is (%d, %d, %d), " + "which is invalid. The partition table's CHS " + "geometry is (%d, %d, %d). If you select Ignore, " + "the file system's CHS geometry will be left " + "unchanged. If you select Fix, the file system's " + "CHS geometry will be set to match the partition " + "table's CHS geometry."), + cyl_count, fs_info->heads, fs_info->sectors_per_track, + bios_geom->cylinders, bios_geom->heads, + bios_geom->sectors)) { + + case PED_EXCEPTION_FIX: + fs_info->sectors_per_track = bios_geom->sectors; + fs_info->heads = bios_geom->heads; + bs->secs_track + = PED_CPU_TO_LE16 (fs_info->sectors_per_track); + bs->heads = PED_CPU_TO_LE16 (fs_info->heads); + if (!fat_boot_sector_write (bs, fs)) + return 0; + break; + + case PED_EXCEPTION_CANCEL: + return 0; + + case PED_EXCEPTION_IGNORE: + break; + + default: + break; + } + } + + if (bs->sectors) + fs_info->sector_count = PED_LE16_TO_CPU (bs->sectors) + * fs_info->logical_sector_size; + else + fs_info->sector_count = PED_LE32_TO_CPU (bs->sector_count) + * fs_info->logical_sector_size; + + fs_info->fat_table_count = bs->fats; + fs_info->root_dir_entry_count = PED_LE16_TO_CPU (bs->dir_entries); + fs_info->fat_offset = PED_LE16_TO_CPU (bs->reserved) + * fs_info->logical_sector_size; + fs_info->cluster_sectors = bs->cluster_size + * fs_info->logical_sector_size; + fs_info->cluster_size = fs_info->cluster_sectors * 512; + + if (fs_info->logical_sector_size == 0) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("FAT boot sector says logical sector size is 0. " + "This is weird. ")); + return 0; + } + if (fs_info->fat_table_count == 0) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("FAT boot sector says there are no FAT tables. This " + "is weird. ")); + return 0; + } + if (fs_info->cluster_sectors == 0) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("FAT boot sector says clusters are 0 sectors. This " + "is weird. ")); + return 0; + } + + fs_info->fat_type = fat_boot_sector_probe_type (bs, fs->geom); + if (fs_info->fat_type == FAT_TYPE_FAT12) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("File system is FAT12, which is unsupported.")); + return 0; + } + if (fs_info->fat_type == FAT_TYPE_FAT16) { + fs_info->fat_sectors = PED_LE16_TO_CPU (bs->fat_length) + * fs_info->logical_sector_size; + fs_info->serial_number + = PED_LE32_TO_CPU (bs->u.fat16.serial_number); + fs_info->root_cluster = 0; + fs_info->root_dir_offset + = fs_info->fat_offset + + fs_info->fat_sectors * fs_info->fat_table_count; + fs_info->root_dir_sector_count + = fs_info->root_dir_entry_count * sizeof (FatDirEntry) + / (512 * fs_info->logical_sector_size); + fs_info->cluster_offset + = fs_info->root_dir_offset + + fs_info->root_dir_sector_count; + } + if (fs_info->fat_type == FAT_TYPE_FAT32) { + fs_info->fat_sectors = PED_LE32_TO_CPU (bs->u.fat32.fat_length) + * fs_info->logical_sector_size; + fs_info->serial_number + = PED_LE32_TO_CPU (bs->u.fat32.serial_number); + fs_info->info_sector_offset + = PED_LE16_TO_CPU (fs_info->boot_sector->u.fat32.info_sector) + * fs_info->logical_sector_size; + fs_info->boot_sector_backup_offset + = PED_LE16_TO_CPU (fs_info->boot_sector->u.fat32.backup_sector) + * fs_info->logical_sector_size; + fs_info->root_cluster + = PED_LE32_TO_CPU (bs->u.fat32.root_dir_cluster); + fs_info->root_dir_offset = 0; + fs_info->root_dir_sector_count = 0; + fs_info->cluster_offset + = fs_info->fat_offset + + fs_info->fat_sectors * fs_info->fat_table_count; + } + + fs_info->cluster_count + = (fs_info->sector_count - fs_info->cluster_offset) + / fs_info->cluster_sectors; + + fat_entry_size = fat_table_entry_size (fs_info->fat_type); + if (fs_info->cluster_count + 2 + > fs_info->fat_sectors * 512 / fat_entry_size) + fs_info->cluster_count + = fs_info->fat_sectors * 512 / fat_entry_size - 2; + + fs_info->dir_entries_per_cluster + = fs_info->cluster_size / sizeof (FatDirEntry); + return 1; +} + +#ifndef DISCOVER_ONLY +int +fat_boot_sector_set_boot_code (FatBootSector** bsp, const PedFileSystem* fs) +{ + PED_ASSERT (bsp != NULL); + *bsp = ped_malloc (fs->geom->dev->sector_size); + FatBootSector *bs = *bsp; + PED_ASSERT (bs != NULL); + + memset (bs, 0, 512); + memcpy (bs->boot_jump, FAT_BOOT_JUMP, 3); + PED_ASSERT (sizeof(FAT_BOOT_CODE) < sizeof(bs->u.fat32.boot_code)); + strcpy (bs->u.fat32.boot_code, FAT_BOOT_CODE); + return 1; +} + +int +fat_boot_sector_generate (FatBootSector** bsp, const PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (bsp != NULL); + FatBootSector *bs = *bsp; + PED_ASSERT (bs != NULL); + + memcpy (bs->system_id, "MSWIN4.1", 8); + bs->sector_size = PED_CPU_TO_LE16 (fs_info->logical_sector_size * 512); + bs->cluster_size = fs_info->cluster_sectors + / fs_info->logical_sector_size; + bs->reserved = PED_CPU_TO_LE16 (fs_info->fat_offset + / fs_info->logical_sector_size); + bs->fats = fs_info->fat_table_count; + + bs->dir_entries = (fs_info->fat_type == FAT_TYPE_FAT16) + ? PED_CPU_TO_LE16 (fs_info->root_dir_entry_count) + : 0; + + if (fs_info->sector_count / fs_info->logical_sector_size > 0xffff + || fs_info->fat_type == FAT_TYPE_FAT32) { + bs->sectors = 0; + bs->sector_count = PED_CPU_TO_LE32 (fs_info->sector_count + / fs_info->logical_sector_size); + } else { + bs->sectors = PED_CPU_TO_LE16 (fs_info->sector_count + / fs_info->logical_sector_size); + bs->sector_count = 0; + } + + bs->media = 0xf8; + + bs->secs_track = PED_CPU_TO_LE16 (fs_info->sectors_per_track); + bs->heads = PED_CPU_TO_LE16 (fs_info->heads); + bs->hidden = PED_CPU_TO_LE32 (fs->geom->start); + + if (fs_info->fat_type == FAT_TYPE_FAT32) { + bs->fat_length = 0; + bs->u.fat32.fat_length = PED_CPU_TO_LE32 (fs_info->fat_sectors + / fs_info->logical_sector_size); + bs->u.fat32.flags = 0; /* FIXME: what the hell are these? */ + bs->u.fat32.version = 0; /* must be 0, for Win98 bootstrap */ + bs->u.fat32.root_dir_cluster + = PED_CPU_TO_LE32 (fs_info->root_cluster); + bs->u.fat32.info_sector + = PED_CPU_TO_LE16 (fs_info->info_sector_offset + / fs_info->logical_sector_size); + bs->u.fat32.backup_sector + = PED_CPU_TO_LE16 (fs_info->boot_sector_backup_offset + / fs_info->logical_sector_size); + + bs->u.fat32.drive_num = 0x80; /* _ALWAYS_ 0x80. silly DOS */ + + memset (bs->u.fat32.empty_1, 0, 12); + + bs->u.fat32.ext_signature = 0x29; + bs->u.fat32.serial_number + = PED_CPU_TO_LE32 (fs_info->serial_number); + memcpy (bs->u.fat32.volume_name, "NO NAME ", 11); + memcpy (bs->u.fat32.fat_name, "FAT32 ", 8); + } else { + bs->fat_length + = PED_CPU_TO_LE16 (fs_info->fat_sectors + / fs_info->logical_sector_size); + + bs->u.fat16.drive_num = 0x80; /* _ALWAYS_ 0x80. silly DOS */ + + bs->u.fat16.ext_signature = 0x29; + bs->u.fat16.serial_number + = PED_CPU_TO_LE32 (fs_info->serial_number); + memcpy (bs->u.fat16.volume_name, "NO NAME ", 11); + memcpy (bs->u.fat16.fat_name, "FAT16 ", 8); + } + + bs->boot_sign = PED_CPU_TO_LE16 (0xaa55); + + return 1; +} + +int +fat_boot_sector_write (const FatBootSector* bs, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (bs != NULL); + + if (!ped_geometry_write (fs->geom, bs, 0, 1)) + return 0; + if (fs_info->fat_type == FAT_TYPE_FAT32) { + if (!ped_geometry_write (fs->geom, bs, + fs_info->boot_sector_backup_offset, 1)) + return 0; + } + return ped_geometry_sync (fs->geom); +} + +int +fat_info_sector_read (FatInfoSector** isp, const PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int status; + + PED_ASSERT (isp != NULL); + + if (!ped_geometry_read_alloc (fs->geom, (void **)isp, fs_info->info_sector_offset, 1)) + return 0; + FatInfoSector *is = *isp; + if (PED_LE32_TO_CPU (is->signature_2) != FAT32_INFO_MAGIC2) { + status = ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The information sector has the wrong " + "signature (%x). Select cancel for now, " + "and send in a bug report. If you're " + "desperate, it's probably safe to ignore."), + PED_LE32_TO_CPU (is->signature_2)); + if (status == PED_EXCEPTION_CANCEL) return 0; + } + return 1; +} + +int +fat_info_sector_generate (FatInfoSector** isp, const PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (isp != NULL); + *isp = ped_malloc (fs->geom->dev->sector_size); + FatInfoSector *is = *isp; + + fat_table_count_stats (fs_info->fat); + + memset (is, 0, 512); + + is->signature_1 = PED_CPU_TO_LE32 (FAT32_INFO_MAGIC1); + is->signature_2 = PED_CPU_TO_LE32 (FAT32_INFO_MAGIC2); + is->free_clusters = PED_CPU_TO_LE32 (fs_info->fat->free_cluster_count); + is->next_cluster = PED_CPU_TO_LE32 (fs_info->fat->last_alloc); + is->signature_3 = PED_CPU_TO_LE16 (FAT32_INFO_MAGIC3); + + return 1; +} + +int +fat_info_sector_write (const FatInfoSector* is, PedFileSystem *fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (is != NULL); + + if (!ped_geometry_write (fs->geom, is, fs_info->info_sector_offset, 1)) + return 0; + return ped_geometry_sync (fs->geom); +} +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/fat/bootsector.h b/libparted/fs/r/fat/bootsector.h new file mode 100644 index 0000000..699d6cf --- /dev/null +++ b/libparted/fs/r/fat/bootsector.h @@ -0,0 +1,130 @@ +/* + libparted + Copyright (C) 1998-2000, 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/>. +*/ + +#ifndef PED_FAT_BOOTSECTOR_H +#define PED_FAT_BOOTSECTOR_H + +typedef struct _FatBootSector FatBootSector; +typedef struct _FatInfoSector FatInfoSector; + +#include "fat.h" + +#define FAT32_INFO_MAGIC1 0x41615252 +#define FAT32_INFO_MAGIC2 0x61417272 +#define FAT32_INFO_MAGIC3 0xaa55 + +/* stolen from mkdosfs, by Dave Hudson */ + +#define FAT_BOOT_MESSAGE \ +"This partition does not have an operating system loader installed on it.\n\r"\ +"Press a key to reboot..." + +#define FAT_BOOT_JUMP "\xeb\x58\x90" /* jmp +5a */ + +#define FAT_BOOT_CODE "\x0e" /* push cs */ \ + "\x1f" /* pop ds */ \ + "\xbe\x74\x7e" /* mov si, offset message */ \ + /* write_msg_loop: */ \ + "\xac" /* lodsb */ \ + "\x22\xc0" /* and al, al */ \ + "\x74\x06" /* jz done (+8) */ \ + "\xb4\x0e" /* mov ah, 0x0e */ \ + "\xcd\x10" /* int 0x10 */ \ + "\xeb\xf5" /* jmp write_msg_loop */ \ + /* done: */ \ + "\xb4\x00" /* mov ah, 0x00 */ \ + "\xcd\x16" /* int 0x16 */ \ + "\xb4\x00" /* mov ah, 0x00 */ \ + "\xcd\x19" /* int 0x19 */ \ + "\xeb\xfe" /* jmp +0 - in case int 0x19 */ \ + /* doesn't work */ \ + /* message: */ \ + FAT_BOOT_MESSAGE + +struct __attribute__ ((packed)) _FatBootSector { + uint8_t boot_jump[3]; /* 00: Boot strap short or near jump */ + uint8_t system_id[8]; /* 03: system name */ + uint16_t sector_size; /* 0b: bytes per logical sector */ + uint8_t cluster_size; /* 0d: sectors/cluster */ + uint16_t reserved; /* 0e: reserved sectors */ + uint8_t fats; /* 10: number of FATs */ + uint16_t dir_entries; /* 11: number of root directory entries */ + uint16_t sectors; /* 13: if 0, total_sect supersedes */ + uint8_t media; /* 15: media code */ + uint16_t fat_length; /* 16: sectors/FAT for FAT12/16 */ + uint16_t secs_track; /* 18: sectors per track */ + uint16_t heads; /* 1a: number of heads */ + uint32_t hidden; /* 1c: hidden sectors (partition start) */ + uint32_t sector_count; /* 20: no. of sectors (if sectors == 0) */ + + union __attribute__ ((packed)) { + /* FAT16 fields */ + struct __attribute__ ((packed)) { + uint8_t drive_num; /* 24: */ + uint8_t empty_1; /* 25: */ + uint8_t ext_signature; /* 26: always 0x29 */ + uint32_t serial_number; /* 27: */ + uint8_t volume_name [11]; /* 2b: */ + uint8_t fat_name [8]; /* 36: */ + uint8_t boot_code[448]; /* 3f: Boot code (or message) */ + } fat16; + /* FAT32 fields */ + struct __attribute__ ((packed)) { + uint32_t fat_length; /* 24: size of FAT in sectors */ + uint16_t flags; /* 28: bit8: fat mirroring, low4: active fat */ + uint16_t version; /* 2a: minor * 256 + major */ + uint32_t root_dir_cluster; /* 2c: */ + uint16_t info_sector; /* 30: */ + uint16_t backup_sector; /* 32: */ + uint8_t empty_1 [12]; /* 34: */ + uint16_t drive_num; /* 40: */ + uint8_t ext_signature; /* 42: always 0x29 */ + uint32_t serial_number; /* 43: */ + uint8_t volume_name [11]; /* 47: */ + uint8_t fat_name [8]; /* 52: */ + uint8_t boot_code[420]; /* 5a: Boot code (or message) */ + } fat32; + } u; + + uint16_t boot_sign; /* 1fe: always 0xAA55 */ +}; + +struct __attribute__ ((packed)) _FatInfoSector { + uint32_t signature_1; /* should be 0x41615252 */ + uint8_t unused [480]; + uint32_t signature_2; /* should be 0x61417272 */ + uint32_t free_clusters; + uint32_t next_cluster; /* most recently allocated cluster */ + uint8_t unused2 [0xe]; + uint16_t signature_3; /* should be 0xaa55 */ +}; + +int fat_boot_sector_read (FatBootSector** bs, const PedGeometry* geom); +FatType fat_boot_sector_probe_type (const FatBootSector* bs, + const PedGeometry* geom); +int fat_boot_sector_analyse (FatBootSector* bs, PedFileSystem* fs); +int fat_boot_sector_set_boot_code (FatBootSector** bs, const PedFileSystem* fs); +int fat_boot_sector_generate (FatBootSector** bs, const PedFileSystem* fs); +int fat_boot_sector_write (const FatBootSector* bs, PedFileSystem* fs); + +int fat_info_sector_read (FatInfoSector** is, const PedFileSystem* fs); +int fat_info_sector_generate (FatInfoSector** is, const PedFileSystem* fs); +int fat_info_sector_write (const FatInfoSector* is, PedFileSystem* fs); + +#endif /* PED_FAT_BOOTSECTOR_H */ diff --git a/libparted/fs/r/fat/calc.c b/libparted/fs/r/fat/calc.c new file mode 100644 index 0000000..4ba1030 --- /dev/null +++ b/libparted/fs/r/fat/calc.c @@ -0,0 +1,433 @@ +/* + libparted + Copyright (C) 1998-2000, 2002, 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 <config.h> +#include "fat.h" + +#ifndef DISCOVER_ONLY + +/* returns the minimum size of clusters for a given file system type */ +PedSector _GL_ATTRIBUTE_CONST +fat_min_cluster_size (FatType fat_type) { + switch (fat_type) { + case FAT_TYPE_FAT12: return 1; + case FAT_TYPE_FAT16: return 1024/512; + case FAT_TYPE_FAT32: return 4096/512; + } + return 0; +} + +static PedSector _GL_ATTRIBUTE_CONST +_smallest_power2_over (PedSector ceiling) +{ + PedSector result = 1; + + while (result < ceiling) + result *= 2; + + return result; +} + +/* returns the minimum size of clusters for a given file system type */ +PedSector _GL_ATTRIBUTE_CONST +fat_recommend_min_cluster_size (FatType fat_type, PedSector size) { + switch (fat_type) { + case FAT_TYPE_FAT12: return 1; + case FAT_TYPE_FAT16: return fat_min_cluster_size(fat_type); + case FAT_TYPE_FAT32: + return PED_MAX(_smallest_power2_over(size + / MAX_FAT32_CLUSTERS), + fat_min_cluster_size (fat_type)); + } + return 0; +} + +/* returns the maxmimum size of clusters for a given file system type */ +PedSector _GL_ATTRIBUTE_CONST +fat_max_cluster_size (FatType fat_type) { + switch (fat_type) { + case FAT_TYPE_FAT12: return 1; /* dunno... who cares? */ + case FAT_TYPE_FAT16: return 65536/512; + case FAT_TYPE_FAT32: return 65536/512; + } + return 0; +} + +/* returns the minimum number of clusters for a given file system type */ +FatCluster _GL_ATTRIBUTE_CONST +fat_min_cluster_count (FatType fat_type) { + switch (fat_type) { + case FAT_TYPE_FAT12: + case FAT_TYPE_FAT16: + return fat_max_cluster_count (fat_type) / 2; + + case FAT_TYPE_FAT32: return 0xfff0; + } + return 0; +} + +/* returns the maximum number of clusters for a given file system type */ +FatCluster _GL_ATTRIBUTE_CONST +fat_max_cluster_count (FatType fat_type) { + switch (fat_type) { + case FAT_TYPE_FAT12: return 0xff0; + case FAT_TYPE_FAT16: return 0xfff0; + case FAT_TYPE_FAT32: return 0x0ffffff0; + } + return 0; +} + +/* what is this supposed to be? What drugs are M$ on? (Can I have some? :-) */ +PedSector _GL_ATTRIBUTE_CONST +fat_min_reserved_sector_count (FatType fat_type) +{ + return (fat_type == FAT_TYPE_FAT32) ? 32 : 1; +} + +int +fat_check_resize_geometry (const PedFileSystem* fs, + const PedGeometry* geom, + PedSector new_cluster_sectors, + FatCluster new_cluster_count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector free_space; + PedSector min_free_space; + PedSector total_space; + PedSector new_total_space; + PedSector dir_space; + + PED_ASSERT (geom != NULL); + + dir_space = fs_info->total_dir_clusters * fs_info->cluster_sectors; + free_space = fs_info->fat->free_cluster_count + * fs_info->cluster_sectors; + total_space = fs_info->fat->cluster_count * fs_info->cluster_sectors; + new_total_space = new_cluster_count * new_cluster_sectors; + min_free_space = total_space - new_total_space + dir_space; + + PED_ASSERT (new_cluster_count + <= fat_max_cluster_count (FAT_TYPE_FAT32)); + + if (free_space < min_free_space) { + char* needed = ped_unit_format (geom->dev, min_free_space); + char* have = ped_unit_format (geom->dev, free_space); + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("You need %s of free disk space to shrink this " + "partition to this size. Currently, only %s is " + "free."), + needed, have); + free (needed); + free (have); + return 0; + } + + return 1; +} + + +/******************************************************************************/ + +/* DO NOT EDIT THIS ALGORITHM! + * As far as I can tell, this is the same algorithm used by Microsoft to + * calculate the size of the file allocaion tables, and the number of clusters. + * I have not verified this by dissassembling Microsoft code - I came to this + * conclusion by empirical analysis (i.e. trial and error - this was HORRIBLE). + * + * If you think this code makes no sense, then you are right. I will restrain + * the urge to inflict serious bodily harm on Microsoft people. + */ + +static int +entries_per_sector (FatType fat_type) +{ + switch (fat_type) { + case FAT_TYPE_FAT12: + return 512 * 3 / 2; + case FAT_TYPE_FAT16: + return 512 / 2; + case FAT_TYPE_FAT32: + return 512 / 4; + } + return 0; +} + +static int +calc_sizes (PedSector size, PedSector align, FatType fat_type, + PedSector root_dir_sectors, PedSector cluster_sectors, + FatCluster* out_cluster_count, PedSector* out_fat_size) +{ + PedSector data_fat_space; /* space available to clusters + FAT */ + PedSector fat_space; /* space taken by each FAT */ + PedSector cluster_space; /* space taken by clusters */ + FatCluster cluster_count; + int i; + + PED_ASSERT (out_cluster_count != NULL); + PED_ASSERT (out_fat_size != NULL); + + data_fat_space = size - fat_min_reserved_sector_count (fat_type) + - align; + if (fat_type == FAT_TYPE_FAT16) + data_fat_space -= root_dir_sectors; + + fat_space = 0; + for (i = 0; i < 2; i++) { + if (fat_type == FAT_TYPE_FAT32) + cluster_space = data_fat_space - fat_space; + else + cluster_space = data_fat_space - 2 * fat_space; + + cluster_count = cluster_space / cluster_sectors; + fat_space = ped_div_round_up (cluster_count + 2, + entries_per_sector (fat_type)); + } + + cluster_space = data_fat_space - 2 * fat_space; + cluster_count = cluster_space / cluster_sectors; + + /* looks like this should be part of the loop condition? + * Need to build the Big Table TM again to check + */ + if (fat_space < ped_div_round_up (cluster_count + 2, + entries_per_sector (fat_type))) { + fat_space = ped_div_round_up (cluster_count + 2, + entries_per_sector (fat_type)); + } + + if (cluster_count > fat_max_cluster_count (fat_type) + || cluster_count < fat_min_cluster_count (fat_type)) + return 0; + + *out_cluster_count = cluster_count; + *out_fat_size = fat_space; + + return 1; +} + +/****************************************************************************/ + +int +fat_calc_sizes (PedSector size, PedSector align, FatType fat_type, + PedSector root_dir_sectors, + PedSector* out_cluster_sectors, FatCluster* out_cluster_count, + PedSector* out_fat_size) +{ + PedSector cluster_sectors; + + PED_ASSERT (out_cluster_sectors != NULL); + PED_ASSERT (out_cluster_count != NULL); + PED_ASSERT (out_fat_size != NULL); + + for (cluster_sectors = fat_recommend_min_cluster_size (fat_type, size); + cluster_sectors <= fat_max_cluster_size (fat_type); + cluster_sectors *= 2) { + if (calc_sizes (size, align, fat_type, root_dir_sectors, + cluster_sectors, + out_cluster_count, out_fat_size)) { + *out_cluster_sectors = cluster_sectors; + return 1; + } + } + + for (cluster_sectors = fat_recommend_min_cluster_size (fat_type, size); + cluster_sectors >= fat_min_cluster_size (fat_type); + cluster_sectors /= 2) { + if (calc_sizes (size, align, fat_type, root_dir_sectors, + cluster_sectors, + out_cluster_count, out_fat_size)) { + *out_cluster_sectors = cluster_sectors; + return 1; + } + } + + /* only make the cluster size really small (<4k) if a bigger one is + * isn't possible. Windows never makes FS's like this, but it + * seems to work... (do more tests!) + */ + for (cluster_sectors = 4; cluster_sectors > 0; cluster_sectors /= 2) { + if (calc_sizes (size, align, fat_type, root_dir_sectors, + cluster_sectors, + out_cluster_count, out_fat_size)) { + *out_cluster_sectors = cluster_sectors; + return 1; + } + } + + return 0; +} + +/* Same as fat_calc_sizes, except it only attempts to match a particular + * cluster size. This is useful, because the FAT resizer can only shrink the + * cluster size. + */ +int +fat_calc_resize_sizes ( + const PedGeometry* geom, + PedSector align, + FatType fat_type, + PedSector root_dir_sectors, + PedSector cluster_sectors, + PedSector* out_cluster_sectors, + FatCluster* out_cluster_count, + PedSector* out_fat_size) +{ + PED_ASSERT (geom != NULL); + PED_ASSERT (out_cluster_sectors != NULL); + PED_ASSERT (out_cluster_count != NULL); + PED_ASSERT (out_fat_size != NULL); + +/* libparted can only reduce the cluster size at this point */ + for (*out_cluster_sectors = cluster_sectors; + *out_cluster_sectors >= fat_min_cluster_size (fat_type); + *out_cluster_sectors /= 2) { + if (calc_sizes (geom->length, align, fat_type, root_dir_sectors, + *out_cluster_sectors, + out_cluster_count, out_fat_size)) + return 1; + } + return 0; +} + +/* Calculates the number of sectors needed to be added to cluster_offset, + to make the cluster on the new file system match up with the ones + on the old file system. + However, some space is reserved by fat_calc_resize_sizes() and + friends, to allow room for this space. If too much of this space is left + over, everyone will complain, so we have to be greedy, and use it all up... + */ +PedSector _GL_ATTRIBUTE_PURE +fat_calc_align_sectors (const PedFileSystem* new_fs, + const PedFileSystem* old_fs) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs); + PedSector raw_old_meta_data_end; + PedSector new_meta_data_size; + PedSector min_new_meta_data_end; + PedSector new_data_size; + PedSector new_clusters_size; + PedSector align; + + new_meta_data_size + = fat_min_reserved_sector_count (new_fs_info->fat_type) + + new_fs_info->fat_sectors * 2; + + if (new_fs_info->fat_type == FAT_TYPE_FAT16) + new_meta_data_size += new_fs_info->root_dir_sector_count; + + raw_old_meta_data_end = old_fs->geom->start + + old_fs_info->cluster_offset; + + min_new_meta_data_end = new_fs->geom->start + new_meta_data_size; + + if (raw_old_meta_data_end > min_new_meta_data_end) + align = (raw_old_meta_data_end - min_new_meta_data_end) + % new_fs_info->cluster_sectors; + else + align = (new_fs_info->cluster_sectors + - ( (min_new_meta_data_end - raw_old_meta_data_end) + % new_fs_info->cluster_sectors )) + % new_fs_info->cluster_sectors; + + new_data_size = new_fs->geom->length - new_meta_data_size; + new_clusters_size = new_fs_info->cluster_count + * new_fs_info->cluster_sectors; + + while (new_clusters_size + align + new_fs_info->cluster_sectors + <= new_data_size) + align += new_fs_info->cluster_sectors; + + return align; +} + +int _GL_ATTRIBUTE_PURE +fat_is_sector_in_clusters (const PedFileSystem* fs, PedSector sector) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + return sector >= fs_info->cluster_offset + && sector < fs_info->cluster_offset + + fs_info->cluster_sectors * fs_info->cluster_count; +} + +FatFragment _GL_ATTRIBUTE_PURE +fat_cluster_to_frag (const PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2); + + return (cluster - 2) * fs_info->cluster_frags; +} + +FatCluster _GL_ATTRIBUTE_PURE +fat_frag_to_cluster (const PedFileSystem* fs, FatFragment frag) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (frag >= 0 && frag < fs_info->frag_count); + + return frag / fs_info->cluster_frags + 2; +} + +PedSector _GL_ATTRIBUTE_PURE +fat_frag_to_sector (const PedFileSystem* fs, FatFragment frag) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (frag >= 0 && frag < fs_info->frag_count); + + return frag * fs_info->frag_sectors + fs_info->cluster_offset; +} + +FatFragment _GL_ATTRIBUTE_PURE +fat_sector_to_frag (const PedFileSystem* fs, PedSector sector) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (sector >= fs_info->cluster_offset); + + return (sector - fs_info->cluster_offset) / fs_info->frag_sectors; +} + +PedSector _GL_ATTRIBUTE_PURE +fat_cluster_to_sector (const PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2); + + return (cluster - 2) * fs_info->cluster_sectors + + fs_info->cluster_offset; +} + +FatCluster _GL_ATTRIBUTE_PURE +fat_sector_to_cluster (const PedFileSystem* fs, PedSector sector) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (sector >= fs_info->cluster_offset); + + return (sector - fs_info->cluster_offset) / fs_info->cluster_sectors + + 2; +} +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/fat/calc.h b/libparted/fs/r/fat/calc.h new file mode 100644 index 0000000..d4884c1 --- /dev/null +++ b/libparted/fs/r/fat/calc.h @@ -0,0 +1,77 @@ +/* + libparted + Copyright (C) 1998-2000, 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/>. +*/ + +#ifndef PED_FAT_CALC_H +#define PED_FAT_CALC_H + +extern PedSector fat_min_cluster_size (FatType fat_type); +extern PedSector fat_max_cluster_size (FatType fat_type); +extern FatCluster fat_min_cluster_count (FatType fat_type); +extern FatCluster fat_max_cluster_count (FatType fat_type); + +extern PedSector fat_min_reserved_sector_count (FatType fat_type); + +extern int fat_check_resize_geometry (const PedFileSystem* fs, + const PedGeometry* geom, + PedSector new_cluster_sectors, + FatCluster new_cluster_count); + +extern int fat_calc_sizes (PedSector size, + PedSector align, + FatType fat_type, + PedSector root_dir_sectors, + PedSector* out_cluster_sectors, + FatCluster* out_cluster_count, + PedSector* out_fat_size); + +extern int fat_calc_resize_sizes (const PedGeometry* geom, + PedSector align, + FatType fat_type, + PedSector root_dir_sectors, + PedSector cluster_sectors, + PedSector* out_cluster_sectors, + FatCluster* out_cluster_count, + PedSector* out_fat_size); + +extern PedSector +fat_calc_align_sectors (const PedFileSystem* new_fs, + const PedFileSystem* old_fs); + +extern int +fat_is_sector_in_clusters (const PedFileSystem* fs, PedSector sector); + +extern FatFragment +fat_cluster_to_frag (const PedFileSystem* fs, FatCluster cluster); + +extern FatCluster +fat_frag_to_cluster (const PedFileSystem* fs, FatFragment frag); + +extern PedSector +fat_frag_to_sector (const PedFileSystem* fs, FatFragment frag); + +extern FatFragment +fat_sector_to_frag (const PedFileSystem* fs, PedSector sector); + +extern PedSector +fat_cluster_to_sector (const PedFileSystem* fs, FatCluster cluster); + +extern FatCluster +fat_sector_to_cluster (const PedFileSystem* fs, PedSector sector); + +#endif /* PED_FAT_CALC_H */ diff --git a/libparted/fs/r/fat/clstdup.c b/libparted/fs/r/fat/clstdup.c new file mode 100644 index 0000000..6a3054f --- /dev/null +++ b/libparted/fs/r/fat/clstdup.c @@ -0,0 +1,423 @@ +/* + libparted + Copyright (C) 1998-2001, 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 <config.h> +#include <string.h> + +#include "fat.h" + +#ifndef DISCOVER_ONLY + +static int +needs_duplicating (const FatOpContext* ctx, FatFragment frag) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatCluster cluster = fat_frag_to_cluster (ctx->old_fs, frag); + FatClusterFlag flag; + + PED_ASSERT (cluster >= 2 && cluster < old_fs_info->cluster_count + 2); + + flag = fat_get_fragment_flag (ctx->old_fs, frag); + switch (flag) { + case FAT_FLAG_FREE: + return 0; + + case FAT_FLAG_DIRECTORY: + return 1; + + case FAT_FLAG_FILE: + return fat_op_context_map_static_fragment (ctx, frag) == -1; + + case FAT_FLAG_BAD: + return 0; + } + + return 0; +} + +static int +search_next_fragment (FatOpContext* ctx) +{ + FatSpecific* fs_info = FAT_SPECIFIC (ctx->old_fs); + + for (; ctx->buffer_offset < fs_info->frag_count; ctx->buffer_offset++) { + if (needs_duplicating (ctx, ctx->buffer_offset)) + return 1; + } + return 0; /* all done! */ +} + +static int +read_marked_fragments (FatOpContext* ctx, FatFragment length) +{ + FatSpecific* fs_info = FAT_SPECIFIC (ctx->old_fs); + int status; + FatFragment i; + + ped_exception_fetch_all (); + status = fat_read_fragments (ctx->old_fs, fs_info->buffer, + ctx->buffer_offset, length); + ped_exception_leave_all (); + if (status) + return 1; + + ped_exception_catch (); + +/* something bad happened, so read fragments one by one. (The error may + have occurred on an unused fragment: who cares) */ + for (i = 0; i < length; i++) { + if (ctx->buffer_map [i]) { + if (!fat_read_fragment (ctx->old_fs, + fs_info->buffer + i * fs_info->frag_size, + ctx->buffer_offset + i)) + return 0; + } + } + + return 1; +} + +static int +fetch_fragments (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatFragment fetch_length = 0; + FatFragment frag; + + for (frag = 0; frag < ctx->buffer_frags; frag++) + ctx->buffer_map [frag] = -1; + + for (frag = 0; + frag < ctx->buffer_frags + && ctx->buffer_offset + frag < old_fs_info->frag_count; + frag++) { + if (needs_duplicating (ctx, ctx->buffer_offset + frag)) { + ctx->buffer_map [frag] = 1; + fetch_length = frag + 1; + } + } + + if (!read_marked_fragments (ctx, fetch_length)) + return 0; + + return 1; +} + +/***************************************************************************** + * here starts the write code. All assumes that ctx->buffer_map [first] and + * ctx->buffer_map [last] are occupied by fragments that need to be duplicated. + *****************************************************************************/ + +/* finds the first fragment that is not going to get overwritten (that needs to + get read in) */ +static FatFragment _GL_ATTRIBUTE_PURE +get_first_underlay (const FatOpContext* ctx, int first, int last) +{ + int old; + FatFragment new; + + PED_ASSERT (first <= last); + + new = ctx->buffer_map [first]; + for (old = first + 1; old <= last; old++) { + if (ctx->buffer_map [old] == -1) + continue; + new++; + if (ctx->buffer_map [old] != new) + return new; + } + return -1; +} + +/* finds the last fragment that is not going to get overwritten (that needs to + get read in) */ +static FatFragment _GL_ATTRIBUTE_PURE +get_last_underlay (const FatOpContext* ctx, int first, int last) +{ + int old; + FatFragment new; + + PED_ASSERT (first <= last); + + new = ctx->buffer_map [last]; + for (old = last - 1; old >= first; old--) { + if (ctx->buffer_map [old] == -1) + continue; + new--; + if (ctx->buffer_map [old] != new) + return new; + } + return -1; +} + +/* "underlay" refers to the "static" fragments, that remain unchanged. + * when writing large chunks at a time, we don't want to clobber these, + * so we read them in, and write them back again. MUCH quicker that way. + */ +static int +quick_group_write_read_underlay (FatOpContext* ctx, int first, int last) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatFragment first_underlay; + FatFragment last_underlay; + FatFragment underlay_length; + + PED_ASSERT (first <= last); + + first_underlay = get_first_underlay (ctx, first, last); + if (first_underlay == -1) + return 1; + last_underlay = get_last_underlay (ctx, first, last); + + PED_ASSERT (first_underlay <= last_underlay); + + underlay_length = last_underlay - first_underlay + 1; + if (!fat_read_fragments (ctx->new_fs, + new_fs_info->buffer + + (first_underlay - ctx->buffer_map [first]) + * new_fs_info->frag_size, + first_underlay, + underlay_length)) + return 0; + return 1; +} + +/* quick_group_write() makes no attempt to recover from errors - just + * does things fast. If there is an error, slow_group_write() is + * called. + * Note: we do syncing writes, to make sure there isn't any + * error writing out. It's rather difficult recovering from errors + * further on. + */ +static int +quick_group_write (FatOpContext* ctx, int first, int last) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + int active_length; + int i; + int offset; + + PED_ASSERT (first <= last); + + ped_exception_fetch_all (); + if (!quick_group_write_read_underlay (ctx, first, last)) + goto error; + + for (i = first; i <= last; i++) { + if (ctx->buffer_map [i] == -1) + continue; + + offset = ctx->buffer_map [i] - ctx->buffer_map [first]; + memcpy (new_fs_info->buffer + offset * new_fs_info->frag_size, + old_fs_info->buffer + i * new_fs_info->frag_size, + new_fs_info->frag_size); + } + + active_length = ctx->buffer_map [last] - ctx->buffer_map [first] + 1; + if (!fat_write_sync_fragments (ctx->new_fs, new_fs_info->buffer, + ctx->buffer_map [first], active_length)) + goto error; + + ped_exception_leave_all (); + return 1; + +error: + ped_exception_catch (); + ped_exception_leave_all (); + return 0; +} + +/* Writes fragments out, one at a time, avoiding errors on redundant writes + * on damaged parts of the disk we already know about. If there's an error + * on one of the required fragments, it gets marked as bad, and a replacement + * is found. + */ +static int +slow_group_write (FatOpContext* ctx, int first, int last) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + int i; + + PED_ASSERT (first <= last); + + for (i = first; i <= last; i++) { + if (ctx->buffer_map [i] == -1) + continue; + + while (!fat_write_sync_fragment (ctx->new_fs, + old_fs_info->buffer + i * old_fs_info->frag_size, + ctx->buffer_map [i])) { + fat_table_set_bad (new_fs_info->fat, + ctx->buffer_map [i]); + ctx->buffer_map [i] = fat_table_alloc_cluster + (new_fs_info->fat); + if (ctx->buffer_map [i] == 0) + return 0; + } + } + return 1; +} + +static int +update_remap (FatOpContext* ctx, int first, int last) +{ + int i; + + PED_ASSERT (first <= last); + + for (i = first; i <= last; i++) { + if (ctx->buffer_map [i] == -1) + continue; + ctx->remap [ctx->buffer_offset + i] = ctx->buffer_map [i]; + } + + return 1; +} + +static int +group_write (FatOpContext* ctx, int first, int last) +{ + PED_ASSERT (first <= last); + + if (!quick_group_write (ctx, first, last)) { + if (!slow_group_write (ctx, first, last)) + return 0; + } + if (!update_remap (ctx, first, last)) + return 0; + return 1; +} + +/* assumes fragment size and new_fs's cluster size are equal */ +static int +write_fragments (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + int group_start; + int group_end = -1; /* shut gcc up! */ + FatFragment mapped_length; + FatFragment i; + FatCluster new_cluster; + + PED_ASSERT (ctx->buffer_offset < old_fs_info->frag_count); + + group_start = -1; + for (i = 0; i < ctx->buffer_frags; i++) { + if (ctx->buffer_map [i] == -1) + continue; + + ctx->frags_duped++; + + new_cluster = fat_table_alloc_cluster (new_fs_info->fat); + if (!new_cluster) + return 0; + fat_table_set_eof (new_fs_info->fat, new_cluster); + ctx->buffer_map [i] = fat_cluster_to_frag (ctx->new_fs, + new_cluster); + + if (group_start == -1) + group_start = group_end = i; + + PED_ASSERT (ctx->buffer_map [i] + >= ctx->buffer_map [group_start]); + + mapped_length = ctx->buffer_map [i] + - ctx->buffer_map [group_start] + 1; + if (mapped_length <= ctx->buffer_frags) { + group_end = i; + } else { + /* ran out of room in the buffer, so write this group, + * and start a new one... + */ + if (!group_write (ctx, group_start, group_end)) + return 0; + group_start = group_end = i; + } + } + + PED_ASSERT (group_start != -1); + + if (!group_write (ctx, group_start, group_end)) + return 0; + return 1; +} + +/* default all fragments to unmoved + */ +static void +init_remap (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatFragment i; + + for (i = 0; i < old_fs_info->frag_count; i++) + ctx->remap[i] = fat_op_context_map_static_fragment (ctx, i); +} + +static FatFragment +count_frags_to_dup (FatOpContext* ctx) +{ + FatSpecific* fs_info = FAT_SPECIFIC (ctx->old_fs); + FatFragment i; + FatFragment total; + + total = 0; + + for (i = 0; i < fs_info->frag_count; i++) { + if (needs_duplicating (ctx, i)) + total++; + } + + return total; +} + +/* duplicates unreachable file clusters, and all directory clusters + */ +int +fat_duplicate_clusters (FatOpContext* ctx, PedTimer* timer) +{ + FatFragment total_frags_to_dup; + + init_remap (ctx); + total_frags_to_dup = count_frags_to_dup (ctx); + + ped_timer_reset (timer); + ped_timer_set_state_name (timer, "moving data"); + + ctx->buffer_offset = 0; + ctx->frags_duped = 0; + while (search_next_fragment (ctx)) { + ped_timer_update ( + timer, 1.0 * ctx->frags_duped / total_frags_to_dup); + + if (!fetch_fragments (ctx)) + return 0; + if (!write_fragments (ctx)) + return 0; + ctx->buffer_offset += ctx->buffer_frags; + } + + ped_timer_update (timer, 1.0); + return 1; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/fat/clstdup.h b/libparted/fs/r/fat/clstdup.h new file mode 100644 index 0000000..23e51b4 --- /dev/null +++ b/libparted/fs/r/fat/clstdup.h @@ -0,0 +1,28 @@ +/* + libparted + Copyright (C) 1999, 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/>. +*/ + +#ifndef PED_FAT_CLSTDUP_H_INCLUDED +#define PED_FAT_CLSTDUP_H_INCLUDED + +#include "context.h" + +/* the big important one :-) */ +extern int fat_duplicate_clusters (FatOpContext* ctx, PedTimer* timer); + +#endif /* PED_FAT_CLSTDUP_H_INCLUDED */ diff --git a/libparted/fs/r/fat/context.c b/libparted/fs/r/fat/context.c new file mode 100644 index 0000000..c782323 --- /dev/null +++ b/libparted/fs/r/fat/context.c @@ -0,0 +1,261 @@ +/* + libparted + Copyright (C) 1998-2000, 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 <config.h> +#include <string.h> + +#include "fat.h" + +#ifndef DISCOVER_ONLY + +/* Note: this deals with file system start and end sectors, even if the physical + * devices are different (eg for fat_copy()) Perhaps this is a hack, but it + * works ;-) + */ +static int +calc_deltas (FatOpContext* ctx) +{ + PedFileSystem* old_fs = ctx->old_fs; + PedFileSystem* new_fs = ctx->new_fs; + FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs); + PedSector old_cluster_ofs; + PedSector new_cluster_ofs; + PedSector sector_delta; + + old_cluster_ofs = old_fs->geom->start + old_fs_info->cluster_offset; + new_cluster_ofs = new_fs->geom->start + new_fs_info->cluster_offset; + + if (new_cluster_ofs > old_cluster_ofs) { + ctx->start_move_dir = FAT_DIR_FORWARD; + sector_delta = new_cluster_ofs - old_cluster_ofs; + } else { + ctx->start_move_dir = FAT_DIR_BACKWARD; + sector_delta = old_cluster_ofs - new_cluster_ofs; + } + + if (sector_delta % new_fs_info->cluster_sectors) { + ped_exception_throw ( + PED_EXCEPTION_BUG, PED_EXCEPTION_CANCEL, + _("Cluster start delta = %d, which is not a multiple " + "of the cluster size %d."), + (int) sector_delta, + (int) new_fs_info->cluster_sectors); + return 0; + } + + ctx->start_move_delta = sector_delta / ctx->frag_sectors; + +#ifdef PED_VERBOSE + printf ("Start move delta is: %d %s.\n", + (int) ctx->start_move_delta, + (ctx->start_move_dir == FAT_DIR_FORWARD)? + "forwards" : "backwards"); +#endif + + return 1; +} + +FatOpContext* +fat_op_context_new (PedFileSystem* new_fs, PedFileSystem* old_fs) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (new_fs); + FatOpContext* ctx; + + ctx = (FatOpContext*) ped_malloc (sizeof (FatOpContext)); + if (!ctx) + goto error; + + ctx->frag_sectors = PED_MIN (old_fs_info->cluster_sectors, + new_fs_info->cluster_sectors); + if (!fat_set_frag_sectors (new_fs, ctx->frag_sectors)) + goto error; + if (!fat_set_frag_sectors (old_fs, ctx->frag_sectors)) + goto error; + + ctx->buffer_frags = old_fs_info->buffer_sectors / ctx->frag_sectors; + ctx->buffer_map = (FatFragment*) ped_malloc (sizeof (FatFragment) + * ctx->buffer_frags); + if (!ctx->buffer_map) + goto error_free_ctx; + + ctx->remap = (FatFragment*) ped_malloc (sizeof (FatFragment) + * old_fs_info->frag_count); + if (!ctx->remap) + goto error_free_buffer_map; + + ctx->new_fs = new_fs; + ctx->old_fs = old_fs; + if (!calc_deltas (ctx)) + goto error_free_buffer_map; + + return ctx; + +error_free_buffer_map: + free (ctx->buffer_map); +error_free_ctx: + free (ctx); +error: + return NULL; +} + +void +fat_op_context_destroy (FatOpContext* ctx) +{ + free (ctx->buffer_map); + free (ctx->remap); + free (ctx); +} + +FatFragment _GL_ATTRIBUTE_PURE +fat_op_context_map_static_fragment (const FatOpContext* ctx, FatFragment frag) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatFragment result; + + if (ctx->new_fs->geom->dev != ctx->old_fs->geom->dev) + return -1; + + if (ctx->start_move_dir == FAT_DIR_FORWARD) { + if (frag < ctx->start_move_delta) + return -1; + result = frag - ctx->start_move_delta; + } else { + result = frag + ctx->start_move_delta; + } + + if (result >= new_fs_info->frag_count) + return -1; + + return result; +} + +FatCluster +fat_op_context_map_static_cluster (const FatOpContext* ctx, FatCluster clst) +{ + FatFragment mapped_frag; + + mapped_frag = fat_op_context_map_static_fragment (ctx, + fat_cluster_to_frag (ctx->old_fs, clst)); + if (mapped_frag != -1) + return fat_frag_to_cluster (ctx->new_fs, mapped_frag); + else + return 0; +} + +FatFragment _GL_ATTRIBUTE_PURE +fat_op_context_map_fragment (const FatOpContext* ctx, FatFragment frag) +{ + return ctx->remap [frag]; +} + +FatCluster +fat_op_context_map_cluster (const FatOpContext* ctx, FatCluster clst) +{ + FatFragment mapped_frag; + + mapped_frag = fat_op_context_map_fragment (ctx, + fat_cluster_to_frag (ctx->old_fs, clst)); + if (mapped_frag != -1) + return fat_frag_to_cluster (ctx->new_fs, mapped_frag); + else + return 0; +} + +/* This function sets the initial fat for the new resized file system. + This is in *NO WAY* a proper FAT table - all it does is: + a) mark bad clusters as bad. + b) mark used clusters (that is, clusters from the original FS that are + reachable from the resized one). Marks as EOF (i.e. used, end of + file chain). + c) mark original file system metadata as EOF (i.e. used), to prevent + it from being clobbered. This will leave the original file system + intact, until the partition table is modified, if the start of + the partition is moved. + + The FATs are rebuilt *properly* after cluster relocation. This here is + only to mark clusters as used, so when cluster relocation occurs, clusters + aren't relocated on top of ones marked in a, b or c. +*/ +int +fat_op_context_create_initial_fat (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatCluster clst; + FatCluster new_clst; + PedSector sect; + PedSector new_sect; + FatFragment frag; + FatFragment new_frag; + FatClusterFlag frag_flag; + + new_fs_info->fat = fat_table_new ( + new_fs_info->fat_type, + new_fs_info->fat_sectors * 512 + / fat_table_entry_size (new_fs_info->fat_type)); + if (!new_fs_info->fat) + return 0; + + if (!fat_table_set_cluster_count (new_fs_info->fat, + new_fs_info->cluster_count)) + return 0; + +/* mark bad and used clusters */ + for (frag = 0; frag < old_fs_info->frag_count; frag++) { + frag_flag = fat_get_fragment_flag (ctx->old_fs, frag); + if (frag_flag == FAT_FLAG_FREE) + continue; + + new_frag = fat_op_context_map_static_fragment (ctx, frag); + if (new_frag == -1) + continue; + + new_clst = fat_frag_to_cluster (ctx->new_fs, new_frag); + PED_ASSERT (new_clst != 0); + + if (frag_flag == FAT_FLAG_BAD) { + if (!fat_table_set_bad (new_fs_info->fat, new_clst)) + return 0; + } else { + if (!fat_table_set_eof (new_fs_info->fat, new_clst)) + return 0; + } + } + +/* mark metadata regions that map to clusters on the new FS */ + for (sect = 0; sect < old_fs_info->cluster_offset; sect++) { + new_sect = ped_geometry_map (ctx->new_fs->geom, + ctx->old_fs->geom, sect); + if (new_sect == -1 + || !fat_is_sector_in_clusters (ctx->new_fs, new_sect)) + continue; + + clst = fat_sector_to_cluster (ctx->new_fs, new_sect); + PED_ASSERT (clst != 0); + + if (!fat_table_set_eof (new_fs_info->fat, clst)) + return 0; + } + + return 1; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/fat/context.h b/libparted/fs/r/fat/context.h new file mode 100644 index 0000000..9a76a47 --- /dev/null +++ b/libparted/fs/r/fat/context.h @@ -0,0 +1,70 @@ +/* + libparted + Copyright (C) 1999-2000, 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/>. +*/ + +#ifndef PED_FAT_CONTEXT_H_INCLUDED +#define PED_FAT_CONTEXT_H_INCLUDED + +#include "count.h" + +enum _FatDirection { + FAT_DIR_FORWARD, + FAT_DIR_BACKWARD +}; +typedef enum _FatDirection FatDirection; + +struct _FatOpContext { + PedFileSystem* old_fs; + PedFileSystem* new_fs; + + PedSector frag_sectors; /* should equal old_fs and + new_fs's frag_sectors */ + + FatDirection start_move_dir; + FatFragment start_move_delta; + + FatFragment buffer_offset; + FatFragment buffer_frags; + FatFragment* buffer_map; + + FatFragment frags_duped; + + FatFragment* remap; + + FatCluster new_root_dir [32]; +}; +typedef struct _FatOpContext FatOpContext; + +extern FatOpContext* fat_op_context_new (PedFileSystem* new_fs, + PedFileSystem* old_fs); + +extern void fat_op_context_destroy (FatOpContext* ctx); + +extern FatFragment fat_op_context_map_static_fragment (const FatOpContext* ctx, + FatFragment frag); +extern FatCluster fat_op_context_map_static_cluster (const FatOpContext* ctx, + FatCluster clst); + +extern FatFragment fat_op_context_map_fragment (const FatOpContext* ctx, + FatFragment frag); +extern FatCluster fat_op_context_map_cluster (const FatOpContext* ctx, + FatCluster clst); + +extern int fat_op_context_create_initial_fat (FatOpContext* ctx); + +#endif /* PED_FAT_CONTEXT_H_INCLUDED */ diff --git a/libparted/fs/r/fat/count.c b/libparted/fs/r/fat/count.c new file mode 100644 index 0000000..e23404b --- /dev/null +++ b/libparted/fs/r/fat/count.c @@ -0,0 +1,319 @@ +/* + libparted + Copyright (C) 1998-2000, 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 <config.h> +#include "fat.h" +#include "traverse.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef DISCOVER_ONLY + +/* + prints out the sequence of clusters for a given file chain, beginning + at start_cluster. +*/ +#ifdef PED_VERBOSE +static void +print_chain (PedFileSystem* fs, FatCluster start) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster clst; + int this_row; + + this_row = 0; + for (clst = start; !fat_table_is_eof (fs_info->fat, clst); + clst = fat_table_get (fs_info->fat, clst)) { + printf (" %d", (int) clst); + if (++this_row == 7) { + putchar ('\n'); + this_row = 0; + } + } + putchar ('\n'); +} +#endif /* PED_VERBOSE */ + +static PedSector +remainder_round_up (PedSector a, PedSector b) +{ + PedSector result; + + result = a % b; + if (!result) + result = b; + return result; +} + +/* + traverse the FAT for a file/directory, marking each entry's flag + to "flag". +*/ +static int +flag_traverse_fat (PedFileSystem* fs, const char* chain_name, FatCluster start, + FatClusterFlag flag, PedSector size) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster clst; + FatCluster prev_clst; + int last_cluster_usage; + FatCluster chain_length = 0; + + if (fat_table_is_eof (fs_info->fat, start)) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Bad directory entry for %s: first cluster is the " + "end of file marker."), + chain_name) + != PED_EXCEPTION_IGNORE) + return 0; + } + + for (prev_clst = clst = start; !fat_table_is_eof (fs_info->fat, clst); + prev_clst = clst, clst = fat_table_get (fs_info->fat, clst)) { + chain_length++; + if (!clst) { + ped_exception_throw (PED_EXCEPTION_FATAL, + PED_EXCEPTION_CANCEL, + _("Bad FAT: unterminated chain for %s. You " + "should run dosfsck or scandisk."), + chain_name); + return 0; + } + + if (clst >= fs_info->fat->cluster_count + 2) { + ped_exception_throw (PED_EXCEPTION_FATAL, + PED_EXCEPTION_CANCEL, + _("Bad FAT: cluster %d outside file system " + "in chain for %s. You should run dosfsck " + "or scandisk."), + (int) clst, chain_name); + return 0; + } + + if (fs_info->cluster_info [clst].flag != FAT_FLAG_FREE ) { + ped_exception_throw (PED_EXCEPTION_FATAL, + PED_EXCEPTION_CANCEL, + _("Bad FAT: cluster %d is cross-linked for " + "%s. You should run dosfsck or scandisk."), + (int) clst, chain_name); + return 0; + } + + if (flag == FAT_FLAG_DIRECTORY) + fs_info->total_dir_clusters++; + + fs_info->cluster_info [clst].flag = flag; + fs_info->cluster_info [clst].units_used = 0; /* 0 == 64 */ + } + + if (size + && chain_length + != ped_div_round_up (size, fs_info->cluster_sectors)) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("%s is %dk, but it has %d clusters (%dk)."), + chain_name, + (int) size / 2, + (int) chain_length, + (int) chain_length * fs_info->cluster_sectors / 2) + != PED_EXCEPTION_IGNORE) + return 0; + } + + last_cluster_usage + = ped_div_round_up (64 * remainder_round_up (size, + fs_info->cluster_sectors), + fs_info->cluster_sectors); + + fs_info->cluster_info [prev_clst].units_used = last_cluster_usage; + + return 1; +} + +/* + recursively traverses a directory, flagging all clusters in the process. + It frees the traverse_info structure before returning. +*/ +static int +flag_traverse_dir (FatTraverseInfo* trav_info) { + PedFileSystem* fs = trav_info->fs; + FatDirEntry* this_entry; + FatTraverseInfo* subdir_trav_info; + char file_name [4096]; + char* file_name_start; + FatCluster first_cluster; + PedSector size; + + PED_ASSERT (trav_info != NULL); + + strcpy (file_name, trav_info->dir_name); + file_name_start = file_name + strlen (file_name); + + while ( (this_entry = fat_traverse_next_dir_entry (trav_info)) ) { + if (fat_dir_entry_is_null_term (this_entry)) + break; + if (!fat_dir_entry_has_first_cluster (this_entry, fs)) + continue; + if (this_entry->name [0] == '.') + continue; /* skip . and .. entries */ + + fat_dir_entry_get_name (this_entry, file_name_start); + first_cluster = fat_dir_entry_get_first_cluster(this_entry, fs); + size = ped_div_round_up (fat_dir_entry_get_length (this_entry), + 512); + +#ifdef PED_VERBOSE + printf ("%s: ", file_name); + print_chain (fs, first_cluster); +#endif + + if (fat_dir_entry_is_directory (this_entry)) { + if (!flag_traverse_fat (fs, file_name, first_cluster, + FAT_FLAG_DIRECTORY, size)) + return 0; + + subdir_trav_info = fat_traverse_directory (trav_info, + this_entry); + if (!subdir_trav_info) + return 0; + if (!flag_traverse_dir (subdir_trav_info)) + return 0; + } else if (fat_dir_entry_is_file (this_entry)) { + if (!flag_traverse_fat (fs, file_name, first_cluster, + FAT_FLAG_FILE, size)) + return 0; + } + } + + fat_traverse_complete (trav_info); + return 1; +} + +static void +_mark_bad_clusters (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster cluster; + + for (cluster = 2; cluster < fs_info->cluster_count + 2; cluster++) { + if (fat_table_is_bad (fs_info->fat, cluster)) + fs_info->cluster_info [cluster].flag = FAT_FLAG_BAD; + } +} + +/* + fills in cluster_info. Each FAT entry (= cluster) is flagged as either + FAT_FLAG_FREE, FAT_FLAG_FILE or FAT_FLAG_DIRECTORY. + + Also, the fraction of each cluster (x/64) is recorded +*/ +int +fat_collect_cluster_info (PedFileSystem* fs) { + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatTraverseInfo* trav_info; + + /* set all clusters to unused as a default */ + memset (fs_info->cluster_info, 0, fs_info->fat->cluster_count + 2); + fs_info->total_dir_clusters = 0; + + if (fs_info->fat_type == FAT_TYPE_FAT32) { + trav_info = fat_traverse_begin (fs, fs_info->root_cluster, + "\\"); + if (!flag_traverse_dir (trav_info)) + return 0; + if (!flag_traverse_fat (fs, "\\", fs_info->root_cluster, + FAT_FLAG_DIRECTORY, 0)) + return 0; + } else { + trav_info = fat_traverse_begin (fs, FAT_ROOT, "\\"); + if (!flag_traverse_dir (trav_info)) + return 0; + } + + _mark_bad_clusters (fs); + return 1; +} + +FatClusterFlag _GL_ATTRIBUTE_PURE +fat_get_cluster_flag (PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + return fs_info->cluster_info [cluster].flag; +} + +PedSector _GL_ATTRIBUTE_PURE +fat_get_cluster_usage (PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int fraction; + + if (fs_info->cluster_info [cluster].flag == FAT_FLAG_FREE) + return 0; + + fraction = fs_info->cluster_info [cluster].units_used; + if (fraction == 0) + fraction = 64; + + return fraction * fs_info->cluster_sectors / 64; +} + +FatClusterFlag +fat_get_fragment_flag (PedFileSystem* fs, FatFragment frag) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster cluster = fat_frag_to_cluster (fs, frag); + FatFragment offset = frag % fs_info->cluster_frags; + FatFragment last_frag_used; + FatClusterFlag flag; + + PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2); + + flag = fat_get_cluster_flag (fs, cluster); + if (flag != FAT_FLAG_FILE && flag != FAT_FLAG_DIRECTORY) + return flag; + last_frag_used = (fat_get_cluster_usage (fs, cluster) - 1) + / fs_info->frag_sectors; + if (offset > last_frag_used) + return FAT_FLAG_FREE; + else + return flag; +} + +int +fat_is_fragment_active (PedFileSystem* fs, FatFragment frag) +{ + switch (fat_get_fragment_flag (fs, frag)) { + case FAT_FLAG_FREE: + case FAT_FLAG_BAD: + return 0; + + case FAT_FLAG_FILE: + case FAT_FLAG_DIRECTORY: + return 1; + } + return 0; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/fat/count.h b/libparted/fs/r/fat/count.h new file mode 100644 index 0000000..bb7d6af --- /dev/null +++ b/libparted/fs/r/fat/count.h @@ -0,0 +1,46 @@ +/* + libparted + 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/>. +*/ + +#ifndef COUNT_H_INCLUDED +#define COUNT_H_INCLUDED + +typedef enum _FatClusterFlag FatClusterFlag; +typedef struct _FatClusterInfo FatClusterInfo; + +enum _FatClusterFlag { + FAT_FLAG_FREE=0, + FAT_FLAG_FILE=1, + FAT_FLAG_DIRECTORY=2, + FAT_FLAG_BAD=3 +}; + +struct __attribute__ ((packed)) _FatClusterInfo { + unsigned int units_used:6; /* 1 unit = cluster_size / 64 */ + FatClusterFlag flag:2; +}; + +extern int fat_collect_cluster_info (PedFileSystem *fs); +extern FatClusterFlag fat_get_cluster_flag (PedFileSystem* fs, + FatCluster cluster); +extern PedSector fat_get_cluster_usage (PedFileSystem* fs, FatCluster cluster); +extern FatClusterFlag fat_get_fragment_flag (PedFileSystem* fs, + FatFragment frag); +extern int fat_is_fragment_active (PedFileSystem* fs, FatFragment frag); + +#endif /* COUNT_H_INCLUDED */ diff --git a/libparted/fs/r/fat/fat.c b/libparted/fs/r/fat/fat.c new file mode 100644 index 0000000..6583b5b --- /dev/null +++ b/libparted/fs/r/fat/fat.c @@ -0,0 +1,652 @@ +/* + libparted + Copyright (C) 1998-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 <string.h> + +#include "fat.h" +#include "calc.h" +#include "../../../labels/misc.h" + +PedFileSystem* +fat_alloc (const PedGeometry* geom) +{ + PedFileSystem* fs; + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) + goto error; + + fs->type_specific = (FatSpecific*) ped_malloc (sizeof (FatSpecific)); + if (!fs->type_specific) + goto error_free_fs; + FatSpecific* fs_info = (FatSpecific*) fs->type_specific; + fs_info->boot_sector = NULL; + fs_info->info_sector = NULL; + fs->geom = ped_geometry_duplicate (geom); + if (!fs->geom) + goto error_free_type_specific; + + fs->checked = 0; + return fs; + +error_free_type_specific: + free (fs->type_specific); +error_free_fs: + free (fs); +error: + return NULL; +} + +/* Requires the boot sector to be analysed */ +int +fat_alloc_buffers (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + fs_info->buffer_sectors = BUFFER_SIZE; + fs_info->buffer = ped_malloc (fs_info->buffer_sectors * 512); + if (!fs_info->buffer) + goto error; + + fs_info->cluster_info = ped_malloc (fs_info->cluster_count + 2); + if (!fs_info->cluster_info) + goto error_free_buffer; + + return 1; + +error_free_buffer: + free (fs_info->buffer); +error: + return 0; +}; + +void +fat_free_buffers (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + free (fs_info->cluster_info); + free (fs_info->buffer); +} + +void +fat_free (PedFileSystem* fs) +{ + FatSpecific* fs_info = (FatSpecific*) fs->type_specific; + free (fs_info->boot_sector); + ped_geometry_destroy (fs->geom); + free (fs->type_specific); + free (fs); +} + +int +fat_set_frag_sectors (PedFileSystem* fs, PedSector frag_sectors) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (fs_info->cluster_sectors % frag_sectors == 0 + && frag_sectors <= fs_info->cluster_sectors); + + fs_info->frag_size = frag_sectors * 512; + fs_info->frag_sectors = frag_sectors; + fs_info->buffer_frags = fs_info->buffer_sectors / frag_sectors; + fs_info->cluster_frags = fs_info->cluster_sectors / frag_sectors; + fs_info->frag_count = fs_info->cluster_count * fs_info->cluster_frags; + + return 1; +} + +#ifndef DISCOVER_ONLY +int +fat_clobber (PedGeometry* geom) +{ + FatBootSector *boot_sector; + int ok; + + if (!fat_boot_sector_read (&boot_sector, geom)) + return 1; + + boot_sector->system_id[0] = 0; + boot_sector->boot_sign = 0; + if (boot_sector->u.fat16.fat_name[0] == 'F') + boot_sector->u.fat16.fat_name[0] = 0; + if (boot_sector->u.fat32.fat_name[0] == 'F') + boot_sector->u.fat32.fat_name[0] = 0; + + ok = ped_geometry_write (geom, boot_sector, 0, 1); + free (boot_sector); + return ok; +} + +static int +_init_fats (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster table_size; + + table_size = fs_info->fat_sectors * 512 + / fat_table_entry_size (fs_info->fat_type); + fs_info->fat = fat_table_new (fs_info->fat_type, table_size); + if (!fs_info->fat) + goto error; + + if (!fat_table_read (fs_info->fat, fs, 0)) + goto error_free_fat; + + return 1; + +error_free_fat: + fat_table_destroy (fs_info->fat); +error: + return 0; +} + +PedFileSystem* +fat_open (PedGeometry* geom) +{ + PedFileSystem* fs; + FatSpecific* fs_info; + + fs = fat_alloc (geom); + if (!fs) + goto error; + fs_info = (FatSpecific*) fs->type_specific; + + if (!fat_boot_sector_read (&fs_info->boot_sector, geom)) + goto error_free_fs; + if (!fat_boot_sector_analyse (fs_info->boot_sector, fs)) + goto error_free_fs; + fs->type = (fs_info->fat_type == FAT_TYPE_FAT16) + ? &fat16_type + : &fat32_type; + if (fs_info->fat_type == FAT_TYPE_FAT32) { + if (!fat_info_sector_read (&fs_info->info_sector, fs)) + goto error_free_fs; + } + + if (!_init_fats (fs)) + goto error_free_fs; + if (!fat_alloc_buffers (fs)) + goto error_free_fat_table; + if (!fat_collect_cluster_info (fs)) + goto error_free_buffers; + + return fs; + +error_free_buffers: + fat_free_buffers (fs); +error_free_fat_table: + fat_table_destroy (fs_info->fat); +error_free_fs: + fat_free (fs); +error: + return NULL; +} + +static int +fat_root_dir_clear (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + memset (fs_info->buffer, 0, 512 * fs_info->root_dir_sector_count); + return ped_geometry_write (fs->geom, fs_info->buffer, + fs_info->root_dir_offset, + fs_info->root_dir_sector_count); +} + +PedFileSystem* +fat_create (PedGeometry* geom, FatType fat_type, PedTimer* timer) +{ + PedFileSystem* fs; + FatSpecific* fs_info; + FatCluster table_size; + + fs = fat_alloc (geom); + if (!fs) + goto error; + fs_info = (FatSpecific*) fs->type_specific; + + fs_info->logical_sector_size = 1; + fs_info->sectors_per_track = geom->dev->bios_geom.sectors; + fs_info->heads = geom->dev->bios_geom.heads; + fs_info->sector_count = fs->geom->length; + fs_info->fat_table_count = 2; +/* some initial values, to be changed later */ + fs_info->root_dir_sector_count = FAT_ROOT_DIR_ENTRY_COUNT + / (512 / sizeof (FatDirEntry)); + fs_info->root_dir_entry_count = FAT_ROOT_DIR_ENTRY_COUNT; + + fs_info->fat_type = fat_type; + if (!fat_calc_sizes (fs->geom->length, 0, + fs_info->fat_type, + fs_info->root_dir_sector_count, + &fs_info->cluster_sectors, + &fs_info->cluster_count, + &fs_info->fat_sectors)) { + ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Partition too big/small for a %s file system."), + (fat_type == FAT_TYPE_FAT16) + ? fat16_type.name + : fat32_type.name); + goto error_free_fs; + } + + fs_info->cluster_size = fs_info->cluster_sectors * 512; + + fs_info->fat_offset = fat_min_reserved_sector_count (fs_info->fat_type); + fs_info->dir_entries_per_cluster + = fs_info->cluster_size / sizeof (FatDirEntry); + + if (fs_info->fat_type == FAT_TYPE_FAT16) { + /* FAT16 */ + fs->type = &fat16_type; + + if (fs_info->cluster_count + > fat_max_cluster_count (fs_info->fat_type)) { + fs_info->cluster_count + = fat_max_cluster_count (fs_info->fat_type); + } + + fs_info->root_dir_sector_count + = FAT_ROOT_DIR_ENTRY_COUNT + / (512 / sizeof (FatDirEntry)); + fs_info->root_dir_entry_count = FAT_ROOT_DIR_ENTRY_COUNT; + fs_info->root_dir_offset + = fs_info->fat_offset + + fs_info->fat_sectors * fs_info->fat_table_count; + fs_info->cluster_offset + = fs_info->root_dir_offset + + fs_info->root_dir_sector_count; + } else { + /* FAT32 */ + fs->type = &fat32_type; + + fs_info->info_sector_offset = 1; + fs_info->boot_sector_backup_offset = 6; + + fs_info->root_dir_sector_count = 0; + fs_info->root_dir_entry_count = 0; + fs_info->root_dir_offset = 0; + + fs_info->cluster_offset + = fs_info->fat_offset + + fs_info->fat_sectors * fs_info->fat_table_count; + } + + table_size = fs_info->fat_sectors * 512 + / fat_table_entry_size (fs_info->fat_type); + fs_info->fat = fat_table_new (fs_info->fat_type, table_size); + if (!fs_info->fat) + goto error_free_fs; + fat_table_set_cluster_count (fs_info->fat, fs_info->cluster_count); + if (!fat_alloc_buffers (fs)) + goto error_free_fat_table; + + if (fs_info->fat_type == FAT_TYPE_FAT32) { + fs_info->root_cluster + = fat_table_alloc_cluster (fs_info->fat); + fat_table_set_eof (fs_info->fat, fs_info->root_cluster); + memset (fs_info->buffer, 0, fs_info->cluster_size); + if (!fat_write_cluster (fs, fs_info->buffer, + fs_info->root_cluster)) + goto error_free_buffers; + } + + fs_info->serial_number = generate_random_uint32 (); + + if (!fat_boot_sector_set_boot_code (&fs_info->boot_sector, fs)) + goto error_free_buffers; + if (!fat_boot_sector_generate (&fs_info->boot_sector, fs)) + goto error_free_buffers; + if (!fat_boot_sector_write (fs_info->boot_sector, fs)) + goto error_free_buffers; + if (fs_info->fat_type == FAT_TYPE_FAT32) { + if (!fat_info_sector_generate (&fs_info->info_sector, fs)) + goto error_free_buffers; + if (!fat_info_sector_write (fs_info->info_sector, fs)) + goto error_free_buffers; + } + + if (!fat_table_write_all (fs_info->fat, fs)) + goto error_free_buffers; + + if (fs_info->fat_type == FAT_TYPE_FAT16) { + if (!fat_root_dir_clear (fs)) + goto error_free_buffers; + } + + return fs; + +error_free_buffers: + fat_free_buffers (fs); +error_free_fat_table: + fat_table_destroy (fs_info->fat); +error_free_fs: + fat_free (fs); +error: + return NULL; +} + +PedFileSystem* +fat_create_fat16 (PedGeometry* geom, PedTimer* timer) +{ + return fat_create (geom, FAT_TYPE_FAT16, timer); +} + +PedFileSystem* +fat_create_fat32 (PedGeometry* geom, PedTimer* timer) +{ + return fat_create (geom, FAT_TYPE_FAT32, timer); +} + +int +fat_close (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + fat_free_buffers (fs); + fat_table_destroy (fs_info->fat); + fat_free (fs); + return 1; +} + +/* Hack: just resize the file system outside of its boundaries! */ +PedFileSystem* +fat_copy (const PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + PedFileSystem* new_fs; + + new_fs = ped_file_system_open (fs->geom); + if (!new_fs) + goto error; + if (!ped_file_system_resize (new_fs, geom, timer)) + goto error_close_new_fs; + return new_fs; + +error_close_new_fs: + ped_file_system_close (new_fs); +error: + return 0; +} + +static int +_compare_fats (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatTable* table_copy; + FatCluster table_size; + int i; + + table_size = fs_info->fat_sectors * 512 + / fat_table_entry_size (fs_info->fat_type); + + table_copy = fat_table_new (fs_info->fat_type, table_size); + if (!table_copy) + goto error; + + for (i = 1; i < fs_info->fat_table_count; i++) { + if (!fat_table_read (table_copy, fs, i)) + goto error_free_table_copy; + if (!fat_table_compare (fs_info->fat, table_copy)) { + if (ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("The FATs don't match. If you don't know " + "what this means, then select cancel, run " + "scandisk on the file system, and then come " + "back.")) + != PED_EXCEPTION_IGNORE) + goto error_free_table_copy; + } + } + + fat_table_destroy (table_copy); + return 1; + +error_free_table_copy: + fat_table_destroy (table_copy); +error: + return 0; +} + +int +fat_check (PedFileSystem* fs, PedTimer* timer) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector cluster_sectors; + FatCluster cluster_count; + PedSector fat_sectors; + PedSector align_sectors; + FatCluster info_free_clusters; + + align_sectors = fs_info->fat_offset + - fat_min_reserved_sector_count (fs_info->fat_type); + + if (!fat_calc_sizes (fs->geom->length, + align_sectors, + fs_info->fat_type, + fs_info->root_dir_sector_count, + &cluster_sectors, + &cluster_count, + &fat_sectors)) { + if (ped_exception_throw (PED_EXCEPTION_BUG, + PED_EXCEPTION_IGNORE_CANCEL, + _("There are no possible configurations for this FAT " + "type.")) + != PED_EXCEPTION_IGNORE) + goto error; + } + + if (fs_info->fat_type == FAT_TYPE_FAT16) { + if (cluster_sectors != fs_info->cluster_sectors + || cluster_count != fs_info->cluster_count + || fat_sectors != fs_info->fat_sectors) { + if (ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("File system doesn't have expected sizes for " + "Windows to like it. " + "Cluster size is %dk (%dk expected); " + "number of clusters is %d (%d expected); " + "size of FATs is %d sectors (%d expected)."), + (int) fs_info->cluster_sectors / 2, + (int) cluster_sectors / 2, + (int) fs_info->cluster_count, + (int) cluster_count, + (int) fs_info->fat_sectors, + (int) fat_sectors) + != PED_EXCEPTION_IGNORE) + goto error; + } + } + + if (fs_info->fat_type == FAT_TYPE_FAT32) { + info_free_clusters + = PED_LE32_TO_CPU (fs_info->info_sector->free_clusters); + if (info_free_clusters != (FatCluster) -1 + && info_free_clusters != fs_info->fat->free_cluster_count) { + if (ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("File system is reporting the free space as " + "%d clusters, not %d clusters."), + info_free_clusters, + fs_info->fat->free_cluster_count) + != PED_EXCEPTION_IGNORE) + goto error; + } + } + + if (!_compare_fats (fs)) + goto error; + + fs->checked = 1; + return 1; /* existence of fs implies consistency ;-) */ + +error: + return 0; +} + +/* Calculates how much space there will be in clusters in: + * old_fs intersect the-new-fs + */ +static PedSector +_calc_resize_data_size ( + const PedFileSystem* old_fs, + PedSector new_cluster_sectors, + FatCluster new_cluster_count, + PedSector new_fat_size) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (old_fs); + PedSector fat_size_delta; + + fat_size_delta = old_fs_info->fat_sectors - new_fat_size; + return new_cluster_sectors * new_cluster_count - fat_size_delta * 2; +} + +static int +_test_resize_size (const PedFileSystem* fs, + PedSector length, PedSector min_data_size) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedGeometry geom; + PedSector _cluster_sectors; + FatCluster _cluster_count; + PedSector _fat_size; + + ped_geometry_init (&geom, fs->geom->dev, fs->geom->start, length); + + if (fat_calc_resize_sizes ( + &geom, + fs_info->cluster_sectors, + FAT_TYPE_FAT16, + fs_info->root_dir_sector_count, + fs_info->cluster_sectors, + &_cluster_sectors, + &_cluster_count, + &_fat_size) + && _calc_resize_data_size (fs, _cluster_sectors, _cluster_count, + _fat_size) + >= min_data_size) + return 1; + + if (fat_calc_resize_sizes ( + &geom, + fs_info->cluster_sectors, + FAT_TYPE_FAT32, + 0, + fs_info->cluster_sectors, + &_cluster_sectors, + &_cluster_count, + &_fat_size) + && _calc_resize_data_size (fs, _cluster_sectors, _cluster_count, + _fat_size) + >= min_data_size) + return 1; + + return 0; +} + +/* does a binary search (!) for the mininum size. Too hard to compute directly + * (see calc_sizes() for why!) + */ +static PedSector +_get_min_resize_size (const PedFileSystem* fs, PedSector min_data_size) +{ + PedSector min_length = 0; + PedSector max_length = fs->geom->length; + PedSector length; + + while (min_length < max_length - 1) { + length = (min_length + max_length) / 2; + if (_test_resize_size (fs, length, min_data_size)) + max_length = length; + else + min_length = length; + } + +/* adds a bit of leeway (64 sectors), for resolving extra issues, like root + * directory allocation, that aren't covered here. + */ + return max_length + 64; +} + +PedConstraint* +fat_get_copy_constraint (const PedFileSystem* fs, const PedDevice* dev) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedGeometry full_dev; + PedSector min_cluster_count; + FatCluster used_clusters; + PedSector min_data_size; + + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + + used_clusters = fs_info->fat->cluster_count + - fs_info->fat->free_cluster_count; + min_cluster_count = used_clusters + fs_info->total_dir_clusters; + min_data_size = min_cluster_count * fs_info->cluster_sectors; + + return ped_constraint_new (ped_alignment_any, ped_alignment_any, + &full_dev, &full_dev, + _get_min_resize_size (fs, min_data_size), + dev->length); +} + +PedConstraint* +fat_get_resize_constraint (const PedFileSystem* fs) +{ + return fat_get_copy_constraint (fs, fs->geom->dev); +} + +PedConstraint* +fat_get_create_constraint_fat16 (const PedDevice* dev) +{ + PedGeometry full_dev; + PedSector min_size; + PedSector max_size; + + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + + min_size = 65794; + max_size = 2097153; + + return ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + &full_dev, &full_dev, + min_size, max_size); +} + +PedConstraint* +fat_get_create_constraint_fat32 (const PedDevice* dev) +{ + PedGeometry full_dev; + PedSector min_size; + + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + + min_size = 525224; + + return ped_constraint_new ( + ped_alignment_any, ped_alignment_any, + &full_dev, &full_dev, + min_size, dev->length); +} +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/fat/fat.h b/libparted/fs/r/fat/fat.h new file mode 100644 index 0000000..54f0669 --- /dev/null +++ b/libparted/fs/r/fat/fat.h @@ -0,0 +1,159 @@ +/* + libparted + Copyright (C) 1998-2001, 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/>. +*/ + +#ifndef FAT_H_INCLUDED +#define FAT_H_INCLUDED + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#define BUFFER_SIZE 1024 /* buffer size in sectors (512 bytes) */ + +typedef uint32_t FatCluster; +typedef int32_t FatFragment; + +enum _FatType { + FAT_TYPE_FAT12, + FAT_TYPE_FAT16, + FAT_TYPE_FAT32 +}; +typedef enum _FatType FatType; + +typedef struct _FatSpecific FatSpecific; +typedef struct _FatDirEntry FatDirEntry; + +/* FIXME: YUCKY */ +#include "table.h" +#include "bootsector.h" +#include "context.h" +#include "fatio.h" +#include "traverse.h" +#include "calc.h" +#include "count.h" +#include "clstdup.h" + +struct __attribute__ ((packed)) _FatDirEntry { + char name[8]; + uint8_t extension[3]; + uint8_t attributes; + uint8_t is_upper_case_name; + uint8_t creation_time_low; /* milliseconds */ + uint16_t creation_time_high; + uint16_t creation_date; + uint16_t access_date; + uint16_t first_cluster_high; /* for FAT32 */ + uint16_t time; + uint16_t date; + uint16_t first_cluster; + uint32_t length; +}; + +struct _FatSpecific { + FatBootSector *boot_sector; /* structure of boot sector */ + FatInfoSector *info_sector; /* fat32-only information sector */ + + int logical_sector_size; /* illogical sector size :-) */ + PedSector sector_count; + + int sectors_per_track; /* BIOS CHS stuff (S) */ + int heads; /* BIOS CHS stuff (H) */ + + int cluster_size; + PedSector cluster_sectors; + FatCluster cluster_count; + int dir_entries_per_cluster; + + FatType fat_type; + int fat_table_count; + PedSector fat_sectors; + + uint32_t serial_number; + + PedSector info_sector_offset; /* FAT32 only */ + PedSector fat_offset; + PedSector root_dir_offset; /* non-FAT32 */ + PedSector cluster_offset; + PedSector boot_sector_backup_offset; + + FatCluster root_cluster; /* FAT32 only */ + int root_dir_entry_count; /* non-FAT32 */ + PedSector root_dir_sector_count; /* non-FAT32 */ + FatCluster total_dir_clusters; + + FatTable* fat; + FatClusterInfo* cluster_info; + + PedSector buffer_sectors; + char* buffer; + + int frag_size; + PedSector frag_sectors; + FatFragment frag_count; + FatFragment buffer_frags; + FatFragment cluster_frags; +}; + +#define FAT_SPECIFIC(fs) ((FatSpecific*) fs->type_specific) + +#define FAT_ROOT 0 + +#define DELETED_FLAG 0xe5 + +#define READONLY_ATTR 0x01 +#define HIDDEN_ATTR 0x02 +#define SYSTEM_ATTR 0x04 +#define VOLUME_LABEL_ATTR 0x08 +#define VFAT_ATTR 0x0f +#define DIRECTORY_ATTR 0x10 +#define ARCH_ATTR 0x20 + +#define MAX_FAT12_CLUSTERS 4086 +#define MAX_FAT16_CLUSTERS 65526 +#define MAX_FAT32_CLUSTERS 2000000 + +#define FAT_ROOT_DIR_ENTRY_COUNT 512 + +extern PedFileSystemType fat16_type; +extern PedFileSystemType fat32_type; + +extern void fat_print (const PedFileSystem* fs); + +extern PedFileSystem* fat_alloc (const PedGeometry* geom); +extern void fat_free (PedFileSystem* fs); +extern int fat_alloc_buffers (PedFileSystem* fs); +extern void fat_free_buffers (PedFileSystem* fs); + +extern int fat_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer); + +extern int fat_set_frag_sectors (PedFileSystem* fs, PedSector frag_sectors); + +#endif /* FAT_H_INCLUDED */ diff --git a/libparted/fs/r/fat/fatio.c b/libparted/fs/r/fat/fatio.c new file mode 100644 index 0000000..3a947ff --- /dev/null +++ b/libparted/fs/r/fat/fatio.c @@ -0,0 +1,150 @@ +/* + libparted + Copyright (C) 1998-2000, 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 <config.h> +#include "fat.h" +#include "fatio.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> + +#ifndef DISCOVER_ONLY + +int +fat_read_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector sector = fat_frag_to_sector (fs, frag); + PedSector sector_count = count * fs_info->frag_sectors; + + PED_ASSERT (frag >= 0 && frag < fs_info->frag_count); + + return ped_geometry_read (fs->geom, buf, sector, sector_count); +} + +int +fat_read_fragment (PedFileSystem* fs, char* buf, FatFragment frag) +{ + return fat_read_fragments (fs, buf, frag, 1); +} + +int +fat_write_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector sector = fat_frag_to_sector (fs, frag); + PedSector sector_count = count * fs_info->frag_sectors; + + PED_ASSERT (frag >= 0 && frag < fs_info->frag_count); + + return ped_geometry_write (fs->geom, buf, sector, sector_count); +} + +int +fat_write_fragment (PedFileSystem* fs, char* buf, FatFragment frag) +{ + return fat_write_fragments (fs, buf, frag, 1); +} + +int +fat_write_sync_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count) +{ + if (!fat_write_fragments (fs, buf, frag, count)) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + return 1; +} + +int +fat_write_sync_fragment (PedFileSystem* fs, char* buf, FatFragment frag) +{ + return fat_write_sync_fragments (fs, buf, frag, 1); +} + +int +fat_read_clusters (PedFileSystem* fs, char *buf, FatCluster cluster, + FatCluster count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector sector = fat_cluster_to_sector (fs, cluster); + PedSector sector_count = count * fs_info->cluster_sectors; + + PED_ASSERT (cluster >= 2 + && cluster + count - 1 < fs_info->cluster_count + 2); + + return ped_geometry_read (fs->geom, buf, sector, sector_count); +} + +int +fat_read_cluster (PedFileSystem* fs, char *buf, FatCluster cluster) +{ + return fat_read_clusters (fs, buf, cluster, 1); +} + +int +fat_write_clusters (PedFileSystem* fs, char *buf, FatCluster cluster, + FatCluster count) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector sector = fat_cluster_to_sector (fs, cluster); + PedSector sector_count = count * fs_info->cluster_sectors; + + PED_ASSERT (cluster >= 2 + && cluster + count - 1 < fs_info->cluster_count + 2); + + return ped_geometry_write (fs->geom, buf, sector, sector_count); +} + +int +fat_write_cluster (PedFileSystem* fs, char *buf, FatCluster cluster) +{ + return fat_write_clusters (fs, buf, cluster, 1); +} + +int +fat_write_sync_clusters (PedFileSystem* fs, char *buf, FatCluster cluster, + FatCluster count) +{ + if (!fat_write_clusters (fs, buf, cluster, count)) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + return 1; +} + +int +fat_write_sync_cluster (PedFileSystem* fs, char *buf, FatCluster cluster) +{ + if (!fat_write_cluster (fs, buf, cluster)) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + return 1; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/fat/fatio.h b/libparted/fs/r/fat/fatio.h new file mode 100644 index 0000000..53ebed7 --- /dev/null +++ b/libparted/fs/r/fat/fatio.h @@ -0,0 +1,49 @@ +/* + libparted + Copyright (C) 1998-2000, 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/>. +*/ + +#ifndef FATIO_H_INCLUDED +#define FATIO_H_INCLUDED + +#include "fat.h" + +extern int fat_read_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count); +extern int fat_write_fragments (PedFileSystem* fs, char* buf, FatFragment frag, + FatFragment count); +extern int fat_write_sync_fragments (PedFileSystem* fs, char* buf, + FatFragment frag, FatFragment count); + +extern int fat_read_fragment (PedFileSystem* fs, char* buf, FatFragment frag); +extern int fat_write_fragment (PedFileSystem* fs, char* buf, FatFragment frag); +extern int fat_write_sync_fragment (PedFileSystem* fs, char* buf, + FatFragment frag); + +extern int fat_read_clusters (PedFileSystem* fs, char* buf, FatCluster cluster, + FatCluster count); +extern int fat_write_clusters (PedFileSystem* fs, char* buf, FatCluster cluster, + FatCluster count); +extern int fat_write_sync_clusters (PedFileSystem* fs, char* buf, + FatCluster cluster, FatCluster count); + +extern int fat_read_cluster (PedFileSystem* fs, char *buf, FatCluster cluster); +extern int fat_write_cluster (PedFileSystem* fs, char *buf, FatCluster cluster); +extern int fat_write_sync_cluster (PedFileSystem* fs, char *buf, + FatCluster cluster); + +#endif /* FATIO_H_INCLUDED */ diff --git a/libparted/fs/r/fat/resize.c b/libparted/fs/r/fat/resize.c new file mode 100644 index 0000000..78dede4 --- /dev/null +++ b/libparted/fs/r/fat/resize.c @@ -0,0 +1,876 @@ +/* + libparted + Copyright (C) 1998-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 "fat.h" +#include "traverse.h" +#include "count.h" +#include "fatio.h" +#include "calc.h" + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <ctype.h> +#include <stdarg.h> +#include <string.h> + +#ifndef DISCOVER_ONLY + +/* Recursively builds (i.e. makes consistent) the duplicated directory tree + * (leaving the original directory tree in tact) + */ +static int +fat_construct_directory (FatOpContext* ctx, FatTraverseInfo* trav_info) +{ + FatTraverseInfo* sub_dir_info; + FatDirEntry* dir_entry; + FatCluster old_first_cluster; + + while ( (dir_entry = fat_traverse_next_dir_entry (trav_info)) ) { + if (fat_dir_entry_is_null_term (dir_entry)) + break; + if (!fat_dir_entry_has_first_cluster (dir_entry, ctx->old_fs)) + continue; + + fat_traverse_mark_dirty (trav_info); + + old_first_cluster = fat_dir_entry_get_first_cluster (dir_entry, + ctx->old_fs); + fat_dir_entry_set_first_cluster (dir_entry, ctx->new_fs, + fat_op_context_map_cluster (ctx, old_first_cluster)); + + if (fat_dir_entry_is_directory (dir_entry) + && dir_entry->name [0] != '.') { + sub_dir_info + = fat_traverse_directory (trav_info, dir_entry); + if (!sub_dir_info) + return 0; + if (!fat_construct_directory (ctx, sub_dir_info)) + return 0; + } + } + /* remove "stale" entries at the end */ + while ((dir_entry = fat_traverse_next_dir_entry (trav_info))) { + memset (dir_entry, 0, sizeof (FatDirEntry)); + fat_traverse_mark_dirty (trav_info); + } + fat_traverse_complete (trav_info); + return 1; +} + +static int +duplicate_legacy_root_dir (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + + PED_ASSERT (old_fs_info->root_dir_sector_count + == new_fs_info->root_dir_sector_count); + + if (!ped_geometry_read (ctx->old_fs->geom, old_fs_info->buffer, + old_fs_info->root_dir_offset, + old_fs_info->root_dir_sector_count)) + return 0; + + if (!ped_geometry_write (ctx->new_fs->geom, old_fs_info->buffer, + new_fs_info->root_dir_offset, + new_fs_info->root_dir_sector_count)) + return 0; + + return 1; +} + +/* + Constructs the new directory tree for legacy (FAT16) file systems. +*/ +static int +fat_construct_legacy_root (FatOpContext* ctx) +{ + FatTraverseInfo* trav_info; + + if (!duplicate_legacy_root_dir (ctx)) + return 0; + trav_info = fat_traverse_begin (ctx->new_fs, FAT_ROOT, "\\"); + return fat_construct_directory (ctx, trav_info); +} + +/* + Constructs the new directory tree for new (FAT32) file systems. +*/ +static int +fat_construct_root (FatOpContext* ctx) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatTraverseInfo* trav_info; + + trav_info = fat_traverse_begin (ctx->new_fs, new_fs_info->root_cluster, + "\\"); + fat_construct_directory (ctx, trav_info); + return 1; +} + +/* Converts the root directory between FAT16 and FAT32. NOTE: this code + * can also do no conversion. I'm leaving fat_construct_directory(), because + * it's really pretty :-) It also leaves a higher chance of deleted file + * recovery, because it doesn't remove redundant entries. (We do this here, + * because brain-damaged FAT16 has an arbitary limit on root directory entries, + * so we save room) + */ +static int +fat_convert_directory (FatOpContext* ctx, FatTraverseInfo* old_trav, + FatTraverseInfo* new_trav) +{ + FatTraverseInfo* sub_old_dir_trav; + FatTraverseInfo* sub_new_dir_trav; + FatDirEntry* new_dir_entry; + FatDirEntry* old_dir_entry; + FatCluster old_first_cluster; + + while ( (old_dir_entry = fat_traverse_next_dir_entry (old_trav)) ) { + if (fat_dir_entry_is_null_term (old_dir_entry)) + break; + if (!fat_dir_entry_is_active (old_dir_entry)) + continue; + + new_dir_entry = fat_traverse_next_dir_entry (new_trav); + if (!new_dir_entry) { + return ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("There's not enough room in the root " + "directory for all of the files. Either " + "cancel, or ignore to lose the files.")) + == PED_EXCEPTION_IGNORE; + } + + *new_dir_entry = *old_dir_entry; + fat_traverse_mark_dirty (new_trav); + + if (!fat_dir_entry_has_first_cluster (old_dir_entry, + ctx->old_fs)) + continue; + + old_first_cluster = fat_dir_entry_get_first_cluster ( + old_dir_entry, ctx->old_fs); + fat_dir_entry_set_first_cluster (new_dir_entry, ctx->new_fs, + fat_op_context_map_cluster (ctx, old_first_cluster)); + + if (fat_dir_entry_is_directory (old_dir_entry) + && old_dir_entry->name [0] != '.') { + sub_old_dir_trav + = fat_traverse_directory (old_trav, old_dir_entry); + if (!sub_old_dir_trav) return 0; + sub_new_dir_trav + = fat_traverse_directory (new_trav, new_dir_entry); + if (!sub_new_dir_trav) { + fat_traverse_complete (sub_old_dir_trav); + return 0; + } + + if (!fat_convert_directory (ctx, sub_old_dir_trav, + sub_new_dir_trav)) + return 0; + } + } + + /* remove "stale" entries at the end, just in case there is some + * overlap + */ + while ((new_dir_entry = fat_traverse_next_dir_entry (new_trav))) { + memset (new_dir_entry, 0, sizeof (FatDirEntry)); + fat_traverse_mark_dirty (new_trav); + } + + fat_traverse_complete (old_trav); + fat_traverse_complete (new_trav); + return 1; +} + +static void +clear_cluster (PedFileSystem* fs, FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + memset (fs_info->buffer, 0, fs_info->cluster_size); + fat_write_cluster (fs, fs_info->buffer, cluster); +} + +/* This MUST be called BEFORE the fat_construct_new_fat(), because cluster + * allocation depend on the old FAT. The reason is, old clusters may + * still be needed during the resize, (particularly clusters in the directory + * tree) even if they will be discarded later. + */ +static int +alloc_root_dir (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatCluster i; + FatCluster cluster; + FatCluster cluster_count; + + PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT32); + + cluster_count = ped_div_round_up ( + PED_MAX (16, old_fs_info->root_dir_sector_count), + new_fs_info->cluster_sectors); + + for (i = 0; i < cluster_count; i++) { + cluster = fat_table_alloc_check_cluster (new_fs_info->fat, + ctx->new_fs); + if (!cluster) + return 0; + ctx->new_root_dir [i] = cluster; + clear_cluster (ctx->new_fs, cluster); + } + ctx->new_root_dir [i] = 0; + new_fs_info->root_cluster = ctx->new_root_dir [0]; + return 1; +} + +/* when converting FAT32 -> FAT16 + * fat_duplicate clusters() duplicated the root directory unnecessarily. + * Let's free it. + * + * This must be called AFTER fat_construct_new_fat(). (otherwise, our + * changes just get overwritten) + */ +static int +free_root_dir (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatCluster old_cluster; + FatFragment i; + + PED_ASSERT (old_fs_info->fat_type == FAT_TYPE_FAT32); + PED_ASSERT (new_fs_info->fat_type == FAT_TYPE_FAT16); + + for (old_cluster = old_fs_info->root_cluster; + !fat_table_is_eof (old_fs_info->fat, old_cluster); + old_cluster = fat_table_get (old_fs_info->fat, old_cluster)) { + FatFragment old_frag; + old_frag = fat_cluster_to_frag (ctx->old_fs, old_cluster); + for (i = 0; i < new_fs_info->cluster_frags; i++) { + FatFragment new_frag; + FatCluster new_clst; + new_frag = fat_op_context_map_fragment (ctx, + old_frag + i); + new_clst = fat_frag_to_cluster (ctx->old_fs, new_frag); + if (!fat_table_set_avail (new_fs_info->fat, new_clst)) + return 0; + } + } + + return 1; +} + +static int +fat_clear_root_dir (PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int i; + + PED_ASSERT (fs_info->fat_type == FAT_TYPE_FAT16); + PED_ASSERT (fs_info->root_dir_sector_count); + + memset (fs_info->buffer, 0, 512); + + for (i = 0; i < fs_info->root_dir_sector_count; i++) { + if (!ped_geometry_write (fs->geom, fs_info->buffer, + fs_info->root_dir_offset + i, 1)) { + if (ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Error writing to the root directory.")) + == PED_EXCEPTION_CANCEL) + return 0; + } + } + return 1; +} + +static int +fat_construct_converted_tree (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatTraverseInfo* old_trav_info; + FatTraverseInfo* new_trav_info; + + if (new_fs_info->fat_type == FAT_TYPE_FAT32) { + new_trav_info = fat_traverse_begin (ctx->new_fs, + new_fs_info->root_cluster, "\\"); + if (!new_trav_info) return 0; + old_trav_info = fat_traverse_begin (ctx->old_fs, FAT_ROOT, + "\\"); + } else { + fat_clear_root_dir (ctx->new_fs); + new_trav_info = fat_traverse_begin (ctx->new_fs, FAT_ROOT, + "\\"); + if (!new_trav_info) return 0; + old_trav_info = fat_traverse_begin (ctx->old_fs, + old_fs_info->root_cluster, "\\"); + } + if (!old_trav_info) { + fat_traverse_complete (new_trav_info); + return 0; + } + if (!fat_convert_directory (ctx, old_trav_info, new_trav_info)) + return 0; + return 1; +} + +/* + Constructs the new directory tree to match the new file locations. +*/ +static int +fat_construct_dir_tree (FatOpContext* ctx) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + + if (new_fs_info->fat_type == old_fs_info->fat_type) { + switch (old_fs_info->fat_type) { + case FAT_TYPE_FAT12: + PED_ASSERT (0); + break; + + case FAT_TYPE_FAT16: + return fat_construct_legacy_root (ctx); + + case FAT_TYPE_FAT32: + return fat_construct_root (ctx); + } + } else { + return fat_construct_converted_tree (ctx); + } + + return 0; +} + +static FatFragment +_get_next_old_frag (FatOpContext* ctx, FatFragment frag) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatCluster cluster; + FatCluster next_cluster; + + if ((frag + 1) % old_fs_info->cluster_frags != 0) { + if (fat_is_fragment_active (ctx->old_fs, frag + 1)) + return frag + 1; + else + return -1; + } else { + cluster = fat_frag_to_cluster (ctx->old_fs, frag); + next_cluster = fat_table_get (old_fs_info->fat, cluster); + + if (fat_table_is_eof (old_fs_info->fat, next_cluster)) + return -1; + else + return fat_cluster_to_frag (ctx->old_fs, next_cluster); + } +} + +/* + Constructs the new fat for the resized file system. +*/ +static int +fat_construct_new_fat (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + FatFragment old_frag; + FatCluster new_cluster; + FatFragment new_frag; + FatFragment old_next_frag; + FatFragment new_next_frag; + FatCluster new_next_cluster; + FatClusterFlag flag; + int i; + + fat_table_clear (new_fs_info->fat); + if (!fat_table_set_cluster_count (new_fs_info->fat, + new_fs_info->cluster_count)) + return 0; + + for (old_frag = 0; old_frag < old_fs_info->frag_count; old_frag++) { + flag = fat_get_fragment_flag (ctx->old_fs, old_frag); + if (flag == FAT_FLAG_FREE) + continue; + if (flag == FAT_FLAG_BAD) { + new_frag = fat_op_context_map_static_fragment ( + ctx, old_frag); + if (new_frag == -1) + continue; + new_cluster = fat_frag_to_cluster (ctx->new_fs, + new_frag); + fat_table_set_bad (new_fs_info->fat, new_cluster); + continue; + } + + new_frag = fat_op_context_map_fragment (ctx, old_frag); + new_cluster = fat_frag_to_cluster (ctx->new_fs, new_frag); + + old_next_frag = _get_next_old_frag (ctx, old_frag); + if (old_next_frag == -1) { + fat_table_set_eof (new_fs_info->fat, new_cluster); + continue; + } + + new_next_frag = fat_op_context_map_fragment (ctx, + old_next_frag); + PED_ASSERT (new_next_frag != -1); + + new_next_cluster = fat_frag_to_cluster (ctx->new_fs, + new_next_frag); + PED_ASSERT (new_next_cluster != new_cluster); + + fat_table_set (new_fs_info->fat, new_cluster, new_next_cluster); + } + + if (old_fs_info->fat_type == FAT_TYPE_FAT32 + && new_fs_info->fat_type == FAT_TYPE_FAT32) { + new_fs_info->root_cluster + = fat_op_context_map_cluster (ctx, + old_fs_info->root_cluster); + } + + if (old_fs_info->fat_type == FAT_TYPE_FAT16 + && new_fs_info->fat_type == FAT_TYPE_FAT32) { + for (i=0; ctx->new_root_dir[i+1]; i++) { + fat_table_set (new_fs_info->fat, + ctx->new_root_dir[i], + ctx->new_root_dir[i+1]); + } + fat_table_set_eof (new_fs_info->fat, ctx->new_root_dir[i]); + } + + return 1; +} + +static int +ask_type (PedFileSystem* fs, int fat16_ok, int fat32_ok, FatType* out_fat_type) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedExceptionOption status; + const char* fat16_msg; + const char* fat32_msg; + + if (fs_info->fat_type == FAT_TYPE_FAT16) + fat16_msg = _("If you leave your file system as FAT16, " + "then you will have no problems."); + else + fat16_msg = _("If you convert to FAT16, and MS Windows " + "is installed on this partition, then " + "you must re-install the MS Windows boot " + "loader. If you want to do this, you " + "should consult the Parted manual (or " + "your distribution's manual)."); + + if (fs_info->fat_type == FAT_TYPE_FAT32) + fat32_msg = _("If you leave your file system as FAT32, " + "then you will not introduce any new " + "problems."); + else + fat32_msg = _("If you convert to FAT32, and MS Windows " + "is installed on this partition, then " + "you must re-install the MS Windows boot " + "loader. If you want to do this, you " + "should consult the Parted manual (or " + "your distribution's manual). Also, " + "converting to FAT32 will make the file " + "system unreadable by MS DOS, MS Windows " + "95a, and MS Windows NT."); + + if (fat16_ok && fat32_ok) { + status = ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_YES_NO_CANCEL, + _("%s %s %s"), + _("Would you like to use FAT32?"), + fat16_msg, + fat32_msg); + + switch (status) { + case PED_EXCEPTION_YES: + *out_fat_type = FAT_TYPE_FAT32; + return 1; + + case PED_EXCEPTION_NO: + *out_fat_type = FAT_TYPE_FAT16; + return 1; + + case PED_EXCEPTION_UNHANDLED: + *out_fat_type = fs_info->fat_type; + return 1; + + case PED_EXCEPTION_CANCEL: + return 0; + + default: + PED_ASSERT (0); + break; + } + } + + if (fat16_ok) { + if (fs_info->fat_type != FAT_TYPE_FAT16) { + status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK_CANCEL, + _("%s %s"), + _("The file system can only be resized to this " + "size by converting to FAT16."), + fat16_msg); + if (status == PED_EXCEPTION_CANCEL) + return 0; + } + *out_fat_type = FAT_TYPE_FAT16; + return 1; + } + + if (fat32_ok) { + if (fs_info->fat_type != FAT_TYPE_FAT32) { + status = ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK_CANCEL, + _("%s %s"), + _("The file system can only be resized to this " + "size by converting to FAT32."), + fat32_msg); + if (status == PED_EXCEPTION_CANCEL) + return 0; + } + *out_fat_type = FAT_TYPE_FAT32; + return 1; + } + + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("GNU Parted cannot resize this partition to this size. " + "We're working on it!")); + + return 0; +} + +/* For resize operations: determine if the file system must be FAT16 or FAT32, + * or either. If the new file system must be FAT32, then query for + * confirmation. If either file system can be used, query for which one. + */ +static int +get_fat_type (PedFileSystem* fs, const PedGeometry* new_geom, + FatType* out_fat_type) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + PedSector fat16_cluster_sectors; + PedSector fat32_cluster_sectors; + FatCluster dummy_cluster_count; + PedSector dummy_fat_sectors; + int fat16_ok; + int fat32_ok; + + fat16_ok = fat_calc_resize_sizes ( + new_geom, + fs_info->cluster_sectors, + FAT_TYPE_FAT16, + fs_info->root_dir_sector_count, + fs_info->cluster_sectors, + &fat16_cluster_sectors, + &dummy_cluster_count, + &dummy_fat_sectors); + + fat32_ok = fat_calc_resize_sizes ( + new_geom, + fs_info->cluster_sectors, + FAT_TYPE_FAT32, + fs_info->root_dir_sector_count, + fs_info->cluster_sectors, + &fat32_cluster_sectors, + &dummy_cluster_count, + &dummy_fat_sectors); + + return ask_type (fs, fat16_ok, fat32_ok, out_fat_type); +} + +/* Creates the PedFileSystem struct for the new resized file system, and + sticks it in a FatOpContext. At the end of the process, the original + (ctx->old_fs) is destroyed, and replaced with the new one (ctx->new_fs). + */ +static FatOpContext* +create_resize_context (PedFileSystem* fs, const PedGeometry* new_geom) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatSpecific* new_fs_info; + PedFileSystem* new_fs; + PedSector new_cluster_sectors; + FatCluster new_cluster_count; + PedSector new_fat_sectors; + FatType new_fat_type; + PedSector root_dir_sector_count; + FatOpContext* context; + + /* hypothetical number of root dir sectors, if we end up using + * FAT16 + */ + if (fs_info->root_dir_sector_count) + root_dir_sector_count = fs_info->root_dir_sector_count; + else + root_dir_sector_count = FAT_ROOT_DIR_ENTRY_COUNT + * sizeof (FatDirEntry) / 512; + + if (!get_fat_type (fs, new_geom, &new_fat_type)) + return 0; + + fat_calc_resize_sizes (new_geom, fs_info->cluster_sectors, new_fat_type, + root_dir_sector_count, fs_info->cluster_sectors, + &new_cluster_sectors, &new_cluster_count, &new_fat_sectors); + + if (!fat_check_resize_geometry (fs, new_geom, new_cluster_sectors, + new_cluster_count)) + goto error; + + new_fs = fat_alloc (new_geom); + if (!new_fs) + goto error; + + new_fs_info = FAT_SPECIFIC (new_fs); + if (!new_fs_info) + goto error_free_new_fs; + +/* preserve boot code, etc. */ + new_fs_info->boot_sector = ped_malloc (new_geom->dev->sector_size); + memcpy (new_fs_info->boot_sector, fs_info->boot_sector, + new_geom->dev->sector_size); + new_fs_info->info_sector = NULL; + if (fs_info->fat_type == FAT_TYPE_FAT32) + { + PED_ASSERT (fs_info->info_sector != NULL); + new_fs_info->info_sector = + ped_malloc (new_geom->dev->sector_size); + memcpy (new_fs_info->info_sector, fs_info->info_sector, + new_geom->dev->sector_size); + } + + new_fs_info->logical_sector_size = fs_info->logical_sector_size; + new_fs_info->sector_count = new_geom->length; + + new_fs_info->sectors_per_track = fs_info->sectors_per_track; + new_fs_info->heads = fs_info->heads; + + new_fs_info->cluster_size = new_cluster_sectors * 512; + new_fs_info->cluster_sectors = new_cluster_sectors; + new_fs_info->cluster_count = new_cluster_count; + new_fs_info->dir_entries_per_cluster = fs_info->dir_entries_per_cluster; + + new_fs_info->fat_type = new_fat_type; + new_fs_info->fat_table_count = 2; + new_fs_info->fat_sectors = new_fat_sectors; + + /* what about copying? */ + new_fs_info->serial_number = fs_info->serial_number; + + if (new_fs_info->fat_type == FAT_TYPE_FAT32) { + new_fs_info->info_sector_offset = 1; + new_fs_info->boot_sector_backup_offset = 6; + + new_fs_info->root_dir_offset = 0; + new_fs_info->root_dir_entry_count = 0; + new_fs_info->root_dir_sector_count = 0; + + /* we add calc_align_sectors to push the cluster_offset + forward, to keep the clusters aligned between the new + and old file systems + */ + new_fs_info->fat_offset + = fat_min_reserved_sector_count (FAT_TYPE_FAT32) + + fat_calc_align_sectors (new_fs, fs); + + new_fs_info->cluster_offset + = new_fs_info->fat_offset + + 2 * new_fs_info->fat_sectors; + } else { + new_fs_info->root_dir_sector_count = root_dir_sector_count; + new_fs_info->root_dir_entry_count + = root_dir_sector_count * 512 / sizeof (FatDirEntry); + + new_fs_info->fat_offset + = fat_min_reserved_sector_count (FAT_TYPE_FAT16) + + fat_calc_align_sectors (new_fs, fs); + + new_fs_info->root_dir_offset = new_fs_info->fat_offset + + 2 * new_fs_info->fat_sectors; + + new_fs_info->cluster_offset = new_fs_info->root_dir_offset + + new_fs_info->root_dir_sector_count; + } + + new_fs_info->total_dir_clusters = fs_info->total_dir_clusters; + + context = fat_op_context_new (new_fs, fs); + if (!context) + goto error_free_new_fs_info; + + if (!fat_op_context_create_initial_fat (context)) + goto error_free_context; + + if (!fat_alloc_buffers (new_fs)) + goto error_free_fat; + + return context; + +error_free_fat: + fat_table_destroy (new_fs_info->fat); +error_free_context: + free (context); +error_free_new_fs_info: + free (new_fs_info); +error_free_new_fs: + free (new_fs); +error: + return NULL; +} + +static int +resize_context_assimilate (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + + fat_free_buffers (ctx->old_fs); + fat_table_destroy (old_fs_info->fat); + free (old_fs_info); + ped_geometry_destroy (ctx->old_fs->geom); + + ctx->old_fs->type_specific = ctx->new_fs->type_specific; + ctx->old_fs->geom = ctx->new_fs->geom; + ctx->old_fs->type = (new_fs_info->fat_type == FAT_TYPE_FAT16) + ? &fat16_type + : &fat32_type; + + free (ctx->new_fs); + + fat_op_context_destroy (ctx); + + return 1; +} + +static int +resize_context_abort (FatOpContext* ctx) +{ + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + + fat_free_buffers (ctx->new_fs); + fat_table_destroy (new_fs_info->fat); + free (new_fs_info); + ped_geometry_destroy (ctx->new_fs->geom); + free (ctx->new_fs); + + fat_op_context_destroy (ctx); + + return 1; +} + +/* copies the "hidden" sectors, between the boot sector and the FAT. Required, + * for the Windows 98 FAT32 boot loader + */ +int +_copy_hidden_sectors (FatOpContext* ctx) +{ + FatSpecific* old_fs_info = FAT_SPECIFIC (ctx->old_fs); + FatSpecific* new_fs_info = FAT_SPECIFIC (ctx->new_fs); + PedSector first = 1; + PedSector last; + PedSector count; + + /* nothing to copy for FAT16 */ + if (old_fs_info->fat_type == FAT_TYPE_FAT16 + || new_fs_info->fat_type == FAT_TYPE_FAT16) + return 1; + + last = PED_MIN (old_fs_info->fat_offset, new_fs_info->fat_offset) - 1; + count = last - first + 1; + + PED_ASSERT (count < BUFFER_SIZE); + + if (!ped_geometry_read (ctx->old_fs->geom, old_fs_info->buffer, + first, count)) + return 0; + if (!ped_geometry_write (ctx->new_fs->geom, old_fs_info->buffer, + first, count)) + return 0; + return 1; +} + +int +fat_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatSpecific* new_fs_info; + FatOpContext* ctx; + PedFileSystem* new_fs; + + ctx = create_resize_context (fs, geom); + if (!ctx) + goto error; + new_fs = ctx->new_fs; + new_fs_info = FAT_SPECIFIC (new_fs); + + if (!fat_duplicate_clusters (ctx, timer)) + goto error_abort_ctx; + if (fs_info->fat_type == FAT_TYPE_FAT16 + && new_fs_info->fat_type == FAT_TYPE_FAT32) { + if (!alloc_root_dir (ctx)) + goto error_abort_ctx; + } + if (!fat_construct_new_fat (ctx)) + goto error_abort_ctx; + if (fs_info->fat_type == FAT_TYPE_FAT32 + && new_fs_info->fat_type == FAT_TYPE_FAT16) { + if (!free_root_dir (ctx)) + goto error_abort_ctx; + } + if (!fat_construct_dir_tree (ctx)) + goto error_abort_ctx; + if (!fat_table_write_all (new_fs_info->fat, new_fs)) + goto error_abort_ctx; + + _copy_hidden_sectors (ctx); + fat_boot_sector_generate (&new_fs_info->boot_sector, new_fs); + fat_boot_sector_write (new_fs_info->boot_sector, new_fs); + if (new_fs_info->fat_type == FAT_TYPE_FAT32) { + fat_info_sector_generate (&new_fs_info->info_sector, new_fs); + fat_info_sector_write (new_fs_info->info_sector, new_fs); + } + + if (!resize_context_assimilate (ctx)) + goto error; + + return 1; + +error_abort_ctx: + resize_context_abort (ctx); +error: + return 0; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/fat/table.c b/libparted/fs/r/fat/table.c new file mode 100644 index 0000000..ec0907f --- /dev/null +++ b/libparted/fs/r/fat/table.c @@ -0,0 +1,481 @@ +/* + libparted + Copyright (C) 1998-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/endian.h> +#include "fat.h" + +#ifndef DISCOVER_ONLY + +FatTable* +fat_table_new (FatType fat_type, FatCluster size) +{ + FatTable* ft; + int entry_size = fat_table_entry_size (fat_type); + + ft = (FatTable*) ped_malloc (sizeof (FatTable)); + if (!ft) return NULL; + + ft->cluster_count = ft->free_cluster_count = size - 2; + +/* ensure there's some free room on the end, to finish off the sector */ + ft->size = ped_div_round_up (size * entry_size, 512) * 512 / entry_size; + ft->fat_type = fat_type; + ft->raw_size = ft->size * entry_size; + + ft->table = ped_malloc (ft->raw_size); + if (!ft->table) { + free (ft); + return NULL; + } + + fat_table_clear (ft); + return ft; +} + +void +fat_table_destroy (FatTable* ft) +{ + free (ft->table); + free (ft); +} + +FatTable* +fat_table_duplicate (const FatTable* ft) +{ + FatTable* dup_ft; + + dup_ft = fat_table_new (ft->fat_type, ft->size); + if (!dup_ft) return NULL; + + dup_ft->cluster_count = ft->cluster_count; + dup_ft->free_cluster_count = ft->free_cluster_count; + dup_ft->bad_cluster_count = ft->bad_cluster_count; + dup_ft->last_alloc = ft->last_alloc; + + memcpy (dup_ft->table, ft->table, ft->raw_size); + + return dup_ft; +} + +void +fat_table_clear (FatTable* ft) +{ + memset (ft->table, 0, ft->raw_size); + + fat_table_set (ft, 0, 0x0ffffff8); + fat_table_set (ft, 1, 0x0fffffff); + + ft->free_cluster_count = ft->cluster_count; + ft->bad_cluster_count = 0; + ft->last_alloc = 1; +} + +int +fat_table_set_cluster_count (FatTable* ft, FatCluster new_cluster_count) +{ + PED_ASSERT (new_cluster_count + 2 <= ft->size); + + ft->cluster_count = new_cluster_count; + return fat_table_count_stats (ft); +} + +int +fat_table_count_stats (FatTable* ft) +{ + FatCluster i; + + PED_ASSERT (ft->cluster_count + 2 <= ft->size); + + ft->free_cluster_count = 0; + ft->bad_cluster_count = 0; + + for (i=2; i < ft->cluster_count + 2; i++) { + if (fat_table_is_available (ft, i)) + ft->free_cluster_count++; + if (fat_table_is_bad (ft, i)) + ft->bad_cluster_count++; + } + return 1; +} + +int +fat_table_read (FatTable* ft, const PedFileSystem* fs, int table_num) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512); + + memset (ft->table, 0, ft->raw_size); + + if (!ped_geometry_read (fs->geom, (void *) ft->table, + fs_info->fat_offset + + table_num * fs_info->fat_sectors, + fs_info->fat_sectors)) + return 0; + + if ( *((unsigned char*) ft->table) != fs_info->boot_sector->media) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("FAT %d media %x doesn't match the boot sector's " + "media %x. You should probably run scandisk."), + (int) table_num + 1, + (int) *((unsigned char*) ft->table), + (int) fs_info->boot_sector->media) + != PED_EXCEPTION_IGNORE) + return 0; + } + + ft->cluster_count = fs_info->cluster_count; + + fat_table_count_stats (ft); + + return 1; +} + +int +fat_table_write (const FatTable* ft, PedFileSystem* fs, int table_num) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + PED_ASSERT (ft->raw_size >= fs_info->fat_sectors * 512); + + if (!ped_geometry_write (fs->geom, (void *) ft->table, + fs_info->fat_offset + + table_num * fs_info->fat_sectors, + fs_info->fat_sectors)) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + + return 1; +} + +int +fat_table_write_all (const FatTable* ft, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + int i; + + for (i = 0; i < fs_info->fat_table_count; i++) { + if (!fat_table_write (ft, fs, i)) + return 0; + } + + return 1; +} + +int +fat_table_compare (const FatTable* a, const FatTable* b) +{ + FatCluster i; + + if (a->cluster_count != b->cluster_count) + return 0; + + for (i = 0; i < a->cluster_count + 2; i++) { + if (fat_table_get (a, i) != fat_table_get (b, i)) + return 0; + } + + return 1; +} + +static int +_test_code_available (const FatTable* ft, FatCluster code) +{ + return code == 0; +} + +static int +_test_code_bad (const FatTable* ft, FatCluster code) +{ + switch (ft->fat_type) { + case FAT_TYPE_FAT12: + if (code == 0xff7) return 1; + break; + + case FAT_TYPE_FAT16: + if (code == 0xfff7) return 1; + break; + + case FAT_TYPE_FAT32: + if (code == 0x0ffffff7) return 1; + break; + } + return 0; +} + +static int +_test_code_eof (const FatTable* ft, FatCluster code) +{ + switch (ft->fat_type) { + case FAT_TYPE_FAT12: + if (code >= 0xff7) return 1; + break; + + case FAT_TYPE_FAT16: + if (code >= 0xfff7) return 1; + break; + + case FAT_TYPE_FAT32: + if (code >= 0x0ffffff7) return 1; + break; + } + return 0; +} + +void +_update_stats (FatTable* ft, FatCluster cluster, FatCluster value) +{ + if (_test_code_available (ft, value) + && !fat_table_is_available (ft, cluster)) { + ft->free_cluster_count++; + if (fat_table_is_bad (ft, cluster)) + ft->bad_cluster_count--; + } + + if (!_test_code_available (ft, value) + && fat_table_is_available (ft, cluster)) { + ft->free_cluster_count--; + if (_test_code_bad (ft, cluster)) + ft->bad_cluster_count--; + } +} + +int +fat_table_set (FatTable* ft, FatCluster cluster, FatCluster value) +{ + if (cluster >= ft->cluster_count + 2) { + ped_exception_throw (PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("fat_table_set: cluster %ld outside " + "file system"), + (long) cluster); + return 0; + } + + _update_stats (ft, cluster, value); + + switch (ft->fat_type) { + case FAT_TYPE_FAT12: + PED_ASSERT (0); + break; + + case FAT_TYPE_FAT16: + ((unsigned short *) ft->table) [cluster] + = PED_CPU_TO_LE16 (value); + break; + + case FAT_TYPE_FAT32: + ((unsigned int *) ft->table) [cluster] + = PED_CPU_TO_LE32 (value); + break; + } + return 1; +} + +FatCluster +fat_table_get (const FatTable* ft, FatCluster cluster) +{ + if (cluster >= ft->cluster_count + 2) { + ped_exception_throw (PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("fat_table_get: cluster %ld outside " + "file system"), + (long) cluster); + exit (EXIT_FAILURE); /* FIXME */ + } + + switch (ft->fat_type) { + case FAT_TYPE_FAT12: + PED_ASSERT (0); + break; + + case FAT_TYPE_FAT16: + return PED_LE16_TO_CPU + (((unsigned short *) ft->table) [cluster]); + + case FAT_TYPE_FAT32: + return PED_LE32_TO_CPU + (((unsigned int *) ft->table) [cluster]); + } + + return 0; +} + +FatCluster +fat_table_alloc_cluster (FatTable* ft) +{ + FatCluster i; + FatCluster cluster; + +/* hack: assumes the first two FAT entries are marked as used (which they + * always should be) + */ + for (i=1; i < ft->cluster_count + 1; i++) { + cluster = (i + ft->last_alloc) % ft->cluster_count; + if (fat_table_is_available (ft, cluster)) { + ft->last_alloc = cluster; + return cluster; + } + } + + ped_exception_throw (PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("fat_table_alloc_cluster: no free clusters")); + return 0; +} + +FatCluster +fat_table_alloc_check_cluster (FatTable* ft, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster result; + + while (1) { + result = fat_table_alloc_cluster (ft); + if (!result) + return 0; + if (fat_read_cluster (fs, fs_info->buffer, result)) + return result; + fat_table_set_bad (ft, result); + } +} + +/* + returns true if <cluster> is marked as bad +*/ +int +fat_table_is_bad (const FatTable* ft, FatCluster cluster) +{ + return _test_code_bad (ft, fat_table_get (ft, cluster)); +} + +/* + returns true if <cluster> represents an EOF marker +*/ +int _GL_ATTRIBUTE_PURE +fat_table_is_eof (const FatTable* ft, FatCluster cluster) +{ + return _test_code_eof (ft, cluster); +} + +/* + returns true if <cluster> is available. +*/ +int +fat_table_is_available (const FatTable* ft, FatCluster cluster) +{ + return _test_code_available (ft, fat_table_get (ft, cluster)); +} + +/* + returns true if <cluster> is empty. Note that this includes bad clusters. +*/ +int +fat_table_is_empty (const FatTable* ft, FatCluster cluster) +{ + return fat_table_is_available (ft, cluster) + || fat_table_is_bad (ft, cluster); +} + +/* + returns true if <cluster> is being used for something constructive. +*/ +int +fat_table_is_active (const FatTable* ft, FatCluster cluster) +{ + return !fat_table_is_bad (ft, cluster) + && !fat_table_is_available (ft, cluster); +} + +/* + marks <cluster> as the last cluster in the chain +*/ +int +fat_table_set_eof (FatTable* ft, FatCluster cluster) +{ + + switch (ft->fat_type) { + case FAT_TYPE_FAT12: + PED_ASSERT (0); + break; + + case FAT_TYPE_FAT16: + return fat_table_set (ft, cluster, 0xfff8); + + case FAT_TYPE_FAT32: + return fat_table_set (ft, cluster, 0x0fffffff); + } + + return 0; +} + +/* + Marks a clusters as unusable, due to physical disk damage. +*/ +int +fat_table_set_bad (FatTable* ft, FatCluster cluster) +{ + if (!fat_table_is_bad (ft, cluster)) + ft->bad_cluster_count++; + + switch (ft->fat_type) { + case FAT_TYPE_FAT12: + return fat_table_set (ft, cluster, 0xff7); + + case FAT_TYPE_FAT16: + return fat_table_set (ft, cluster, 0xfff7); + + case FAT_TYPE_FAT32: + return fat_table_set (ft, cluster, 0x0ffffff7); + } + + return 0; +} + +/* + marks <cluster> as unused/free/available +*/ +int +fat_table_set_avail (FatTable* ft, FatCluster cluster) +{ + return fat_table_set (ft, cluster, 0); +} + +#endif /* !DISCOVER_ONLY */ + +int _GL_ATTRIBUTE_CONST +fat_table_entry_size (FatType fat_type) +{ + switch (fat_type) { + case FAT_TYPE_FAT12: + return 2; /* FIXME: how? */ + + case FAT_TYPE_FAT16: + return 2; + + case FAT_TYPE_FAT32: + return 4; + } + + return 0; +} diff --git a/libparted/fs/r/fat/table.h b/libparted/fs/r/fat/table.h new file mode 100644 index 0000000..a70241b --- /dev/null +++ b/libparted/fs/r/fat/table.h @@ -0,0 +1,74 @@ +/* + libparted + Copyright (C) 1998-2000, 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/>. +*/ + +#ifndef PED_FAT_TABLE_H_INCLUDED +#define PED_FAT_TABLE_H_INCLUDED + +typedef struct _FatTable FatTable; + +#include "fat.h" + +struct _FatTable { + void* table; + FatCluster size; + int raw_size; + + FatType fat_type; + FatCluster cluster_count; + FatCluster free_cluster_count; + FatCluster bad_cluster_count; + + FatCluster last_alloc; +}; + +extern FatTable* fat_table_new (FatType fat_type, FatCluster size); +extern FatTable* fat_table_duplicate (const FatTable* ft); +extern void fat_table_destroy (FatTable* ft); +extern void fat_table_clear (FatTable* ft); +extern int fat_table_set_cluster_count (FatTable* ft, + FatCluster new_cluster_count); + +extern int fat_table_read (FatTable* ft, const PedFileSystem* fs, + int table_num); +extern int fat_table_write (const FatTable* ft, PedFileSystem* fs, + int table_num); +extern int fat_table_write_all (const FatTable* ft, PedFileSystem* fs); +extern int fat_table_compare (const FatTable* a, const FatTable* b); +extern int fat_table_count_stats (FatTable* ft); + +extern FatCluster fat_table_get (const FatTable* ft, FatCluster cluster); +extern int fat_table_set (FatTable* ft, FatCluster cluster, FatCluster value); + +extern FatCluster fat_table_alloc_cluster (FatTable* ft); +extern FatCluster fat_table_alloc_check_cluster (FatTable* ft, + PedFileSystem* fs); + +extern int fat_table_is_bad (const FatTable* ft, FatCluster cluster); +extern int fat_table_is_eof (const FatTable* ft, FatCluster cluster); +extern int fat_table_is_empty (const FatTable* ft, FatCluster cluster); +extern int fat_table_is_available (const FatTable* ft, FatCluster cluster); +extern int fat_table_is_active (const FatTable* ft, FatCluster cluster); + +extern int fat_table_set_eof (FatTable* ft, FatCluster cluster); +extern int fat_table_set_avail (FatTable* ft, FatCluster cluster); +extern int fat_table_set_bad (FatTable* ft, FatCluster cluster); + +extern int fat_table_entry_size (FatType fat_type); + +#endif /* PED_FAT_TABLE_H_INCLUDED */ diff --git a/libparted/fs/r/fat/traverse.c b/libparted/fs/r/fat/traverse.c new file mode 100644 index 0000000..42eeff9 --- /dev/null +++ b/libparted/fs/r/fat/traverse.c @@ -0,0 +1,368 @@ +/* + libparted + Copyright (C) 1998-2000, 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 "fat.h" +#include "traverse.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef DISCOVER_ONLY + +#define NO_CLUSTER -1 + +static char tmp_buffer [4096]; + +int _GL_ATTRIBUTE_PURE +fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info) +{ + return trav_info->buffer_size / sizeof (FatDirEntry); +} + +/* returns 1 if there are no more directory entries in the directory being + * traversed, 0 otherwise. + */ +static int +is_last_buffer (FatTraverseInfo* trav_info) { + FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); + + if (trav_info->is_legacy_root_dir) + return 1; + else + return fat_table_is_eof (fs_info->fat, trav_info->next_buffer); +} + +static int +write_root_dir (FatTraverseInfo* trav_info) +{ + FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); + + if (!ped_geometry_write (trav_info->fs->geom, trav_info->dir_entries, + fs_info->root_dir_offset, + fs_info->root_dir_sector_count)) + return 0; + if (!ped_geometry_sync (trav_info->fs->geom)) + return 0; + trav_info->dirty = 0; + return 1; +} + +static int +write_dir_cluster (FatTraverseInfo* trav_info) +{ + if (!fat_write_sync_cluster (trav_info->fs, + (void*) trav_info->dir_entries, + trav_info->this_buffer)) + return 0; + trav_info->dirty = 0; + return 1; +} + +static int +write_dir_buffer (FatTraverseInfo* trav_info) +{ + if (trav_info->is_legacy_root_dir) + return write_root_dir (trav_info); + else + return write_dir_cluster (trav_info); +} + +static int +read_next_dir_buffer (FatTraverseInfo* trav_info) +{ + FatSpecific* fs_info = FAT_SPECIFIC (trav_info->fs); + + PED_ASSERT (!trav_info->is_legacy_root_dir); + + trav_info->this_buffer = trav_info->next_buffer; + + if (trav_info->this_buffer < 2 + || trav_info->this_buffer >= fs_info->cluster_count + 2) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + "Cluster %ld in directory %s is outside file system!", + (long) trav_info->this_buffer, + trav_info->dir_name); + return 0; + } + + trav_info->next_buffer + = fat_table_get (fs_info->fat, trav_info->this_buffer); + + return fat_read_cluster (trav_info->fs, (void *) trav_info->dir_entries, + trav_info->this_buffer); +} + +/* FIXME: put into fat_dir_entry_* operations */ +void +fat_traverse_mark_dirty (FatTraverseInfo* trav_info) +{ + trav_info->dirty = 1; +} + +FatTraverseInfo* +fat_traverse_begin (PedFileSystem* fs, FatCluster start_cluster, + const char* dir_name) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatTraverseInfo* trav_info; + + trav_info = (FatTraverseInfo*) ped_malloc (sizeof (FatTraverseInfo)); + if (!trav_info) + goto error; + + trav_info->dir_name = strdup (dir_name); + if (!trav_info->dir_name) + goto error_free_trav_info; + + trav_info->fs = fs; + trav_info->is_legacy_root_dir + = (fs_info->fat_type == FAT_TYPE_FAT16) && (start_cluster == 0); + trav_info->dirty = 0; + trav_info->eof = 0; + trav_info->current_entry = -1; + + if (trav_info->is_legacy_root_dir) { + trav_info->buffer_size = 512 * fs_info->root_dir_sector_count; + } else { + trav_info->next_buffer = start_cluster; + trav_info->buffer_size = fs_info->cluster_size; + } + + trav_info->dir_entries + = (FatDirEntry*) ped_malloc (trav_info->buffer_size); + if (!trav_info->dir_entries) + goto error_free_dir_name; + + if (trav_info->is_legacy_root_dir) { + if (!ped_geometry_read (fs->geom, trav_info->dir_entries, + fs_info->root_dir_offset, + fs_info->root_dir_sector_count)) + goto error_free_dir_entries; + } else { + if (!read_next_dir_buffer (trav_info)) + goto error_free_dir_entries; + } + + return trav_info; + +error_free_dir_entries: + free (trav_info->dir_entries); +error_free_dir_name: + free (trav_info->dir_name); +error_free_trav_info: + free (trav_info); +error: + return NULL; +} + +int +fat_traverse_complete (FatTraverseInfo* trav_info) +{ + if (trav_info->dirty) { + if (!write_dir_buffer (trav_info)) + return 0; + } + free (trav_info->dir_entries); + free (trav_info->dir_name); + free (trav_info); + return 1; +} + +FatTraverseInfo* +fat_traverse_directory (FatTraverseInfo *trav_info, FatDirEntry* parent) +{ + strcpy (tmp_buffer, trav_info->dir_name); + fat_dir_entry_get_name (parent, + tmp_buffer + strlen (trav_info->dir_name)); + strcat (tmp_buffer, "\\"); + + return fat_traverse_begin (trav_info->fs, + fat_dir_entry_get_first_cluster (parent, trav_info->fs), + tmp_buffer); +} + +FatDirEntry* +fat_traverse_next_dir_entry (FatTraverseInfo *trav_info) +{ + if (trav_info->eof) + return NULL; + + trav_info->current_entry++; + if (trav_info->current_entry + >= fat_traverse_entries_per_buffer (trav_info)) { + if (trav_info->dirty) { + if (!write_dir_buffer (trav_info)) + return NULL; + } + + trav_info->current_entry = 0; + if (is_last_buffer (trav_info)) { + trav_info->eof = 1; + return NULL; + } + if (!read_next_dir_buffer (trav_info)) + return NULL; + } + return trav_info->dir_entries + trav_info->current_entry; +} + +FatCluster _GL_ATTRIBUTE_PURE +fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry, PedFileSystem *fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + switch (fs_info->fat_type) { + case FAT_TYPE_FAT12: + case FAT_TYPE_FAT16: + return PED_LE16_TO_CPU (dir_entry->first_cluster); + + case FAT_TYPE_FAT32: + return PED_LE16_TO_CPU (dir_entry->first_cluster_high) + * 65536L + + PED_LE16_TO_CPU (dir_entry->first_cluster); + } + + return 0; +} + +void +fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs, + FatCluster cluster) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + + switch (fs_info->fat_type) { + case FAT_TYPE_FAT12: + PED_ASSERT (0); + break; + + case FAT_TYPE_FAT16: + dir_entry->first_cluster = PED_CPU_TO_LE16 (cluster); + break; + + case FAT_TYPE_FAT32: + dir_entry->first_cluster + = PED_CPU_TO_LE16 (cluster & 0xffff); + dir_entry->first_cluster_high + = PED_CPU_TO_LE16 (cluster / 0x10000); + break; + } +} + +uint32_t _GL_ATTRIBUTE_PURE +fat_dir_entry_get_length (FatDirEntry* dir_entry) +{ + return PED_LE32_TO_CPU (dir_entry->length); +} + +int +fat_dir_entry_is_null_term (const FatDirEntry* dir_entry) +{ + FatDirEntry null_entry; + + memset (&null_entry, 0, sizeof (null_entry)); + return memcmp (&null_entry, dir_entry, sizeof (null_entry)) == 0; +} + +int _GL_ATTRIBUTE_PURE +fat_dir_entry_is_active (FatDirEntry* dir_entry) +{ + if ((unsigned char) dir_entry->name[0] == DELETED_FLAG) return 0; + if ((unsigned char) dir_entry->name[0] == 0) return 0; + if ((unsigned char) dir_entry->name[0] == 0xF6) return 0; + return 1; +} + +int _GL_ATTRIBUTE_PURE +fat_dir_entry_is_file (FatDirEntry* dir_entry) { + if (dir_entry->attributes == VFAT_ATTR) return 0; + if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0; + if (!fat_dir_entry_is_active (dir_entry)) return 0; + if ((dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR) return 0; + return 1; +} + +int _GL_ATTRIBUTE_PURE +fat_dir_entry_is_system_file (FatDirEntry* dir_entry) +{ + if (!fat_dir_entry_is_file (dir_entry)) return 0; + return (dir_entry->attributes & SYSTEM_ATTR) + || (dir_entry->attributes & HIDDEN_ATTR); +} + +int _GL_ATTRIBUTE_PURE +fat_dir_entry_is_directory (FatDirEntry* dir_entry) +{ + if (dir_entry->attributes == VFAT_ATTR) return 0; + if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0; + if (!fat_dir_entry_is_active (dir_entry)) return 0; + return (dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR; +} + +int +fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs) +{ + FatSpecific* fs_info = FAT_SPECIFIC (fs); + FatCluster first_cluster; + + if (!fat_dir_entry_is_file (dir_entry) + && !fat_dir_entry_is_directory (dir_entry)) + return 0; + + first_cluster = fat_dir_entry_get_first_cluster (dir_entry, fs); + if (first_cluster == 0 + || fat_table_is_eof (fs_info->fat, first_cluster)) + return 0; + + return 1; +} + +/* + decrypts silly DOS names to FILENAME.EXT +*/ +void +fat_dir_entry_get_name (const FatDirEntry *dir_entry, char *result) { + size_t i; + const char *src; + const char *ext; + + src = dir_entry->name; + + for (i=0; i < sizeof dir_entry->name; i++) { + if (src[i] == ' ' || src[i] == 0) break; + *result++ = src[i]; + } + + ext = (const char *) dir_entry->extension; + if (ext[0] != ' ' && ext[0] != 0) { + *result++ = '.'; + for (i=0; i < sizeof dir_entry->extension; i++) { + if (ext[i] == ' ' || ext[i] == 0) break; + *result++ = ext[i]; + } + } + + *result = 0; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/fat/traverse.h b/libparted/fs/r/fat/traverse.h new file mode 100644 index 0000000..02318ba --- /dev/null +++ b/libparted/fs/r/fat/traverse.h @@ -0,0 +1,75 @@ +/* + libparted + Copyright (C) 1998-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/>. +*/ + +#ifndef TRAVERSE_H_INCLUDED +#define TRAVERSE_H_INCLUDED + +#include "fatio.h" + +typedef struct _FatTraverseInfo FatTraverseInfo; + +struct _FatTraverseInfo { + PedFileSystem* fs; + char* dir_name; + + int is_legacy_root_dir; + int dirty; + int eof; + + FatDirEntry* dir_entries; + int current_entry; + FatCluster this_buffer, next_buffer; + int buffer_size; +}; + +extern int fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info); + +/* starts traversal at an arbitary cluster. if start_cluster==0, then uses + root directory */ +extern FatTraverseInfo* fat_traverse_begin (PedFileSystem* fs, + FatCluster start_cluster, + const char* dir_name); + +extern int fat_traverse_complete (FatTraverseInfo* trav_info); + +extern FatTraverseInfo* fat_traverse_directory (FatTraverseInfo* trav_info, + FatDirEntry* parent); + +extern void fat_traverse_mark_dirty (FatTraverseInfo* trav_info); + +extern FatDirEntry* fat_traverse_next_dir_entry (FatTraverseInfo* trav_info); + +extern FatCluster fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry, + PedFileSystem* fs); + +extern void fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry, + PedFileSystem* fs, FatCluster cluster); + +extern uint32_t fat_dir_entry_get_length (FatDirEntry* dir_entry); + +extern int fat_dir_entry_is_null_term (const FatDirEntry* dir_entry); +extern int fat_dir_entry_is_file (FatDirEntry* dir_entry); +extern int fat_dir_entry_is_system_file (FatDirEntry* dir_entry); +extern int fat_dir_entry_is_directory (FatDirEntry* dir_entry); +extern void fat_dir_entry_get_name (const FatDirEntry* dir_entry, char* result); +extern int fat_dir_entry_is_active (FatDirEntry* dir_entry); +extern int fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry, + PedFileSystem* fs); + +#endif /* TRAVERSE_H_INCLUDED */ diff --git a/libparted/fs/r/filesys.c b/libparted/fs/r/filesys.c new file mode 100644 index 0000000..856238c --- /dev/null +++ b/libparted/fs/r/filesys.c @@ -0,0 +1,320 @@ +/* libparted - a library for manipulating disk partitions + Copyright (C) 1999-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/>. +*/ + +/** \file filesys.c */ + +/** + * \addtogroup PedFileSystem + * + * \note File systems exist on a PedGeometry - NOT a PedPartition. + * + * @{ + */ + +#include <config.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 */ + +#define STREQ(a, b) (strcmp (a, b) == 0) + +#ifndef MIN +# define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +typedef PedFileSystem * (*open_fn_t) (PedGeometry *); +extern PedFileSystem *hfsplus_open (PedGeometry *); +extern PedFileSystem *hfs_open (PedGeometry *); +extern PedFileSystem *fat_open (PedGeometry *); + +typedef int (*close_fn_t) (PedFileSystem *); +extern int hfsplus_close (PedFileSystem *); +extern int hfs_close (PedFileSystem *); +extern int fat_close (PedFileSystem *); + +typedef int (*resize_fn_t) (PedFileSystem *fs, PedGeometry *geom, + PedTimer *timer); +extern int hfsplus_resize (PedFileSystem *fs, PedGeometry *geom, + PedTimer *timer); +extern int hfs_resize (PedFileSystem *fs, PedGeometry *geom, + PedTimer *timer); +extern int fat_resize (PedFileSystem *fs, PedGeometry *geom, + PedTimer *timer); + +typedef PedConstraint * (*resize_constraint_fn_t) (PedFileSystem const *fs); +extern PedConstraint *hfsplus_get_resize_constraint (PedFileSystem const *fs); +extern PedConstraint *hfs_get_resize_constraint (PedFileSystem const *fs); +extern PedConstraint *fat_get_resize_constraint (PedFileSystem const *fs); + +static bool +is_hfs_plus (char const *fs_type_name) +{ + return STREQ (fs_type_name, "hfsx") || STREQ (fs_type_name, "hfs+"); +} + +static open_fn_t +open_fn (char const *fs_type_name) +{ + if (is_hfs_plus (fs_type_name)) + return hfsplus_open; + if (STREQ (fs_type_name, "hfs")) + return hfs_open; + if (strncmp (fs_type_name, "fat", 3) == 0) + return fat_open; + return NULL; +} + +static close_fn_t +close_fn (char const *fs_type_name) +{ + if (is_hfs_plus (fs_type_name)) + return hfsplus_close; + if (STREQ (fs_type_name, "hfs")) + return hfs_close; + if (strncmp (fs_type_name, "fat", 3) == 0) + return fat_close; + return NULL; +} + +static resize_fn_t +resize_fn (char const *fs_type_name) +{ + if (is_hfs_plus (fs_type_name)) + return hfsplus_resize; + if (STREQ (fs_type_name, "hfs")) + return hfs_resize; + if (strncmp (fs_type_name, "fat", 3) == 0) + return fat_resize; + return NULL; +} + +static resize_constraint_fn_t +resize_constraint_fn (char const *fs_type_name) +{ + if (is_hfs_plus (fs_type_name)) + return hfsplus_get_resize_constraint; + if (STREQ (fs_type_name, "hfs")) + return hfs_get_resize_constraint; + if (strncmp (fs_type_name, "fat", 3) == 0) + return fat_get_resize_constraint; + return NULL; +} + +/** + * This function opens the file system stored on \p geom, if it + * can find one. + * It is often called in the following manner: + * \code + * fs = ped_file_system_open (&part.geom) + * \endcode + * + * \throws PED_EXCEPTION_ERROR if file system could not be detected + * \throws PED_EXCEPTION_ERROR if the file system is bigger than its volume + * \throws PED_EXCEPTION_NO_FEATURE if opening of a file system stored on + * \p geom is not implemented + * + * \return a PedFileSystem on success, \c NULL on failure. + */ +PedFileSystem * +ped_file_system_open (PedGeometry* geom) +{ + PED_ASSERT (geom != NULL); + + if (!ped_device_open (geom->dev)) + goto error; + + PedFileSystemType *type = ped_file_system_probe (geom); + if (!type) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Could not detect file system.")); + goto error_close_dev; + } + + open_fn_t open_f = open_fn (type->name); + if (open_f == NULL) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("resizing %s file systems is not supported"), + type->name); + goto error_close_dev; + } + + PedGeometry *probed_geom = ped_file_system_probe_specific (type, geom); + if (!probed_geom) + goto error_close_dev; + if (!ped_geometry_test_inside (geom, probed_geom)) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("The file system is bigger than its volume!")) + != PED_EXCEPTION_IGNORE) + goto error_destroy_probed_geom; + } + + PedFileSystem *fs = (*open_f) (probed_geom); + if (!fs) + goto error_destroy_probed_geom; + ped_geometry_destroy (probed_geom); + fs->type = type; + return fs; + +error_destroy_probed_geom: + ped_geometry_destroy (probed_geom); +error_close_dev: + ped_device_close (geom->dev); +error: + return NULL; +} + +/** + * Close file system \p fs. + * + * \return \c 1 on success, \c 0 on failure + */ +int +ped_file_system_close (PedFileSystem* fs) +{ + PED_ASSERT (fs != NULL); + PedDevice *dev = fs->geom->dev; + close_fn_t fn = close_fn (fs->type->name); + + if (!fn || !(fn (fs))) + goto error_close_dev; + ped_device_close (dev); + return 1; + +error_close_dev: + ped_device_close (dev); + return 0; +} + +/** + * This function erases all file system signatures that indicate that a + * file system occupies a given region described by \p geom. + * After this operation ped_file_system_probe() won't detect any file system. + * + * \return \c 1 on success, \c 0 on failure + */ +static int +ped_file_system_clobber (PedGeometry* geom) +{ + PED_ASSERT (geom != NULL); + + if (!ped_device_open (geom->dev)) + return 0; + + /* Clear the first three and the last two sectors, albeit fewer + when GEOM is too small. */ + PedSector len = MIN (geom->length, geom->dev->length); + + int ok = (len <= 5 + ? ptt_geom_clear_sectors (geom, 0, len) + : (ptt_geom_clear_sectors (geom, 0, 3) + && ptt_geom_clear_sectors (geom, geom->dev->length - 2, 2))); + + ped_device_close (geom->dev); + return !!ok; +} + +/* This function erases all signatures that indicate the presence of + * a file system in a particular region, without erasing any data + * contained inside the "exclude" region. + */ +static int +ped_file_system_clobber_exclude (PedGeometry* geom, + const PedGeometry* exclude) +{ + PedGeometry* clobber_geom; + int status; + + if (ped_geometry_test_sector_inside (exclude, geom->start)) + return 1; + + clobber_geom = ped_geometry_duplicate (geom); + if (ped_geometry_test_overlap (clobber_geom, exclude)) + ped_geometry_set_end (clobber_geom, exclude->start - 1); + + status = ped_file_system_clobber (clobber_geom); + ped_geometry_destroy (clobber_geom); + return status; +} + +/** + * Resize \p fs to new geometry \p geom. + * + * \p geom should satisfy the ped_file_system_get_resize_constraint(). + * (This isn't asserted, so it's not a bug not to... just it's likely + * to fail ;) If \p timer is non-NULL, it is used as the progress meter. + * + * \throws PED_EXCEPTION_NO_FEATURE if resizing of file system \p fs + * is not implemented yet + * + * \return \c 0 on failure + */ +int +ped_file_system_resize (PedFileSystem *fs, PedGeometry *geom, PedTimer *timer) +{ + PED_ASSERT (fs != NULL); + PED_ASSERT (geom != NULL); + + resize_fn_t resize_f = resize_fn (fs->type->name); + if (resize_f == NULL) { + ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("resizing %s file systems is not supported"), + fs->type->name); + return 0; + } + + if (!ped_file_system_clobber_exclude (geom, fs->geom)) + return 0; + + return resize_f (fs, geom, timer); +} + +/** + * Return a constraint that represents all of the possible ways the + * file system \p fs can be resized with ped_file_system_resize(). + * This takes into account the amount of used space on + * the filesystem \p fs and the capabilities of the resize algorithm. + * Hints: + * -# if constraint->start_align->grain_size == 0, or + * constraint->start_geom->length == 1, then the start cannot be moved + * -# constraint->min_size is the minimum size you can resize the partition + * to. You might want to tell the user this ;-). + * + * \return a PedConstraint on success, \c NULL on failure + */ +PedConstraint * +ped_file_system_get_resize_constraint (const PedFileSystem *fs) +{ + PED_ASSERT (fs != NULL); + + resize_constraint_fn_t resize_constraint_f = + resize_constraint_fn (fs->type->name); + if (resize_constraint_f == NULL) + return NULL; + + return resize_constraint_f (fs); +} diff --git a/libparted/fs/r/hfs/advfs.c b/libparted/fs/r/hfs/advfs.c new file mode 100644 index 0000000..cb66e9e --- /dev/null +++ b/libparted/fs/r/hfs/advfs.c @@ -0,0 +1,332 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 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/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "file.h" + +#include "advfs.h" + +/* - if a < b, 0 if a == b, + if a > b */ +/* Comparaison is done in the following order : */ +/* CNID, then fork type, then start block */ +/* Note that HFS implementation in linux has a bug */ +/* in this function */ +static int +hfs_extent_key_cmp(HfsPrivateGenericKey* a, HfsPrivateGenericKey* b) +{ + HfsExtentKey* key1 = (HfsExtentKey*) a; + HfsExtentKey* key2 = (HfsExtentKey*) b; + + /* do NOT use a substraction, because */ + /* 0xFFFFFFFF - 1 = 0xFFFFFFFE so this */ + /* would return -2, despite the fact */ + /* 0xFFFFFFFF > 1 !!! (this is the 2.4 bug) */ + if (key1->file_ID != key2->file_ID) + return PED_BE32_TO_CPU(key1->file_ID) < + PED_BE32_TO_CPU(key2->file_ID) ? + -1 : +1; + + if (key1->type != key2->type) + return (int)(key1->type - key2->type); + + if (key1->start == key2->start) + return 0; + /* the whole thing wont work with 16 bits ints */ + /* anyway */ + return (int)( PED_BE16_TO_CPU(key1->start) - + PED_BE16_TO_CPU(key2->start) ); +} + +/* do a B-Tree lookup */ +/* read the first record immediatly inferior or egal to the given key */ +/* return 0 on error */ +/* record_out _must_ be large enough to receive record_size bytes */ +/* WARNING : the compare function called only handle Extents BTree */ +/* so modify this function if you want to do lookup in */ +/* other BTrees has well */ +int +hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key, + void *record_out, unsigned int record_size, + HfsCPrivateLeafRec* record_ref) +{ + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsHeaderRecord* header; + HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node; + HfsPrivateGenericKey* record_key = NULL; + unsigned int node_number, record_number; + int i; + uint16_t record_pos; + + /* Read the header node */ + if (!hfs_file_read_sector(b_tree_file, node, 0)) + return 0; + uint16_t offset; + memcpy(&offset, node+(PED_SECTOR_SIZE_DEFAULT-2), sizeof(uint16_t)); + header = (HfsHeaderRecord*) (node + PED_BE16_TO_CPU(offset)); + + /* Get the node number of the root */ + node_number = PED_BE32_TO_CPU(header->root_node); + if (!node_number) + return 0; + + /* Read the root node */ + if (!hfs_file_read_sector(b_tree_file, node, node_number)) + return 0; + + /* Follow the white rabbit */ + while (1) { + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = record_number; i; i--) { + uint16_t value; + memcpy(&value, node+(PED_SECTOR_SIZE_DEFAULT - (2*i)), sizeof(uint16_t)); + record_pos = PED_BE16_TO_CPU(value); + record_key = (HfsPrivateGenericKey*) (node + record_pos); + /* check for obvious error in FS */ + if ((record_pos< HFS_FIRST_REC) + || (record_pos>= PED_SECTOR_SIZE_DEFAULT + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + return 0; + } + if (hfs_extent_key_cmp(record_key, key) <= 0) + break; + } + if (!i) return 0; + if (desc->type == HFS_IDX_NODE) { + unsigned int skip; + + skip = (1 + record_key->key_length + 1) & ~1; + uint32_t value; + memcpy(&value, node+record_pos+skip, sizeof(uint32_t)); + node_number = PED_BE32_TO_CPU(value); + if (!hfs_file_read_sector(b_tree_file, node, + node_number)) + return 0; + } else + break; + } + + /* copy the result if needed */ + if (record_size) + memcpy (record_out, record_key, record_size); + + /* send record reference if needed */ + if (record_ref) { + record_ref->node_size = 1; /* in sectors */ + record_ref->node_number = node_number; + record_ref->record_pos = record_pos; + record_ref->record_number = i; + } + + /* success */ + return 1; +} + +/* free the bad blocks linked list */ +void +hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first) +{ + HfsPrivateLinkExtent* next; + + while (first) { + next = first->next; + free (first); + first = next; + } +} + +/* This function reads bad blocks extents in the extents file + and store it in f.s. specific data of fs */ +int +hfs_read_bad_blocks (const PedFileSystem *fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + + if (priv_data->bad_blocks_loaded) + return 1; + + { + uint8_t record[sizeof (HfsExtentKey) + + sizeof (HfsExtDataRec)]; + HfsExtentKey search; + HfsExtentKey* ret_key = (HfsExtentKey*) record; + HfsExtDescriptor* ret_cache = (HfsExtDescriptor*) + (record + sizeof (HfsExtentKey)); + unsigned int block, last_start, first_pass = 1; + + search.key_length = sizeof (HfsExtentKey) - 1; + search.type = HFS_DATA_FORK; + search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID); + + last_start = -1; block = 0; + while (1) { + int i; + + search.start = PED_CPU_TO_BE16 (block); + if (!hfs_btree_search (priv_data->extent_file, + (HfsPrivateGenericKey*) &search, + record, sizeof (record), NULL) + || ret_key->file_ID != search.file_ID + || ret_key->type != search.type) { + if (first_pass) + break; + else + goto errbb; + } + if (PED_BE16_TO_CPU (ret_key->start) == last_start) + break; + + last_start = PED_BE16_TO_CPU (ret_key->start); + for (i = 0; i < HFS_EXT_NB; i++) { + if (ret_cache[i].block_count) { + HfsPrivateLinkExtent* new_xt = + (HfsPrivateLinkExtent*) ped_malloc ( + sizeof (HfsPrivateLinkExtent)); + if (!new_xt) + goto errbb; + new_xt->next = priv_data->bad_blocks_xtent_list; + memcpy(&(new_xt->extent), ret_cache+i, + sizeof (HfsExtDescriptor)); + priv_data->bad_blocks_xtent_list = new_xt; + priv_data->bad_blocks_xtent_nb++; + block += PED_BE16_TO_CPU ( + ret_cache[i].block_count); + } + } + first_pass = 0; + } + + priv_data->bad_blocks_loaded = 1; + return 1;} + +errbb: hfs_free_bad_blocks_list(priv_data->bad_blocks_xtent_list); + priv_data->bad_blocks_xtent_list=NULL; + priv_data->bad_blocks_xtent_nb=0; + return 0; +} + +/* This function check if fblock is a bad block */ +int _GL_ATTRIBUTE_PURE +hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsPrivateLinkExtent* walk; + + for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) { + /* Won't compile without the strange cast ! gcc bug ? */ + /* or maybe C subtilties... */ + if ((fblock >= PED_BE16_TO_CPU (walk->extent.start_block)) && + (fblock < (unsigned int) (PED_BE16_TO_CPU ( + walk->extent.start_block) + + PED_BE16_TO_CPU ( + walk->extent.block_count)))) + return 1; + } + + return 0; +} + +/* This function returns the first sector of the last free block of an + HFS volume we can get after a hfs_pack_free_space_from_block call */ +/* On error this function returns 0 */ +PedSector +hfs_get_empty_end (const PedFileSystem *fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsMasterDirectoryBlock* mdb = priv_data->mdb; + unsigned int block, last_bad, end_free_blocks; + + /* find the next block to the last bad block of the volume */ + if (!hfs_read_bad_blocks (fs)) + return 0; + + HfsPrivateLinkExtent* l; + last_bad = 0; + for (l = priv_data->bad_blocks_xtent_list; l; l = l->next) { + if ((unsigned int) PED_BE16_TO_CPU (l->extent.start_block) + + PED_BE16_TO_CPU (l->extent.block_count) > last_bad) + last_bad = PED_BE16_TO_CPU (l->extent.start_block) + + PED_BE16_TO_CPU (l->extent.block_count); + } + + /* Count the free blocks from last_bad to the end of the volume */ + end_free_blocks = 0; + for (block = last_bad; + block < PED_BE16_TO_CPU (mdb->total_blocks); + block++) { + if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + end_free_blocks++; + } + + /* Calculate the block that will by the first free at the + end of the volume */ + block = PED_BE16_TO_CPU (mdb->total_blocks) - end_free_blocks; + + return (PedSector) PED_BE16_TO_CPU (mdb->start_block) + + (PedSector) block * (PED_BE32_TO_CPU (mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT); +} + +/* return the block which should be used to pack data to have at + least free fblock blocks at the end of the volume */ +unsigned int _GL_ATTRIBUTE_PURE +hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + unsigned int block; + + for (block = PED_BE16_TO_CPU (priv_data->mdb->total_blocks) - 1; + block && fblock; + block--) { + if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + fblock--; + } + + while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + block--; + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + block++; + + return block; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/hfs/advfs.h b/libparted/fs/r/hfs/advfs.h new file mode 100644 index 0000000..094298e --- /dev/null +++ b/libparted/fs/r/hfs/advfs.h @@ -0,0 +1,49 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 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/>. +*/ + +#ifndef _ADVFS_H +#define _ADVFS_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key, + void *record_out, unsigned int record_size, + HfsCPrivateLeafRec* record_ref); + +void +hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first); + +int +hfs_read_bad_blocks (const PedFileSystem *fs); + +int +hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock); + +PedSector +hfs_get_empty_end (const PedFileSystem *fs); + +unsigned int +hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock); + +#endif /* _ADVFS_H */ diff --git a/libparted/fs/r/hfs/advfs_plus.c b/libparted/fs/r/hfs/advfs_plus.c new file mode 100644 index 0000000..6104460 --- /dev/null +++ b/libparted/fs/r/hfs/advfs_plus.c @@ -0,0 +1,385 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 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/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "advfs.h" +#include "file_plus.h" + +#include "advfs_plus.h" + +/* - if a < b, 0 if a == b, + if a > b */ +/* Comparaison is done in the following order : */ +/* CNID, then fork type, then start block */ +static int +hfsplus_extent_key_cmp(HfsPPrivateGenericKey* a, HfsPPrivateGenericKey* b) +{ + HfsPExtentKey* key1 = (HfsPExtentKey*) a; + HfsPExtentKey* key2 = (HfsPExtentKey*) b; + + if (key1->file_ID != key2->file_ID) + return PED_BE32_TO_CPU(key1->file_ID) < + PED_BE32_TO_CPU(key2->file_ID) ? + -1 : +1; + + if (key1->type != key2->type) + return (int)(key1->type - key2->type); + + if (key1->start == key2->start) + return 0; + return PED_BE32_TO_CPU(key1->start) < + PED_BE32_TO_CPU(key2->start) ? + -1 : +1; +} + +/* do a B-Tree lookup */ +/* read the first record immediatly inferior or egal to the given key */ +/* return 0 on error */ +/* record_out _must_ be large enough to receive the whole record (key + data) */ +/* WARNING : the search function called only handle Extents BTree */ +/* so modify this function if you want to do lookup in */ +/* other BTrees has well */ +int +hfsplus_btree_search (HfsPPrivateFile* b_tree_file, HfsPPrivateGenericKey* key, + void *record_out, unsigned int record_size, + HfsCPrivateLeafRec* record_ref) +{ + uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPPrivateGenericKey* record_key = NULL; + unsigned int node_number, record_number, size, bsize; + int i; + uint16_t record_pos; + + /* Read the header node */ + if (!hfsplus_file_read_sector(b_tree_file, node_1, 0)) + return 0; + header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC); + + /* Get the node number of the root */ + node_number = PED_BE32_TO_CPU (header->root_node); + if (!node_number) + return 0; + + /* Get the size of a node in sectors and allocate buffer */ + size = (bsize = PED_BE16_TO_CPU (header->node_size)) / PED_SECTOR_SIZE_DEFAULT; + node = ped_malloc (bsize); + if (!node) + return 0; + HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node; + + /* Read the root node */ + if (!hfsplus_file_read (b_tree_file, node, + (PedSector) node_number * size, size)) + return 0; + + /* Follow the white rabbit */ + while (1) { + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = record_number; i; i--) { + uint16_t value; + memcpy(&value, node+(bsize - (2*i)), sizeof(uint16_t)); + record_pos = PED_BE16_TO_CPU(value); + record_key = (HfsPPrivateGenericKey*) (node + record_pos); + /* check for obvious error in FS */ + if ((record_pos < HFS_FIRST_REC) + || (record_pos >= (signed)bsize + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + free (node); + return 0; + } + if (hfsplus_extent_key_cmp(record_key, key) <= 0) + break; + } + if (!i) { free (node); return 0; } + if (desc->type == HFS_IDX_NODE) { + unsigned int skip; + + skip = ( 2 + PED_BE16_TO_CPU (record_key->key_length) + + 1 ) & ~1; + uint32_t value; + memcpy(&value, node+record_pos+skip, sizeof(uint32_t)); + node_number = PED_BE32_TO_CPU(value); + if (!hfsplus_file_read(b_tree_file, node, + (PedSector) node_number * size, + size)) { + free (node); + return 0; + } + } else + break; + } + + /* copy the result if needed */ + if (record_size) + memcpy (record_out, record_key, record_size); + + /* send record reference if needed */ + if (record_ref) { + record_ref->node_size = size; /* in sectors */ + record_ref->node_number = node_number; + record_ref->record_pos = record_pos; + record_ref->record_number = i; + } + + /* success */ + free (node); + return 1; +} + +/* free the bad blocks linked list */ +void +hfsplus_free_bad_blocks_list(HfsPPrivateLinkExtent* first) +{ + HfsPPrivateLinkExtent* next; + + while (first) { + next = first->next; + free (first); + first = next; + } +} + +/* This function reads bad blocks extents in the extents file + and store it in f.s. specific data of fs */ +int +hfsplus_read_bad_blocks (const PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + + if (priv_data->bad_blocks_loaded) + return 1; + + { + uint8_t record[sizeof (HfsPExtentKey) + + sizeof (HfsPExtDataRec)]; + HfsPExtentKey search; + HfsPExtentKey* ret_key = (HfsPExtentKey*) record; + HfsPExtDescriptor* ret_cache = (HfsPExtDescriptor*) + (record + sizeof (HfsPExtentKey)); + int block, first_pass = 1; + unsigned int last_start; + + search.key_length = sizeof (HfsExtentKey) - 2; + search.type = HFS_DATA_FORK; + search.pad = 0; + search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID); + + last_start = -1; block = 0; + while (1) { + int i; + + search.start = PED_CPU_TO_BE32 (block); + if (!hfsplus_btree_search (priv_data->extents_file, + (HfsPPrivateGenericKey*) &search, + record, sizeof (record), NULL) + || ret_key->file_ID != search.file_ID + || ret_key->type != search.type) { + if (first_pass) + break; + else + goto errbbp; + } + if (PED_BE32_TO_CPU (ret_key->start) == last_start) + break; + + last_start = PED_BE32_TO_CPU (ret_key->start); + for (i = 0; i < HFSP_EXT_NB; i++) { + if (ret_cache[i].block_count) { + HfsPPrivateLinkExtent* new_xt = + (HfsPPrivateLinkExtent*) ped_malloc ( + sizeof (HfsPPrivateLinkExtent)); + if (!new_xt) + goto errbbp; + new_xt->next = priv_data->bad_blocks_xtent_list; + memcpy (&(new_xt->extent), ret_cache+i, + sizeof (HfsPExtDescriptor)); + priv_data->bad_blocks_xtent_list = new_xt; + priv_data->bad_blocks_xtent_nb++; + block += PED_BE32_TO_CPU ( + ret_cache[i].block_count); + } + } + first_pass = 0; + } + + priv_data->bad_blocks_loaded = 1; + return 1;} + +errbbp: hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list); + priv_data->bad_blocks_xtent_list=NULL; + priv_data->bad_blocks_xtent_nb=0; + return 0; +} + +/* This function check if fblock is a bad block */ +int _GL_ATTRIBUTE_PURE +hfsplus_is_bad_block (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPPrivateLinkExtent* walk; + + for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) { + /* Won't compile without the strange cast ! gcc bug ? */ + /* or maybe C subtilties... */ + if ((fblock >= PED_BE32_TO_CPU (walk->extent.start_block)) && + (fblock < (unsigned int)(PED_BE32_TO_CPU ( + walk->extent.start_block) + + PED_BE32_TO_CPU (walk->extent.block_count)))) + return 1; + } + + return 0; +} + +/* This function returns the first sector of the last free block of + an HFS+ volume we can get after a hfsplus_pack_free_space_from_block call */ +PedSector +hfsplus_get_empty_end (const PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + unsigned int block, last_bad, end_free_blocks; + + /* find the next block to the last bad block of the volume */ + if (!hfsplus_read_bad_blocks (fs)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad blocks could not be read.")); + return 0; + } + + HfsPPrivateLinkExtent* l; + last_bad = 0; + for (l = priv_data->bad_blocks_xtent_list; l; l = l->next) { + if ((unsigned int) PED_BE32_TO_CPU (l->extent.start_block) + + PED_BE32_TO_CPU (l->extent.block_count) > last_bad) + last_bad = PED_BE32_TO_CPU (l->extent.start_block) + + PED_BE32_TO_CPU (l->extent.block_count); + } + + /* Count the free blocks from last_bad to the end of the volume */ + end_free_blocks = 0; + for (block = last_bad; + block < PED_BE32_TO_CPU (vh->total_blocks); + block++) { + if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + end_free_blocks++; + } + + /* Calculate the block that will by the first free at + the end of the volume */ + block = PED_BE32_TO_CPU (vh->total_blocks) - end_free_blocks; + + return (PedSector) block * ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ); +} + +/* On error, returns 0 */ +PedSector +hfsplus_get_min_size (const PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + PedSector min_size; + + /* don't need to add anything because every sector + can be part of allocation blocks in HFS+, and + the last block _must_ be reserved */ + min_size = hfsplus_get_empty_end(fs); + if (!min_size) return 0; + + if (priv_data->wrapper) { + HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + unsigned int hfs_sect_block; + PedSector hgee; + hfs_sect_block = + PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + /* + * if hfs+ is embedded in an hfs wrapper then the new size is : + * the new size of the hfs+ volume rounded up to the size + * of hfs blocks + * + the minimum size of the hfs wrapper without any hfs+ + * modification + * - the current size of the hfs+ volume in the hfs wrapper + */ + hgee = hfs_get_empty_end(priv_data->wrapper); + if (!hgee) return 0; + min_size = ((min_size + hfs_sect_block - 1) / hfs_sect_block) + * hfs_sect_block + + hgee + 2 + - (PedSector) PED_BE16_TO_CPU ( hfs_priv_data->mdb + ->old_new.embedded + .location.block_count ) + * hfs_sect_block; + } + + return min_size; +} + +/* return the block which should be used to pack data to have + at least free fblock blocks at the end of the volume */ +unsigned int _GL_ATTRIBUTE_PURE +hfsplus_find_start_pack (const PedFileSystem *fs, unsigned int fblock) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + unsigned int block; + + for (block = PED_BE32_TO_CPU (priv_data->vh->total_blocks) - 1; + block && fblock; + block--) { + if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + fblock--; + } + + while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + block--; + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) + block++; + + return block; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/hfs/advfs_plus.h b/libparted/fs/r/hfs/advfs_plus.h new file mode 100644 index 0000000..61972c2 --- /dev/null +++ b/libparted/fs/r/hfs/advfs_plus.h @@ -0,0 +1,52 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 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/>. +*/ + +#ifndef _ADVFS_PLUS_H +#define _ADVFS_PLUS_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfsplus_btree_search (HfsPPrivateFile* b_tree_file, HfsPPrivateGenericKey* key, + void *record_out, unsigned int record_size, + HfsCPrivateLeafRec* record_ref); + +void +hfsplus_free_bad_blocks_list(HfsPPrivateLinkExtent* first); + +int +hfsplus_read_bad_blocks (const PedFileSystem *fs); + +int +hfsplus_is_bad_block (const PedFileSystem *fs, unsigned int fblock); + +PedSector +hfsplus_get_empty_end (const PedFileSystem *fs); + +PedSector +hfsplus_get_min_size (const PedFileSystem *fs); + +unsigned int +hfsplus_find_start_pack (const PedFileSystem *fs, unsigned int fblock); + +#endif /* _ADVFS_PLUS_H */ diff --git a/libparted/fs/r/hfs/cache.c b/libparted/fs/r/hfs/cache.c new file mode 100644 index 0000000..255f1fd --- /dev/null +++ b/libparted/fs/r/hfs/cache.c @@ -0,0 +1,239 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 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/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" + +#include "cache.h" + +static HfsCPrivateCacheTable* +hfsc_new_cachetable(unsigned int size) +{ + HfsCPrivateCacheTable* ret; + + ret = (HfsCPrivateCacheTable*) ped_malloc(sizeof(*ret)); + if (!ret) return NULL; + + ret->next_cache = NULL; + ret->table_size = size; + ret->table_first_free = 0; + + ret->table = ped_malloc(sizeof(*ret->table)*size); + if (!ret->table) { free(ret); return NULL; } + memset(ret->table, 0, sizeof(*ret->table)*size); + + return ret; +} + +HfsCPrivateCache* +hfsc_new_cache(unsigned int block_number, unsigned int file_number) +{ + unsigned int cachetable_size, i; + HfsCPrivateCache* ret; + + ret = (HfsCPrivateCache*) ped_malloc(sizeof(*ret)); + if (!ret) return NULL; + ret->block_number = block_number; + /* following code avoid integer overflow */ + ret->linked_ref_size = block_number > block_number + ((1<<CR_SHIFT)-1) ? + ( block_number >> CR_SHIFT ) + 1 : + ( block_number + ((1<<CR_SHIFT)-1) ) >> CR_SHIFT + ; + + ret->linked_ref = (HfsCPrivateExtent**) + ped_malloc( sizeof(*ret->linked_ref) + * ret->linked_ref_size ); + if (!ret->linked_ref) { free(ret); return NULL; } + + cachetable_size = file_number + file_number / CR_OVER_DIV + CR_ADD_CST; + if (cachetable_size < file_number) cachetable_size = (unsigned) -1; + ret->first_cachetable_size = cachetable_size; + ret->table_list = hfsc_new_cachetable(cachetable_size); + if (!ret->table_list) { + free(ret->linked_ref); + free(ret); + return NULL; + } + ret->last_table = ret->table_list; + + for (i = 0; i < ret->linked_ref_size; ++i) + ret->linked_ref[i] = NULL; + + ret->needed_alloc_size = 0; + + return ret; +} + +static void +hfsc_delete_cachetable(HfsCPrivateCacheTable* list) +{ + HfsCPrivateCacheTable* next; + + while (list) { + free (list->table); + next = list->next_cache; + free (list); + list = next; + } +} + +void +hfsc_delete_cache(HfsCPrivateCache* cache) +{ + hfsc_delete_cachetable(cache->table_list); + free(cache->linked_ref); + free(cache); +} + +HfsCPrivateExtent* +hfsc_cache_add_extent(HfsCPrivateCache* cache, uint32_t start, uint32_t length, + uint32_t block, uint16_t offset, uint8_t sbb, + uint8_t where, uint8_t ref_index) +{ + HfsCPrivateExtent* ext; + unsigned int idx = start >> CR_SHIFT; + + PED_ASSERT(idx < cache->linked_ref_size); + + for (ext = cache->linked_ref[idx]; + ext && start != ext->ext_start; + ext = ext->next); + + if (ext) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to register an extent starting at block " + "0x%X, but another one already exists at this " + "position. You should check the file system!"), + start); + return NULL; + } + + if ( cache->last_table->table_first_free + == cache->last_table->table_size ) { + cache->last_table->next_cache = + hfsc_new_cachetable( ( cache->first_cachetable_size + / CR_NEW_ALLOC_DIV ) + + CR_ADD_CST ); + if (!cache->last_table->next_cache) + return NULL; + cache->last_table = cache->last_table->next_cache; + } + + ext = cache->last_table->table+(cache->last_table->table_first_free++); + + ext->ext_start = start; + ext->ext_length = length; + ext->ref_block = block; + ext->ref_offset = offset; + ext->sect_by_block = sbb; + ext->where = where; + ext->ref_index = ref_index; + + ext->next = cache->linked_ref[idx]; + cache->linked_ref[idx] = ext; + + cache->needed_alloc_size = cache->needed_alloc_size > + (unsigned) PED_SECTOR_SIZE_DEFAULT * sbb ? + cache->needed_alloc_size : + (unsigned) PED_SECTOR_SIZE_DEFAULT * sbb; + + return ext; +} + +HfsCPrivateExtent* _GL_ATTRIBUTE_PURE +hfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start) +{ + HfsCPrivateExtent* ret; + unsigned int idx = start >> CR_SHIFT; + + PED_ASSERT(idx < cache->linked_ref_size); + + for (ret = cache->linked_ref[idx]; + ret && start != ret->ext_start; + ret = ret->next); + + return ret; +} + +/* Can't fail if extent begining at old_start exists */ +/* Returns 0 if no such extent, or on error */ +HfsCPrivateExtent* +hfsc_cache_move_extent(HfsCPrivateCache* cache, uint32_t old_start, + uint32_t new_start) +{ + HfsCPrivateExtent** ppext; + HfsCPrivateExtent* pext; + + unsigned int idx1 = old_start >> CR_SHIFT; + unsigned int idx2 = new_start >> CR_SHIFT; + + PED_ASSERT(idx1 < cache->linked_ref_size); + PED_ASSERT(idx2 < cache->linked_ref_size); + + for (pext = cache->linked_ref[idx2]; + pext && new_start != pext->ext_start; + pext = pext->next); + + if (pext) { + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("Trying to move an extent from block 0x%X to block " + "0x%X, but another one already exists at this " + "position. This should not happen!"), + old_start, new_start); + return NULL; + } + + for (ppext = &(cache->linked_ref[idx1]); + (*ppext) && old_start != (*ppext)->ext_start; + ppext = &((*ppext)->next)); + + if (!(*ppext)) return NULL; + + /* removing the extent from the cache */ + pext = *ppext; + (*ppext) = pext->next; + + /* change ext_start and insert the extent again */ + pext->ext_start = new_start; + pext->next = cache->linked_ref[idx2]; + cache->linked_ref[idx2] = pext; + + return pext; +} + +#endif /* DISCOVER_ONLY */ diff --git a/libparted/fs/r/hfs/cache.h b/libparted/fs/r/hfs/cache.h new file mode 100644 index 0000000..d009fd9 --- /dev/null +++ b/libparted/fs/r/hfs/cache.h @@ -0,0 +1,118 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 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/>. +*/ + +#ifndef _CACHE_H +#define _CACHE_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +/* CR => CACHE REF */ +#define CR_NULL 0 /* reserved */ +#define CR_PRIM_CAT 1 +#define CR_PRIM_EXT 2 +#define CR_PRIM_ATTR 3 +#define CR_PRIM_ALLOC 4 +#define CR_PRIM_START 5 +#define CR_BTREE_CAT 6 +#define CR_BTREE_ATTR 7 +#define CR_BTREE_EXT_0 8 +#define CR_BTREE_EXT_CAT 9 +#define CR_BTREE_EXT_EXT 10 /* should not happen ! */ +#define CR_BTREE_EXT_ATTR 11 +#define CR_BTREE_EXT_ALLOC 12 +#define CR_BTREE_EXT_START 13 /* unneeded in current code */ +#define CR_BTREE_CAT_JIB 14 /* journal info block */ +#define CR_BTREE_CAT_JL 15 /* journal */ +/* 16 -> 31 || high order bit */ /* reserved */ + +/* tuning */ +#define CR_SHIFT 8 /* number of bits to shift start_block by */ + /* to get the index of the linked list */ +#define CR_OVER_DIV 16 /* alloc a table for (1+1/CR_OVER_DIV) * + file_number + CR_ADD_CST */ +#define CR_ADD_CST 16 +#define CR_NEW_ALLOC_DIV 4 /* divide the size of the first alloc table + by this value to allocate next tables */ + +/* See DOC for an explaination of this structure */ +/* Access read only from outside cache.c */ +struct _HfsCPrivateExtent { + struct _HfsCPrivateExtent* next; + uint32_t ext_start; + uint32_t ext_length; + uint32_t ref_block; + uint16_t ref_offset; + uint8_t sect_by_block; + unsigned where : 5; + unsigned ref_index : 3; /* 0 -> 7 */ +}; +typedef struct _HfsCPrivateExtent HfsCPrivateExtent; + +/* Internaly used by cache.c for custom memory managment only */ +struct _HfsCPrivateCacheTable { + struct _HfsCPrivateCacheTable* next_cache; + HfsCPrivateExtent* table; + unsigned int table_size; + unsigned int table_first_free; + /* first_elemt ? */ +}; +typedef struct _HfsCPrivateCacheTable HfsCPrivateCacheTable; + +/* Internaly used by cache.c for custom memory managment + and cache handling only */ +struct _HfsCPrivateCache { + HfsCPrivateCacheTable* table_list; + HfsCPrivateCacheTable* last_table; + HfsCPrivateExtent** linked_ref; + unsigned int linked_ref_size; + unsigned int block_number; + unsigned int first_cachetable_size; + unsigned int needed_alloc_size; +}; +typedef struct _HfsCPrivateCache HfsCPrivateCache; + +HfsCPrivateCache* +hfsc_new_cache(unsigned int block_number, unsigned int file_number); + +void +hfsc_delete_cache(HfsCPrivateCache* cache); + +HfsCPrivateExtent* +hfsc_cache_add_extent(HfsCPrivateCache* cache, uint32_t start, uint32_t length, + uint32_t block, uint16_t offset, uint8_t sbb, + uint8_t where, uint8_t index); + +HfsCPrivateExtent* +hfsc_cache_search_extent(HfsCPrivateCache* cache, uint32_t start); + +HfsCPrivateExtent* +hfsc_cache_move_extent(HfsCPrivateCache* cache, uint32_t old_start, + uint32_t new_start); + +static __inline__ unsigned int +hfsc_cache_needed_buffer(HfsCPrivateCache* cache) +{ + return cache->needed_alloc_size; +} + +#endif /* _CACHE_H */ diff --git a/libparted/fs/r/hfs/file.c b/libparted/fs/r/hfs/file.c new file mode 100644 index 0000000..6024d84 --- /dev/null +++ b/libparted/fs/r/hfs/file.c @@ -0,0 +1,229 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 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/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "advfs.h" + +#include "file.h" + +/* Open the data fork of a file with its first three extents and its CNID */ +HfsPrivateFile* +hfs_file_open (PedFileSystem *fs, uint32_t CNID, + HfsExtDataRec ext_desc, PedSector sect_nb) +{ + HfsPrivateFile* file; + + file = (HfsPrivateFile*) ped_malloc (sizeof (HfsPrivateFile)); + if (!file) return NULL; + + file->fs = fs; + file->sect_nb = sect_nb; + file->CNID = CNID; + memcpy(file->first, ext_desc, sizeof (HfsExtDataRec)); + file->start_cache = 0; + + return file; +} + +/* Close an HFS file */ +void +hfs_file_close (HfsPrivateFile* file) +{ + free (file); +} + +/* warning : only works on data forks */ +static int +hfs_get_extent_containing (HfsPrivateFile* file, unsigned int block, + HfsExtDataRec cache, uint16_t* ptr_start_cache) +{ + uint8_t record[sizeof (HfsExtentKey) + + sizeof (HfsExtDataRec)]; + HfsExtentKey search; + HfsExtentKey* ret_key = (HfsExtentKey*) record; + HfsExtDescriptor* ret_cache = (HfsExtDescriptor*) + (record + sizeof (HfsExtentKey)); + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + file->fs->type_specific; + + search.key_length = sizeof (HfsExtentKey) - 1; + search.type = HFS_DATA_FORK; + search.file_ID = file->CNID; + search.start = PED_CPU_TO_BE16 (block); + + if (!hfs_btree_search (priv_data->extent_file, + (HfsPrivateGenericKey*) &search, + record, sizeof (record), NULL)) + return 0; + + if (ret_key->file_ID != search.file_ID || ret_key->type != search.type) + return 0; + + memcpy (cache, ret_cache, sizeof(HfsExtDataRec)); + *ptr_start_cache = PED_BE16_TO_CPU (ret_key->start); + + return 1; +} + +/* find and return the nth sector of a file */ +/* return 0 on error */ +static PedSector +hfs_file_find_sector (HfsPrivateFile* file, PedSector sector) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + file->fs->type_specific; + unsigned int sect_by_block = PED_BE32_TO_CPU ( + priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + unsigned int i, s, vol_block; + unsigned int block = sector / sect_by_block; + unsigned int offset = sector % sect_by_block; + + /* in the three first extent */ + for (s = 0, i = 0; i < HFS_EXT_NB; i++) { + if ((block >= s) && ( block < s + PED_BE16_TO_CPU ( + file->first[i].block_count))) { + vol_block = (block - s) + PED_BE16_TO_CPU ( + file->first[i].start_block); + goto sector_found; + } + s += PED_BE16_TO_CPU (file->first[i].block_count); + } + + /* in the three cached extent */ + if (file->start_cache && block >= file->start_cache) + for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE16_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + PED_BE16_TO_CPU ( + file->cache[i].start_block); + goto sector_found; + } + s += PED_BE16_TO_CPU (file->cache[i].block_count); + } + + /* update cache */ + if (!hfs_get_extent_containing (file, block, file->cache, + &(file->start_cache))) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_CANCEL, + _("Could not update the extent cache for HFS file with " + "CNID %X."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + /* in the three cached extent */ + PED_ASSERT(file->start_cache && block >= file->start_cache); + for (s = file->start_cache, i = 0; i < HFS_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE16_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + PED_BE16_TO_CPU ( + file->cache[i].start_block); + goto sector_found; + } + s += PED_BE16_TO_CPU (file->cache[i].block_count); + } + + return 0; + + sector_found: + return (PedSector) PED_BE16_TO_CPU (priv_data->mdb->start_block) + + (PedSector) vol_block * sect_by_block + + offset; +} + +/* Read the nth sector of a file */ +/* return 0 on error */ +int +hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector) +{ + PedSector abs_sector; + + if (sector >= file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to read HFS file with CNID %X behind EOF."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + abs_sector = hfs_file_find_sector (file, sector); + if (!abs_sector) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not find sector %lli of HFS file with " + "CNID %X."), + sector, PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + return ped_geometry_read (file->fs->geom, buf, abs_sector, 1); +} + +/* Write the nth sector of a file */ +/* return 0 on error */ +int +hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector) +{ + PedSector abs_sector; + + if (sector >= file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to write HFS file with CNID %X behind EOF."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + abs_sector = hfs_file_find_sector (file, sector); + if (!abs_sector) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not find sector %lli of HFS file with " + "CNID %X."), + sector, PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + return ped_geometry_write (file->fs->geom, buf, abs_sector, 1); +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/hfs/file.h b/libparted/fs/r/hfs/file.h new file mode 100644 index 0000000..f8cb485 --- /dev/null +++ b/libparted/fs/r/hfs/file.h @@ -0,0 +1,42 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 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/>. +*/ + +#ifndef _FILE_H +#define _FILE_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +HfsPrivateFile* +hfs_file_open (PedFileSystem *fs, uint32_t CNID, + HfsExtDataRec ext_desc, PedSector sect_nb); + +void +hfs_file_close (HfsPrivateFile* file); + +int +hfs_file_read_sector (HfsPrivateFile* file, void *buf, PedSector sector); + +int +hfs_file_write_sector (HfsPrivateFile* file, void *buf, PedSector sector); + +#endif /* _FILE_H */ diff --git a/libparted/fs/r/hfs/file_plus.c b/libparted/fs/r/hfs/file_plus.c new file mode 100644 index 0000000..aeff5ee --- /dev/null +++ b/libparted/fs/r/hfs/file_plus.c @@ -0,0 +1,274 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 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/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "advfs_plus.h" + +#include "file_plus.h" + +/* Open the data fork of a file with its first eight extents and its CNID */ +/* CNID and ext_desc must be in disc order, sect_nb in CPU order */ +/* return null on failure */ +HfsPPrivateFile* +hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID, + HfsPExtDataRec ext_desc, PedSector sect_nb) +{ + HfsPPrivateFile* file; + + file = (HfsPPrivateFile*) ped_malloc (sizeof (HfsPPrivateFile)); + if (!file) return NULL; + + file->fs = fs; + file->sect_nb = sect_nb; + file->CNID = CNID; + memcpy(file->first, ext_desc, sizeof (HfsPExtDataRec)); + file->start_cache = 0; + + return file; +} + +/* Close an HFS+ file */ +void +hfsplus_file_close (HfsPPrivateFile* file) +{ + free (file); +} + +/* warning : only works on data forks */ +static int +hfsplus_get_extent_containing (HfsPPrivateFile* file, unsigned int block, + HfsPExtDataRec cache, uint32_t* ptr_start_cache) +{ + uint8_t record[sizeof (HfsPExtentKey) + + sizeof (HfsPExtDataRec)]; + HfsPExtentKey search; + HfsPExtentKey* ret_key = (HfsPExtentKey*) record; + HfsPExtDescriptor* ret_cache = (HfsPExtDescriptor*) + (record + sizeof (HfsPExtentKey)); + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + + search.key_length = PED_CPU_TO_BE16 (sizeof (HfsPExtentKey) - 2); + search.type = HFS_DATA_FORK; + search.pad = 0; + search.file_ID = file->CNID; + search.start = PED_CPU_TO_BE32 (block); + + if (!hfsplus_btree_search (priv_data->extents_file, + (HfsPPrivateGenericKey*) &search, + record, sizeof (record), NULL)) + return 0; + + if (ret_key->file_ID != search.file_ID || ret_key->type != search.type) + return 0; + + memcpy (cache, ret_cache, sizeof(HfsPExtDataRec)); + *ptr_start_cache = PED_BE32_TO_CPU (ret_key->start); + + return 1; +} + +/* find a sub extent contained in the desired area */ +/* and with the same starting point */ +/* return 0 in sector_count on error, or the physical area */ +/* on the volume corresponding to the logical area in the file */ +static HfsPPrivateExtent +hfsplus_file_find_extent (HfsPPrivateFile* file, PedSector sector, + unsigned int nb) +{ + HfsPPrivateExtent ret = {0,0}; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + unsigned int sect_by_block = PED_BE32_TO_CPU ( + priv_data->vh->block_size) + / PED_SECTOR_SIZE_DEFAULT; + unsigned int i, s, vol_block, size; + PedSector sect_size; + unsigned int block = sector / sect_by_block; + unsigned int offset = sector % sect_by_block; + + /* in the 8 first extent */ + for (s = 0, i = 0; i < HFSP_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE32_TO_CPU ( + file->first[i].block_count))) { + vol_block = (block - s) + + PED_BE32_TO_CPU (file->first[i] + .start_block); + size = PED_BE32_TO_CPU (file->first[i].block_count) + + s - block; + goto plus_sector_found; + } + s += PED_BE32_TO_CPU (file->first[i].block_count); + } + + /* in the 8 cached extent */ + if (file->start_cache && block >= file->start_cache) + for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE32_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + + PED_BE32_TO_CPU (file->cache[i] + .start_block); + size = PED_BE32_TO_CPU (file->cache[i].block_count) + + s - block; + goto plus_sector_found; + } + s += PED_BE32_TO_CPU (file->cache[i].block_count); + } + + /* update cache */ + if (!hfsplus_get_extent_containing (file, block, file->cache, + &(file->start_cache))) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_CANCEL, + _("Could not update the extent cache for HFS+ file " + "with CNID %X."), + PED_BE32_TO_CPU(file->CNID)); + return ret; /* ret == {0,0} */ + } + + /* ret == {0,0} */ + PED_ASSERT(file->start_cache && block >= file->start_cache); + + for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) { + if ((block >= s) && (block < s + PED_BE32_TO_CPU ( + file->cache[i].block_count))) { + vol_block = (block - s) + + PED_BE32_TO_CPU (file->cache[i] + .start_block); + size = PED_BE32_TO_CPU (file->cache[i].block_count) + + s - block; + goto plus_sector_found; + } + s += PED_BE32_TO_CPU (file->cache[i].block_count); + } + + return ret; + +plus_sector_found: + sect_size = (PedSector) size * sect_by_block - offset; + ret.start_sector = vol_block * sect_by_block + offset; + ret.sector_count = (sect_size < nb) ? sect_size : nb; + return ret; +} + +int +hfsplus_file_read(HfsPPrivateFile* file, void *buf, PedSector sector, + unsigned int nb) +{ + HfsPPrivateExtent phy_area; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + char *b = buf; + + if (sector+nb < sector /* detect overflow */ + || sector+nb > file->sect_nb) /* out of file */ { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to read HFS+ file with CNID %X behind EOF."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + while (nb) { + phy_area = hfsplus_file_find_extent(file, sector, nb); + if (phy_area.sector_count == 0) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not find sector %lli of HFS+ file " + "with CNID %X."), + sector, PED_BE32_TO_CPU(file->CNID)); + return 0; + } + if (!ped_geometry_read(priv_data->plus_geom, b, + phy_area.start_sector, + phy_area.sector_count)) + return 0; + + nb -= phy_area.sector_count; /* < nb anyway ... */ + sector += phy_area.sector_count; + b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT; + } + + return 1; +} + +int +hfsplus_file_write(HfsPPrivateFile* file, void *buf, PedSector sector, + unsigned int nb) +{ + HfsPPrivateExtent phy_area; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + file->fs->type_specific; + char *b = buf; + + if (sector+nb < sector /* detect overflow */ + || sector+nb > file->sect_nb) /* out of file */ { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Trying to write HFS+ file with CNID %X behind EOF."), + PED_BE32_TO_CPU(file->CNID)); + return 0; + } + + while (nb) { + phy_area = hfsplus_file_find_extent(file, sector, nb); + if (phy_area.sector_count == 0) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not find sector %lli of HFS+ file " + "with CNID %X."), + sector, PED_BE32_TO_CPU(file->CNID)); + return 0; + } + if (!ped_geometry_write(priv_data->plus_geom, b, + phy_area.start_sector, + phy_area.sector_count)) + return 0; + + nb -= phy_area.sector_count; /* < nb anyway ... */ + sector += phy_area.sector_count; + b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT; + } + + return 1; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/hfs/file_plus.h b/libparted/fs/r/hfs/file_plus.h new file mode 100644 index 0000000..cd54f3f --- /dev/null +++ b/libparted/fs/r/hfs/file_plus.h @@ -0,0 +1,61 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 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/>. +*/ + +#ifndef _FILE_PLUS_H +#define _FILE_PLUS_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +HfsPPrivateFile* +hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID, + HfsPExtDataRec ext_desc, PedSector sect_nb); + +void +hfsplus_file_close (HfsPPrivateFile* file); + +int +hfsplus_file_read(HfsPPrivateFile* file, void *buf, + PedSector sector, unsigned int nb); + +int +hfsplus_file_write(HfsPPrivateFile* file, void *buf, + PedSector sector, unsigned int nb); + +/* Read the nth sector of a file */ +/* return 0 on error */ +static __inline__ int +hfsplus_file_read_sector (HfsPPrivateFile* file, void *buf, PedSector sector) +{ + return hfsplus_file_read(file, buf, sector, 1); +} + +/* Write the nth sector of a file */ +/* return 0 on error */ +static __inline__ int +hfsplus_file_write_sector (HfsPPrivateFile* file, void *buf, PedSector sector) +{ + return hfsplus_file_write(file, buf, sector, 1); +} + + +#endif /* _FILE_PLUS_H */ diff --git a/libparted/fs/r/hfs/hfs.c b/libparted/fs/r/hfs/hfs.c new file mode 100644 index 0000000..8959b47 --- /dev/null +++ b/libparted/fs/r/hfs/hfs.c @@ -0,0 +1,1166 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2003-2005, 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/>. +*/ + +/* + Author : Guillaume Knispel <k_guillaume@libertysurf.fr> + Report bug to <bug-parted@gnu.org> +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "probe.h" + +uint8_t* hfs_block = NULL; +uint8_t* hfsp_block = NULL; +unsigned hfs_block_count; +unsigned hfsp_block_count; + +#define HFS_BLOCK_SIZES ((int[2]){512, 0}) +#define HFSP_BLOCK_SIZES ((int[2]){512, 0}) +#define HFSX_BLOCK_SIZES ((int[2]){512, 0}) + +#ifndef DISCOVER_ONLY +#include "file.h" +#include "reloc.h" +#include "advfs.h" + +static PedFileSystemType hfs_type; +static PedFileSystemType hfsplus_type; + + +/* ----- HFS ----- */ + +PedFileSystem * +hfs_open (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedFileSystem* fs; + HfsMasterDirectoryBlock* mdb; + HfsPrivateFSData* priv_data; + + if (!hfsc_can_use_geom (geom)) + return NULL; + + /* Read MDB */ + if (!ped_geometry_read (geom, buf, 2, 1)) + return NULL; + + /* Allocate memory */ + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) goto ho; + mdb = (HfsMasterDirectoryBlock*) + ped_malloc (sizeof (HfsMasterDirectoryBlock)); + if (!mdb) goto ho_fs; + priv_data = (HfsPrivateFSData*) + ped_malloc (sizeof (HfsPrivateFSData)); + if (!priv_data) goto ho_mdb; + + memcpy (mdb, buf, sizeof (HfsMasterDirectoryBlock)); + + /* init structures */ + priv_data->mdb = mdb; + priv_data->bad_blocks_loaded = 0; + priv_data->bad_blocks_xtent_nb = 0; + priv_data->bad_blocks_xtent_list = NULL; + priv_data->extent_file = + hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID), + mdb->extents_file_rec, + PED_CPU_TO_BE32 (mdb->extents_file_size) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->extent_file) goto ho_pd; + priv_data->catalog_file = + hfs_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID), + mdb->catalog_file_rec, + PED_CPU_TO_BE32 (mdb->catalog_file_size) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->catalog_file) goto ho_ce; + /* Read allocation blocks */ + if (!ped_geometry_read(geom, priv_data->alloc_map, + PED_BE16_TO_CPU (mdb->volume_bitmap_block), + ( PED_BE16_TO_CPU (mdb->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8) ) ) + goto ho_cf; + + fs->type = &hfs_type; + fs->geom = ped_geometry_duplicate (geom); + if (!fs->geom) goto ho_cf; + fs->type_specific = (void*) priv_data; + fs->checked = ( PED_BE16_TO_CPU (mdb->volume_attributes) + >> HFS_UNMOUNTED ) & 1; + + return fs; + +/*--- clean error handling ---*/ +ho_cf: hfs_file_close(priv_data->catalog_file); +ho_ce: hfs_file_close(priv_data->extent_file); +ho_pd: free(priv_data); +ho_mdb: free(mdb); +ho_fs: free(fs); +ho: return NULL; +} + +int +hfs_close (PedFileSystem *fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) fs->type_specific; + + hfs_file_close (priv_data->extent_file); + hfs_file_close (priv_data->catalog_file); + if (priv_data->bad_blocks_loaded) + hfs_free_bad_blocks_list (priv_data->bad_blocks_xtent_list); + free (priv_data->mdb); + free (priv_data); + ped_geometry_destroy (fs->geom); + free (fs); + + return 1; +} + +PedConstraint * +hfs_get_resize_constraint (const PedFileSystem *fs) +{ + PedDevice* dev = fs->geom->dev; + PedAlignment start_align; + PedGeometry start_sector; + PedGeometry full_dev; + PedSector min_size; + + if (!ped_alignment_init (&start_align, fs->geom->start, 0)) + return NULL; + if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1)) + return NULL; + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + /* 2 = last two sectors (alternate MDB and unused sector) */ + min_size = hfs_get_empty_end(fs) + 2; + if (min_size == 2) return NULL; + + return ped_constraint_new (&start_align, ped_alignment_any, + &start_sector, &full_dev, min_size, + fs->geom->length); +} + +int +hfs_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + unsigned int nblock, nfree; + unsigned int block, to_free; + HfsPrivateFSData* priv_data; + HfsMasterDirectoryBlock* mdb; + int resize = 1; + unsigned int hfs_sect_block; + PedSector hgee; + + /* check preconditions */ + PED_ASSERT (fs != NULL); + PED_ASSERT (fs->geom != NULL); + PED_ASSERT (geom != NULL); +#ifdef DEBUG + PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0); +#else + if ((hgee = hfs_get_empty_end(fs)) == 0) + return 0; +#endif + + PED_ASSERT ((hgee = hfs_get_empty_end(fs)) != 0); + + if (ped_geometry_test_equal(fs->geom, geom)) + return 1; + + priv_data = (HfsPrivateFSData*) fs->type_specific; + mdb = priv_data->mdb; + hfs_sect_block = PED_BE32_TO_CPU (mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + + if (fs->geom->start != geom->start + || geom->length > fs->geom->length + || geom->length < hgee + 2) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Sorry, HFS cannot be resized that way yet.")); + return 0; + } + + /* Flush caches */ + if (!ped_geometry_sync(fs->geom)) + return 0; + + /* Clear the unmounted bit */ + mdb->volume_attributes &= PED_CPU_TO_BE16 (~( 1 << HFS_UNMOUNTED )); + if (!ped_geometry_read (fs->geom, buf, 2, 1)) + return 0; + memcpy (buf, mdb, sizeof (HfsMasterDirectoryBlock)); + if ( !ped_geometry_write (fs->geom, buf, 2, 1) + || !ped_geometry_sync (fs->geom)) + return 0; + + ped_timer_reset (timer); + ped_timer_set_state_name(timer, _("shrinking")); + ped_timer_update(timer, 0.0); + /* relocate data */ + to_free = ( fs->geom->length - geom->length + + hfs_sect_block - 1 ) + / hfs_sect_block ; + block = hfs_find_start_pack (fs, to_free); + if (!hfs_pack_free_space_from_block (fs, block, timer, to_free)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Data relocation has failed.")); + goto write_MDB; + } + + /* Calculate new block number and other MDB field */ + nblock = ( geom->length - (PED_BE16_TO_CPU (mdb->start_block) + 2) ) + / hfs_sect_block; + nfree = PED_BE16_TO_CPU (mdb->free_blocks) + - ( PED_BE16_TO_CPU (mdb->total_blocks) - nblock ); + + /* Check that all block after future end are really free */ + for (block = nblock; + block < PED_BE16_TO_CPU (mdb->total_blocks); + block++) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Data relocation left some data in the end " + "of the volume.")); + goto write_MDB; + } + } + + /* Mark out of volume blocks as used + (broken implementations compatibility) */ + for ( block = nblock; block < (1 << 16); ++block) + SET_BLOC_OCCUPATION(priv_data->alloc_map,block); + + /* save the allocation map + I do not write until start of allocation blocks + but only until pre-resize end of bitmap blocks + because the specifications do _not_ assert that everything + until allocation blocks is boot, mdb and alloc */ + ped_geometry_write(fs->geom, priv_data->alloc_map, + PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block), + ( PED_BE16_TO_CPU (priv_data->mdb->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1) + / (PED_SECTOR_SIZE_DEFAULT * 8)); + + /* Update geometry */ + if (resize) { + /* update in fs structure */ + if (PED_BE16_TO_CPU (mdb->next_allocation) >= nblock) + mdb->next_allocation = PED_CPU_TO_BE16 (0); + mdb->total_blocks = PED_CPU_TO_BE16 (nblock); + mdb->free_blocks = PED_CPU_TO_BE16 (nfree); + /* update parted structure */ + fs->geom->length = geom->length; + fs->geom->end = fs->geom->start + geom->length - 1; + } + + /* Set the unmounted bit */ + mdb->volume_attributes |= PED_CPU_TO_BE16 ( 1 << HFS_UNMOUNTED ); + + /* Effective write */ + write_MDB: + ped_timer_set_state_name(timer,_("writing HFS Master Directory Block")); + + if (!hfs_update_mdb(fs)) { + ped_geometry_sync(geom); + return 0; + } + + if (!ped_geometry_sync(geom)) + return 0; + + ped_timer_update(timer, 1.0); + + return (resize); +} + +/* ----- HFS+ ----- */ + +#include "file_plus.h" +#include "advfs_plus.h" +#include "reloc_plus.h" +#include "journal.h" + +int +hfsplus_close (PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + + if (priv_data->bad_blocks_loaded) + hfsplus_free_bad_blocks_list(priv_data->bad_blocks_xtent_list); + free(priv_data->alloc_map); + free(priv_data->dirty_alloc_map); + hfsplus_file_close (priv_data->allocation_file); + hfsplus_file_close (priv_data->attributes_file); + hfsplus_file_close (priv_data->catalog_file); + hfsplus_file_close (priv_data->extents_file); + if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom); + if (priv_data->wrapper) hfs_close(priv_data->wrapper); + ped_geometry_destroy (fs->geom); + free(priv_data->vh); + free(priv_data); + free(fs); + + return 1; +} + +PedFileSystem* +hfsplus_open (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedFileSystem* fs; + HfsPVolumeHeader* vh; + HfsPPrivateFSData* priv_data; + PedGeometry* wrapper_geom; + unsigned int map_sectors; + + if (!hfsc_can_use_geom (geom)) + return NULL; + + fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem)); + if (!fs) goto hpo; + vh = (HfsPVolumeHeader*) ped_malloc (sizeof (HfsPVolumeHeader)); + if (!vh) goto hpo_fs; + priv_data = (HfsPPrivateFSData*)ped_malloc (sizeof (HfsPPrivateFSData)); + if (!priv_data) goto hpo_vh; + + fs->geom = ped_geometry_duplicate (geom); + if (!fs->geom) goto hpo_pd; + fs->type_specific = (void*) priv_data; + + if ((wrapper_geom = hfs_and_wrapper_probe (geom))) { + HfsPrivateFSData* hfs_priv_data; + PedSector abs_sect, length; + unsigned int bs; + + ped_geometry_destroy (wrapper_geom); + priv_data->wrapper = hfs_open(geom); + if (!priv_data->wrapper) goto hpo_gm; + hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + bs = PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + abs_sect = (PedSector) geom->start + + (PedSector) PED_BE16_TO_CPU ( + hfs_priv_data->mdb->start_block) + + (PedSector) PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new + .embedded.location.start_block ) + * bs; + length = (PedSector) PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new + .embedded.location.block_count) + * bs; + priv_data->plus_geom = ped_geometry_new (geom->dev, abs_sect, + length); + if (!priv_data->plus_geom) goto hpo_wr; + priv_data->free_geom = 1; + } else { + priv_data->wrapper = NULL; + priv_data->plus_geom = fs->geom; + priv_data->free_geom = 0; + } + + if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1)) goto hpo_pg; + memcpy (vh, buf, sizeof (HfsPVolumeHeader)); + priv_data->vh = vh; + + if (vh->signature != PED_CPU_TO_BE16(HFSP_SIGNATURE) + && vh->signature != PED_CPU_TO_BE16(HFSX_SIGNATURE)) { + ped_exception_throw ( + PED_EXCEPTION_BUG, + PED_EXCEPTION_CANCEL, + _("No valid HFS[+X] signature has been found while " + "opening.")); + goto hpo_pg; + } + + if (vh->signature == PED_CPU_TO_BE16(HFSP_SIGNATURE) + && vh->version != PED_CPU_TO_BE16(HFSP_VERSION)) { + if (ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_IGNORE_CANCEL, + _("Version %d of HFS+ isn't supported."), + PED_BE16_TO_CPU(vh->version)) + != PED_EXCEPTION_IGNORE) + goto hpo_pg; + } + + if (vh->signature == PED_CPU_TO_BE16(HFSX_SIGNATURE) + && vh->version != PED_CPU_TO_BE16(HFSX_VERSION)) { + if (ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_IGNORE_CANCEL, + _("Version %d of HFSX isn't supported."), + PED_BE16_TO_CPU(vh->version)) + != PED_EXCEPTION_IGNORE) + goto hpo_pg; + } + + priv_data->jib_start_block = 0; + priv_data->jl_start_block = 0; + if (vh->attributes & PED_CPU_TO_BE32(1<<HFSP_JOURNALED)) { + if (!hfsj_replay_journal(fs)) + goto hpo_pg; + } + + priv_data->bad_blocks_loaded = 0; + priv_data->bad_blocks_xtent_nb = 0; + priv_data->bad_blocks_xtent_list = NULL; + priv_data->extents_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_XTENT_ID), + vh->extents_file.extents, + PED_BE64_TO_CPU ( + vh->extents_file.logical_size ) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->extents_file) goto hpo_pg; + priv_data->catalog_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFS_CATALOG_ID), + vh->catalog_file.extents, + PED_BE64_TO_CPU ( + vh->catalog_file.logical_size ) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->catalog_file) goto hpo_ce; + priv_data->attributes_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ATTRIB_ID), + vh->attributes_file.extents, + PED_BE64_TO_CPU ( + vh->attributes_file.logical_size) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->attributes_file) goto hpo_cc; + + map_sectors = ( PED_BE32_TO_CPU (vh->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8); + priv_data->dirty_alloc_map = (uint8_t*) + ped_malloc ((map_sectors + 7) / 8); + if (!priv_data->dirty_alloc_map) goto hpo_cl; + memset(priv_data->dirty_alloc_map, 0, (map_sectors + 7) / 8); + priv_data->alloc_map = (uint8_t*) + ped_malloc (map_sectors * PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->alloc_map) goto hpo_dm; + + priv_data->allocation_file = + hfsplus_file_open (fs, PED_CPU_TO_BE32 (HFSP_ALLOC_ID), + vh->allocation_file.extents, + PED_BE64_TO_CPU ( + vh->allocation_file.logical_size) + / PED_SECTOR_SIZE_DEFAULT); + if (!priv_data->allocation_file) goto hpo_am; + if (!hfsplus_file_read (priv_data->allocation_file, + priv_data->alloc_map, 0, map_sectors)) { + hfsplus_close(fs); + return NULL; + } + + fs->type = &hfsplus_type; + fs->checked = ((PED_BE32_TO_CPU (vh->attributes) >> HFS_UNMOUNTED) & 1) + && !((PED_BE32_TO_CPU (vh->attributes) >> HFSP_INCONSISTENT) & 1); + + return fs; + +/*--- clean error handling ---*/ +hpo_am: free(priv_data->alloc_map); +hpo_dm: free(priv_data->dirty_alloc_map); +hpo_cl: hfsplus_file_close (priv_data->attributes_file); +hpo_cc: hfsplus_file_close (priv_data->catalog_file); +hpo_ce: hfsplus_file_close (priv_data->extents_file); +hpo_pg: if (priv_data->free_geom) ped_geometry_destroy (priv_data->plus_geom); +hpo_wr: if (priv_data->wrapper) hfs_close(priv_data->wrapper); +hpo_gm: ped_geometry_destroy (fs->geom); +hpo_pd: free(priv_data); +hpo_vh: free(vh); +hpo_fs: free(fs); +hpo: return NULL; +} + +PedConstraint * +hfsplus_get_resize_constraint (const PedFileSystem *fs) +{ + PedDevice* dev = fs->geom->dev; + PedAlignment start_align; + PedGeometry start_sector; + PedGeometry full_dev; + PedSector min_size; + + if (!ped_alignment_init (&start_align, fs->geom->start, 0)) + return NULL; + if (!ped_geometry_init (&start_sector, dev, fs->geom->start, 1)) + return NULL; + if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) + return NULL; + + min_size = hfsplus_get_min_size (fs); + if (!min_size) return NULL; + + return ped_constraint_new (&start_align, ped_alignment_any, + &start_sector, &full_dev, min_size, + fs->geom->length); +} + +static int +hfsplus_volume_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + unsigned int nblock, nfree, mblock; + unsigned int block, to_free, old_blocks; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + int resize = 1; + unsigned int hfsp_sect_block = + ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ); + unsigned int map_sectors; + + old_blocks = PED_BE32_TO_CPU (vh->total_blocks); + + /* Flush caches */ + if (!ped_geometry_sync(priv_data->plus_geom)) + return 0; + + /* Clear the unmounted bit */ + /* and set the implementation code (Apple Creator Code) */ + vh->attributes &= PED_CPU_TO_BE32 (~( 1 << HFS_UNMOUNTED )); + vh->last_mounted_version = PED_CPU_TO_BE32(HFSP_IMPL_Shnk); + if (!ped_geometry_read (priv_data->plus_geom, buf, 2, 1)) + return 0; + memcpy (buf, vh, sizeof (HfsPVolumeHeader)); + if ( !ped_geometry_write (priv_data->plus_geom, buf, 2, 1) + || !ped_geometry_sync (priv_data->plus_geom)) + return 0; + + ped_timer_reset (timer); + ped_timer_set_state_name(timer, _("shrinking")); + ped_timer_update(timer, 0.0); + /* relocate data */ + to_free = ( priv_data->plus_geom->length + - geom->length + hfsp_sect_block + - 1 ) / hfsp_sect_block; + block = hfsplus_find_start_pack (fs, to_free); + if (!hfsplus_pack_free_space_from_block (fs, block, timer, to_free)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Data relocation has failed.")); + goto write_VH; + } + + /* Calculate new block number and other VH field */ + /* nblock must be rounded _down_ */ + nblock = geom->length / hfsp_sect_block; + nfree = PED_BE32_TO_CPU (vh->free_blocks) + - (old_blocks - nblock); + /* free block readjustement is only needed when incorrect nblock + was used by my previous implementation, so detect the case */ + if (priv_data->plus_geom->length < old_blocks + * ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT) ) { + if (priv_data->plus_geom->length % hfsp_sect_block == 1) + nfree++; + } + + /* Check that all block after future end are really free */ + mblock = ( priv_data->plus_geom->length - 2 ) + / hfsp_sect_block; + if (mblock > old_blocks - 1) + mblock = old_blocks - 1; + for ( block = nblock; + block < mblock; + block++ ) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Data relocation left some data at the end " + "of the volume.")); + goto write_VH; + } + } + + /* Mark out of volume blocks as used */ + map_sectors = ( ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8) ) + * (PED_SECTOR_SIZE_DEFAULT * 8); + for ( block = nblock; block < map_sectors; ++block) + SET_BLOC_OCCUPATION(priv_data->alloc_map, block); + + /* Update geometry */ + if (resize) { + /* update in fs structure */ + if (PED_BE32_TO_CPU (vh->next_allocation) >= nblock) + vh->next_allocation = PED_CPU_TO_BE32 (0); + vh->total_blocks = PED_CPU_TO_BE32 (nblock); + vh->free_blocks = PED_CPU_TO_BE32 (nfree); + /* update parted structure */ + priv_data->plus_geom->length = geom->length; + priv_data->plus_geom->end = priv_data->plus_geom->start + + geom->length - 1; + } + + /* Effective write */ + write_VH: + /* lasts two sectors are allocated by the alternate VH + and a reserved sector, and last block is always reserved */ + block = (priv_data->plus_geom->length - 1) / hfsp_sect_block; + if (block < PED_BE32_TO_CPU (vh->total_blocks)) + SET_BLOC_OCCUPATION(priv_data->alloc_map, block); + block = (priv_data->plus_geom->length - 2) / hfsp_sect_block; + if (block < PED_BE32_TO_CPU (vh->total_blocks)) + SET_BLOC_OCCUPATION(priv_data->alloc_map, block); + SET_BLOC_OCCUPATION(priv_data->alloc_map, + PED_BE32_TO_CPU (vh->total_blocks) - 1); + + /* Write the _old_ area to set out of volume blocks as used */ + map_sectors = ( old_blocks + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8); + if (!hfsplus_file_write (priv_data->allocation_file, + priv_data->alloc_map, 0, map_sectors)) { + resize = 0; + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Error while writing the allocation file.")); + } else { + /* Write remaining part of allocation bitmap */ + /* This is necessary to handle pre patch-11 and third party */ + /* implementations */ + memset(buf, 0xFF, PED_SECTOR_SIZE_DEFAULT); + for (block = map_sectors; + block < priv_data->allocation_file->sect_nb; + ++block) { + if (!hfsplus_file_write_sector ( + priv_data->allocation_file, + buf, block)) { + ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("Error while writing the " + "compatibility part of the " + "allocation file.")); + break; + } + } + } + ped_geometry_sync (priv_data->plus_geom); + + if (resize) { + /* Set the unmounted bit and clear the inconsistent bit */ + vh->attributes |= PED_CPU_TO_BE32 ( 1 << HFS_UNMOUNTED ); + vh->attributes &= ~ PED_CPU_TO_BE32 ( 1 << HFSP_INCONSISTENT ); + } + + ped_timer_set_state_name(timer, _("writing HFS+ Volume Header")); + if (!hfsplus_update_vh(fs)) { + ped_geometry_sync(priv_data->plus_geom); + return 0; + } + + if (!ped_geometry_sync(priv_data->plus_geom)) + return 0; + + ped_timer_update(timer, 1.0); + + return (resize); +} + +/* Update the HFS wrapper mdb and bad blocks file to reflect + the new geometry of the embedded HFS+ volume */ +static int +hfsplus_wrapper_update (PedFileSystem* fs) +{ + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsCPrivateLeafRec ref; + HfsExtentKey key; + HfsNodeDescriptor* node_desc = (HfsNodeDescriptor*) node; + HfsExtentKey* ret_key; + HfsExtDescriptor* ret_data; + unsigned int i; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + unsigned int hfs_sect_block = + PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT ; + PedSector hfsplus_sect = (PedSector) + PED_BE32_TO_CPU (priv_data->vh->total_blocks) + * ( PED_BE32_TO_CPU (priv_data->vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ); + unsigned int hfs_blocks_embedded = + (hfsplus_sect + hfs_sect_block - 1) + / hfs_sect_block; + unsigned int hfs_blocks_embedded_old; + + /* update HFS wrapper MDB */ + hfs_blocks_embedded_old = PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new + .embedded.location.block_count ); + hfs_priv_data->mdb->old_new.embedded.location.block_count = + PED_CPU_TO_BE16 (hfs_blocks_embedded); + /* maybe macOS will boot with this */ + /* update : yes it does \o/ :) */ + hfs_priv_data->mdb->free_blocks = + PED_CPU_TO_BE16 ( PED_BE16_TO_CPU (hfs_priv_data->mdb->free_blocks) + + hfs_blocks_embedded_old + - hfs_blocks_embedded ); + + if (!hfs_update_mdb(priv_data->wrapper)) + return 0; + + /* force reload bad block list */ + if (hfs_priv_data->bad_blocks_loaded) { + hfs_free_bad_blocks_list (hfs_priv_data->bad_blocks_xtent_list); + hfs_priv_data->bad_blocks_xtent_list = NULL; + hfs_priv_data->bad_blocks_xtent_nb = 0; + hfs_priv_data->bad_blocks_loaded = 0; + } + + /* clean HFS wrapper allocation map */ + for (i = PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new.embedded + .location.start_block ) + + hfs_blocks_embedded; + i < PED_BE16_TO_CPU ( + hfs_priv_data->mdb->old_new.embedded + .location.start_block ) + + hfs_blocks_embedded_old; + i++ ) { + CLR_BLOC_OCCUPATION(hfs_priv_data->alloc_map, i); + } + /* and save it */ + if (!ped_geometry_write (fs->geom, hfs_priv_data->alloc_map, + PED_BE16_TO_CPU ( + hfs_priv_data->mdb->volume_bitmap_block ), + ( PED_BE16_TO_CPU ( + hfs_priv_data->mdb->total_blocks ) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8))) + return 0; + if (!ped_geometry_sync (fs->geom)) + return 0; + + /* search and update the bad blocks file */ + key.key_length = sizeof(key) - 1; + key.type = HFS_DATA_FORK; + key.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID); + key.start = 0; + if (!hfs_btree_search (hfs_priv_data->extent_file, + (HfsPrivateGenericKey*) &key, NULL, 0, &ref)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("An error occurred while looking for the mandatory " + "bad blocks file.")); + return 0; + } + if (!hfs_file_read_sector (hfs_priv_data->extent_file, node, + ref.node_number)) + return 0; + ret_key = (HfsExtentKey*) (node + ref.record_pos); + ret_data = (HfsExtDescriptor*) ( node + ref.record_pos + + sizeof (HfsExtentKey) ); + + while (ret_key->type == key.type && ret_key->file_ID == key.file_ID) { + for (i = 0; i < HFS_EXT_NB; i++) { + if ( ret_data[i].start_block + == hfs_priv_data->mdb->old_new + .embedded.location.start_block) { + ret_data[i].block_count = + hfs_priv_data->mdb->old_new + .embedded.location.block_count; + /* found ! : update */ + if (!hfs_file_write_sector ( + hfs_priv_data->extent_file, + node, ref.node_number) + || !ped_geometry_sync(fs->geom)) + return 0; + return 1; + } + } + + if (ref.record_number < PED_BE16_TO_CPU (node_desc->rec_nb)) { + ref.record_number++; + } else { + ref.node_number = PED_BE32_TO_CPU (node_desc->next); + if (!ref.node_number + || !hfs_file_read_sector(hfs_priv_data->extent_file, + node, ref.node_number)) + goto bb_not_found; + ref.record_number = 1; + } + + uint16_t value; + memcpy(&value, node+PED_SECTOR_SIZE_DEFAULT - (2*ref.record_number), sizeof(uint16_t)); + ref.record_pos = PED_BE16_TO_CPU(value); + ret_key = (HfsExtentKey*) (node + ref.record_pos); + ret_data = (HfsExtDescriptor*) (node + ref.record_pos + + sizeof (HfsExtentKey) ); + } + +bb_not_found: + /* not found : not a valid hfs+ wrapper : failure */ + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("It seems there is an error in the HFS wrapper: the bad " + "blocks file doesn't contain the embedded HFS+ volume.")); + return 0; +} + +int +hfsplus_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) +{ + HfsPPrivateFSData* priv_data; + PedTimer* timer_plus; + PedGeometry* embedded_geom; + PedSector hgms; + + /* check preconditions */ + PED_ASSERT (fs != NULL); + PED_ASSERT (fs->geom != NULL); + PED_ASSERT (geom != NULL); + PED_ASSERT (fs->geom->dev == geom->dev); +#ifdef DEBUG + PED_ASSERT ((hgms = hfsplus_get_min_size (fs)) != 0); +#else + if ((hgms = hfsplus_get_min_size (fs)) == 0) + return 0; +#endif + + if (ped_geometry_test_equal(fs->geom, geom)) + return 1; + + priv_data = (HfsPPrivateFSData*) fs->type_specific; + + if (fs->geom->start != geom->start + || geom->length > fs->geom->length + || geom->length < hgms) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Sorry, HFS+ cannot be resized that way yet.")); + return 0; + } + + if (priv_data->wrapper) { + PedSector red, hgee; + HfsPrivateFSData* hfs_priv_data = (HfsPrivateFSData*) + priv_data->wrapper->type_specific; + unsigned int hfs_sect_block = + PED_BE32_TO_CPU (hfs_priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT; + + /* There is a wrapper so we must calculate the new geometry + of the embedded HFS+ volume */ + red = ( (fs->geom->length - geom->length + hfs_sect_block - 1) + / hfs_sect_block ) * hfs_sect_block; + /* Can't we shrink the hfs+ volume by the desired size ? */ + hgee = hfsplus_get_empty_end (fs); + if (!hgee) return 0; + if (red > priv_data->plus_geom->length - hgee) { + /* No, shrink hfs+ by the greatest possible value */ + hgee = ((hgee + hfs_sect_block - 1) / hfs_sect_block) + * hfs_sect_block; + red = priv_data->plus_geom->length - hgee; + } + embedded_geom = ped_geometry_new (geom->dev, + priv_data->plus_geom->start, + priv_data->plus_geom->length + - red); + + /* There is a wrapper so the resize process is a two stages + process (embedded resizing then wrapper resizing) : + we create a sub timer */ + ped_timer_reset (timer); + ped_timer_set_state_name (timer, + _("shrinking embedded HFS+ volume")); + ped_timer_update(timer, 0.0); + timer_plus = ped_timer_new_nested (timer, 0.98); + } else { + /* No wrapper : the desired geometry is the desired + HFS+ volume geometry */ + embedded_geom = geom; + timer_plus = timer; + } + + /* Resize the HFS+ volume */ + if (!hfsplus_volume_resize (fs, embedded_geom, timer_plus)) { + if (timer_plus != timer) ped_timer_destroy_nested (timer_plus); + if (priv_data->wrapper) ped_geometry_destroy (embedded_geom); + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Resizing the HFS+ volume has failed.")); + return 0; + } + + if (priv_data->wrapper) { + ped_geometry_destroy (embedded_geom); + ped_timer_destroy_nested (timer_plus); + ped_timer_set_state_name(timer, _("shrinking HFS wrapper")); + timer_plus = ped_timer_new_nested (timer, 0.02); + /* There's a wrapper : second stage = resizing it */ + if (!hfsplus_wrapper_update (fs) + || !hfs_resize (priv_data->wrapper, geom, timer_plus)) { + ped_timer_destroy_nested (timer_plus); + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Updating the HFS wrapper has failed.")); + return 0; + } + ped_timer_destroy_nested (timer_plus); + } + ped_timer_update(timer, 1.0); + + return 1; +} + +#ifdef HFS_EXTRACT_FS +/* The following is for debugging purpose only, NOT for packaging */ + +#include <stdio.h> + +uint8_t* extract_buffer = NULL; + +static int +hfs_extract_file(const char* filename, HfsPrivateFile* hfs_file) +{ + FILE* fout; + PedSector sect; + + fout = fopen(filename, "w"); + if (!fout) return 0; + + for (sect = 0; sect < hfs_file->sect_nb; ++sect) { + if (!hfs_file_read_sector(hfs_file, extract_buffer, sect)) + goto err_close; + if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout)) + goto err_close; + } + + return (fclose(fout) == 0 ? 1 : 0); + +err_close: + fclose(fout); + return 0; +} + +static int +hfs_extract_bitmap(const char* filename, PedFileSystem* fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsMasterDirectoryBlock* mdb = priv_data->mdb; + unsigned int count; + FILE* fout; + PedSector sect; + + fout = fopen(filename, "w"); + if (!fout) return 0; + + for (sect = PED_BE16_TO_CPU(mdb->volume_bitmap_block); + sect < PED_BE16_TO_CPU(mdb->start_block); + sect += count) { + uint16_t st_block = PED_BE16_TO_CPU(mdb->start_block); + count = (st_block-sect) < BLOCK_MAX_BUFF ? + (st_block-sect) : BLOCK_MAX_BUFF; + if (!ped_geometry_read(fs->geom, extract_buffer, sect, count)) + goto err_close; + if (!fwrite (extract_buffer, count * PED_SECTOR_SIZE_DEFAULT, + 1, fout)) + goto err_close; + } + + return (fclose(fout) == 0 ? 1 : 0); + +err_close: + fclose(fout); + return 0; +} + +static int +hfs_extract_mdb (const char* filename, PedFileSystem* fs) +{ + FILE* fout; + + fout = fopen(filename, "w"); + if (!fout) return 0; + + if (!ped_geometry_read(fs->geom, extract_buffer, 2, 1)) + goto err_close; + if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout)) + goto err_close; + + return (fclose(fout) == 0 ? 1 : 0); + +err_close: + fclose(fout); + return 0; +} + +static int +hfs_extract (PedFileSystem* fs, PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This is not a real %s check. This is going to extract " + "special low level files for debugging purposes."), + "HFS"); + + extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT); + if (!extract_buffer) return 0; + + hfs_extract_mdb(HFS_MDB_FILENAME, fs); + hfs_extract_file(HFS_CATALOG_FILENAME, priv_data->catalog_file); + hfs_extract_file(HFS_EXTENTS_FILENAME, priv_data->extent_file); + hfs_extract_bitmap(HFS_BITMAP_FILENAME, fs); + + free(extract_buffer); extract_buffer = NULL; + return 0; /* nothing has been fixed by us ! */ +} + +static int +hfsplus_extract_file(const char* filename, HfsPPrivateFile* hfsp_file) +{ + FILE* fout; + unsigned int cp_sect; + PedSector rem_sect; + + fout = fopen(filename, "w"); + if (!fout) return 0; + + for (rem_sect = hfsp_file->sect_nb; rem_sect; rem_sect -= cp_sect) { + cp_sect = rem_sect < BLOCK_MAX_BUFF ? rem_sect : BLOCK_MAX_BUFF; + if (!hfsplus_file_read(hfsp_file, extract_buffer, + hfsp_file->sect_nb - rem_sect, cp_sect)) + goto err_close; + if (!fwrite (extract_buffer, cp_sect * PED_SECTOR_SIZE_DEFAULT, + 1, fout)) + goto err_close; + } + + return (fclose(fout) == 0 ? 1 : 0); + +err_close: + fclose(fout); + return 0; +} + +static int +hfsplus_extract_vh (const char* filename, PedFileSystem* fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + FILE* fout; + PedGeometry* geom = priv_data->plus_geom; + + + fout = fopen(filename, "w"); + if (!fout) return 0; + + if (!ped_geometry_read(geom, extract_buffer, 2, 1)) + goto err_close; + if (!fwrite(extract_buffer, PED_SECTOR_SIZE_DEFAULT, 1, fout)) + goto err_close; + + return (fclose(fout) == 0 ? 1 : 0); + +err_close: + fclose(fout); + return 0; +} + +/* TODO : use the timer to report what is happening */ +/* TODO : use exceptions to report errors */ +static int +hfsplus_extract (PedFileSystem* fs, PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + HfsPPrivateFile* startup_file; + + if (priv_data->wrapper) { + /* TODO : create nested timer */ + hfs_extract (priv_data->wrapper, timer); + } + + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This is not a real %s check. This is going to extract " + "special low level files for debugging purposes."), + "HFS+"); + + extract_buffer = ped_malloc(BLOCK_MAX_BUFF * PED_SECTOR_SIZE_DEFAULT); + if (!extract_buffer) return 0; + + hfsplus_extract_vh(HFSP_VH_FILENAME, fs); + hfsplus_extract_file(HFSP_CATALOG_FILENAME, priv_data->catalog_file); + hfsplus_extract_file(HFSP_EXTENTS_FILENAME, priv_data->extents_file); + hfsplus_extract_file(HFSP_ATTRIB_FILENAME, priv_data->attributes_file); + hfsplus_extract_file(HFSP_BITMAP_FILENAME, priv_data->allocation_file); + + startup_file = hfsplus_file_open(fs, PED_CPU_TO_BE32(HFSP_STARTUP_ID), + vh->startup_file.extents, + PED_BE64_TO_CPU ( + vh->startup_file.logical_size) + / PED_SECTOR_SIZE_DEFAULT); + if (startup_file) { + hfsplus_extract_file(HFSP_STARTUP_FILENAME, startup_file); + hfsplus_file_close(startup_file); startup_file = NULL; + } + + free(extract_buffer); extract_buffer = NULL; + return 0; /* nothing has been fixed by us ! */ +} +#endif /* HFS_EXTRACT_FS */ + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/hfs/hfs.h b/libparted/fs/r/hfs/hfs.h new file mode 100644 index 0000000..5b9138c --- /dev/null +++ b/libparted/fs/r/hfs/hfs.h @@ -0,0 +1,648 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2003-2005, 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/>. +*/ + +#ifndef _HFS_H +#define _HFS_H + +/* WARNING : bn is used 2 times in theses macro */ +/* so _never_ use side effect operators when using them */ +#define TST_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) & (1<<(7-((bn)&7)))) +#define SET_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) |= (1<<(7-((bn)&7)))) +#define CLR_BLOC_OCCUPATION(tab,bn) \ + (((tab)[(bn)/8]) &= ~(1<<(7-((bn)&7)))) + +/* Maximum number of blocks for the copy buffers */ +#define BLOCK_MAX_BUFF 256 +/* Maximum size of the copy buffers, in bytes */ +#define BYTES_MAX_BUFF 8388608 + +/* Apple Creator Codes follow */ +#define HFSP_IMPL_Shnk 0x53686e6b /* in use */ +#define HFSP_IMPL_Xpnd 0x58706e64 /* reserved */ +#define HFSP_IMPL_Resz 0x5265737a /* reserved */ +#define HFSP_IMPL_PHpx 0x50482b78 /* reserved */ +#define HFSP_IMPL_traP 0x74726150 /* reserved */ +#define HFSP_IMPL_GnuP 0x476e7550 /* reserved */ + +#define HFS_SIGNATURE 0x4244 /* 'BD' */ +#define HFSP_SIGNATURE 0x482B /* 'H+' */ +#define HFSX_SIGNATURE 0x4858 /* 'HX' */ + +#define HFSP_VERSION 4 +#define HFSX_VERSION 5 + +#define HFS_HARD_LOCK 7 +#define HFS_UNMOUNTED 8 +#define HFS_BAD_SPARED 9 +#define HFS_SOFT_LOCK 15 +#define HFSP_NO_CACHE 10 +#define HFSP_INCONSISTENT 11 +#define HFSP_REUSE_CNID 12 +#define HFSP_JOURNALED 13 + +#define HFS_IDX_NODE 0x00 +#define HFS_HDR_NODE 0x01 +#define HFS_MAP_NODE 0x02 +#define HFS_LEAF_NODE 0xFF + +#define HFS_FIRST_REC 0x0E +#define HFS_NSD_HD_REC 0x78 +#define HFS_MAP_REC 0xF8 + +#define HFS_DATA_FORK 0x00 +#define HFS_RES_FORK 0xFF + +#define HFS_CAT_DIR 0x01 +#define HFS_CAT_FILE 0x02 +#define HFS_CAT_DIR_TH 0x03 +#define HFS_CAT_FILE_TH 0x04 + +#define HFSP_ATTR_INLINE 0x10 +#define HFSP_ATTR_FORK 0x20 +#define HFSP_ATTR_EXTENTS 0x30 + +#define HFS_ROOT_PAR_ID 0x01 +#define HFS_ROOT_DIR_ID 0x02 +#define HFS_XTENT_ID 0x03 +#define HFS_CATALOG_ID 0x04 +#define HFS_BAD_BLOCK_ID 0x05 +#define HFSP_ALLOC_ID 0x06 +#define HFSP_STARTUP_ID 0x07 +#define HFSP_ATTRIB_ID 0x08 +#define HFSP_BOGUS_ID 0x0F +#define HFSP_FIRST_AV_ID 0x10 + +#define HFSJ_JOURN_IN_FS 0x00 +#define HFSJ_JOURN_OTHER_DEV 0x01 +#define HFSJ_JOURN_NEED_INIT 0x02 + +#define HFSJ_HEADER_MAGIC 0x4a4e4c78 +#define HFSJ_ENDIAN_MAGIC 0x12345678 + +#define HFSX_CASE_FOLDING 0xCF /* case insensitive HFSX */ +#define HFSX_BINARY_COMPARE 0xBC /* case sensitive HFSX */ + +#define HFS_EXT_NB 3 +#define HFSP_EXT_NB 8 + +/* Define the filenames used by the FS extractor */ +#ifdef HFS_EXTRACT_FS + +#define HFS_MDB_FILENAME "mdb.hfs" +#define HFS_CATALOG_FILENAME "catalog.hfs" +#define HFS_EXTENTS_FILENAME "extents.hfs" +#define HFS_BITMAP_FILENAME "bitmap.hfs" + +#define HFSP_VH_FILENAME "vh.hfsplus" +#define HFSP_CATALOG_FILENAME "catalog.hfsplus" +#define HFSP_EXTENTS_FILENAME "extents.hfsplus" +#define HFSP_BITMAP_FILENAME "bitmap.hfsplus" +#define HFSP_ATTRIB_FILENAME "attributes.hfsplus" +#define HFSP_STARTUP_FILENAME "startup.hfsplus" + +#endif /* HFS_EXTRACT_FS */ + + + +/* ----------------------------------- */ +/* -- HFS DATA STRUCTURES -- */ +/* ----------------------------------- */ + +/* Extent descriptor */ +struct __attribute__ ((packed)) _HfsExtDescriptor { + uint16_t start_block; + uint16_t block_count; +}; +typedef struct _HfsExtDescriptor HfsExtDescriptor; +typedef HfsExtDescriptor HfsExtDataRec[HFS_EXT_NB]; + +/* Volume header */ +struct __attribute__ ((packed)) _HfsMasterDirectoryBlock { + uint16_t signature; + uint32_t create_date; + uint32_t modify_date; + uint16_t volume_attributes; + uint16_t files_in_root; + uint16_t volume_bitmap_block; /* in sectors */ + uint16_t next_allocation; + uint16_t total_blocks; + uint32_t block_size; /* in bytes */ + uint32_t def_clump_size; /* in bytes */ + uint16_t start_block; /* in sectors */ + uint32_t next_free_node; + uint16_t free_blocks; + uint8_t name_length; + char name[27]; + uint32_t backup_date; + uint16_t backup_number; + uint32_t write_count; + uint32_t extents_clump; + uint32_t catalog_clump; + uint16_t dirs_in_root; + uint32_t file_count; + uint32_t dir_count; + uint32_t finder_info[8]; + union __attribute__ ((packed)) { + struct __attribute__ ((packed)) { + uint16_t volume_cache_size; /* in blocks */ + uint16_t bitmap_cache_size; /* in blocks */ + uint16_t common_cache_size; /* in blocks */ + } legacy; + struct __attribute__ ((packed)) { + uint16_t signature; + HfsExtDescriptor location; + } embedded; + } old_new; + uint32_t extents_file_size; /* in bytes, block size multiple */ + HfsExtDataRec extents_file_rec; + uint32_t catalog_file_size; /* in bytes, block size multiple */ + HfsExtDataRec catalog_file_rec; +}; +typedef struct _HfsMasterDirectoryBlock HfsMasterDirectoryBlock; + +/* B*-Tree Node Descriptor */ +struct __attribute__ ((packed)) _HfsNodeDescriptor { + uint32_t next; + uint32_t previous; + int8_t type; + uint8_t height; + uint16_t rec_nb; + uint16_t reserved; +}; +typedef struct _HfsNodeDescriptor HfsNodeDescriptor; + +/* Header record of a whole B*-Tree */ +struct __attribute__ ((packed)) _HfsHeaderRecord { + uint16_t depth; + uint32_t root_node; + uint32_t leaf_records; + uint32_t first_leaf_node; + uint32_t last_leaf_node; + uint16_t node_size; + uint16_t max_key_len; + uint32_t total_nodes; + uint32_t free_nodes; + int8_t reserved[76]; +}; +typedef struct _HfsHeaderRecord HfsHeaderRecord; + +/* Catalog key for B*-Tree lookup in the catalog file */ +struct __attribute__ ((packed)) _HfsCatalogKey { + uint8_t key_length; /* length of the key without key_length */ + uint8_t reserved; + uint32_t parent_ID; + uint8_t name_length; + char name[31]; /* in fact physicaly 1 upto 31 */ +}; +typedef struct _HfsCatalogKey HfsCatalogKey; + +/* Extents overflow key for B*-Tree lookup */ +struct __attribute__ ((packed)) _HfsExtentKey { + uint8_t key_length; /* length of the key without key_length */ + uint8_t type; /* data or ressource fork */ + uint32_t file_ID; + uint16_t start; +}; +typedef struct _HfsExtentKey HfsExtentKey; + +/* Catalog subdata case directory */ +struct __attribute__ ((packed)) _HfsDir { + uint16_t flags; + uint16_t valence; /* number of files in this directory */ + uint32_t dir_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + int8_t DInfo[16]; /* used by Finder, handle as reserved */ + int8_t DXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t reserved[4]; +}; +typedef struct _HfsDir HfsDir; + +/* Catalog subdata case file */ +struct __attribute__ ((packed)) _HfsFile { + int8_t flags; + int8_t type; /* should be 0 */ + int8_t FInfo[16]; /* used by Finder, handle as reserved */ + uint32_t file_ID; + uint16_t data_start_block; + uint32_t data_sz_byte; + uint32_t data_sz_block; + uint16_t res_start_block; + uint32_t res_sz_byte; + uint32_t res_sz_block; + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + int8_t FXInfo[16]; /* used by Finder, handle as reserved */ + uint16_t clump_size; + HfsExtDataRec extents_data; + HfsExtDataRec extents_res; + uint32_t reserved; +}; +typedef struct _HfsFile HfsFile; + +/* Catalog subdata case directory thread */ +struct __attribute__ ((packed)) _HfsDirTh { + uint32_t reserved[2]; + uint32_t parent_ID; + int8_t name_length; + char name[31]; +}; +typedef struct _HfsDirTh HfsDirTh; + +/* Catalog subdata case file thread */ +typedef struct _HfsDirTh HfsFileTh; /* same as directory thread */ + +/* Catalog data */ +struct __attribute__ ((packed)) _HfsCatalog { + int8_t type; + int8_t reserved; + union { + HfsDir dir; + HfsFile file; + HfsDirTh dir_th; + HfsFileTh file_th; + } sel; +}; +typedef struct _HfsCatalog HfsCatalog; + + + +/* ------------------------------------ */ +/* -- HFS+ DATA STRUCTURES -- */ +/* ------------------------------------ */ + +/* documented since 2004 in tn1150 */ +struct __attribute__ ((packed)) _HfsPPerms { + uint32_t owner_ID; + uint32_t group_ID; + uint32_t permissions; + uint32_t special_devices; +}; +typedef struct _HfsPPerms HfsPPerms; + +/* HFS+ extent descriptor*/ +struct __attribute__ ((packed)) _HfsPExtDescriptor { + uint32_t start_block; + uint32_t block_count; +}; +typedef struct _HfsPExtDescriptor HfsPExtDescriptor; +typedef HfsPExtDescriptor HfsPExtDataRec[HFSP_EXT_NB]; + +/* HFS+ fork data structure */ +struct __attribute__ ((packed)) _HfsPForkData { + uint64_t logical_size; + uint32_t clump_size; + uint32_t total_blocks; + HfsPExtDataRec extents; +}; +typedef struct _HfsPForkData HfsPForkData; + +/* HFS+ catalog node ID */ +typedef uint32_t HfsPNodeID; + +/* HFS+ file names */ +typedef uint16_t unichar; +struct __attribute__ ((packed)) _HfsPUniStr255 { + uint16_t length; + unichar unicode[255]; /* 1 upto 255 */ +}; +typedef struct _HfsPUniStr255 HfsPUniStr255; + +/* HFS+ volume header */ +struct __attribute__ ((packed)) _HfsPVolumeHeader { + uint16_t signature; + uint16_t version; + uint32_t attributes; + uint32_t last_mounted_version; + uint32_t journal_info_block; + + uint32_t create_date; + uint32_t modify_date; + uint32_t backup_date; + uint32_t checked_date; + + uint32_t file_count; + uint32_t dir_count; + + uint32_t block_size; + uint32_t total_blocks; + uint32_t free_blocks; + + uint32_t next_allocation; + uint32_t res_clump_size; + uint32_t data_clump_size; + HfsPNodeID next_catalog_ID; + + uint32_t write_count; + uint64_t encodings_bitmap; + + uint8_t finder_info[32]; + + HfsPForkData allocation_file; + HfsPForkData extents_file; + HfsPForkData catalog_file; + HfsPForkData attributes_file; + HfsPForkData startup_file; +}; +typedef struct _HfsPVolumeHeader HfsPVolumeHeader; + +/* HFS+ B-Tree Node Descriptor. Same as HFS btree. */ +struct __attribute__ ((packed)) _HfsPNodeDescriptor { + uint32_t next; + uint32_t previous; + int8_t type; + uint8_t height; + uint16_t rec_nb; + uint16_t reserved; +}; +typedef struct _HfsPNodeDescriptor HfsPNodeDescriptor; + +/* Header record of a whole HFS+ B-Tree. */ +struct __attribute__ ((packed)) _HfsPHeaderRecord { + uint16_t depth; + uint32_t root_node; + uint32_t leaf_records; + uint32_t first_leaf_node; + uint32_t last_leaf_node; + uint16_t node_size; + uint16_t max_key_len; + uint32_t total_nodes; + uint32_t free_nodes; /* same as hfs btree until here */ + uint16_t reserved1; + + uint32_t clump_size; + uint8_t btree_type; /* must be 0 for HFS+ B-Tree */ + uint8_t key_compare_type; /* hfsx => 0xCF = case folding */ + /* 0xBC = binary compare */ + /* otherwise, reserved */ + uint32_t attributes; + uint32_t reserved3[16]; +}; +typedef struct _HfsPHeaderRecord HfsPHeaderRecord; + +/* Catalog key for B-Tree lookup in the HFS+ catalog file */ +struct __attribute__ ((packed)) _HfsPCatalogKey { + uint16_t key_length; + HfsPNodeID parent_ID; + HfsPUniStr255 node_name; +}; +typedef struct _HfsPCatalogKey HfsPCatalogKey; + +/* HFS+ catalog subdata case dir */ +struct __attribute__ ((packed)) _HfsPDir { + uint16_t flags; + uint32_t valence; + HfsPNodeID dir_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t attrib_mod_date; + uint32_t access_date; + uint32_t backup_date; + HfsPPerms permissions; + int8_t DInfo[16]; /* used by Finder, handle as reserved */ + int8_t DXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t text_encoding; + uint32_t reserved; +}; +typedef struct _HfsPDir HfsPDir; + +/* HFS+ catalog subdata case file */ +struct __attribute__ ((packed)) _HfsPFile { + uint16_t flags; + uint32_t reserved1; + HfsPNodeID file_ID; + uint32_t create_date; + uint32_t modify_date; + uint32_t attrib_mod_date; + uint32_t access_date; + uint32_t backup_date; + HfsPPerms permissions; + int8_t FInfo[16]; /* used by Finder, handle as reserved */ + int8_t FXInfo[16]; /* used by Finder, handle as reserved */ + uint32_t text_encoding; + uint32_t reserved2; + + HfsPForkData data_fork; + HfsPForkData res_fork; +}; +typedef struct _HfsPFile HfsPFile; + +/* HFS+ catalog subdata case thread */ +struct __attribute__ ((packed)) _HfsPThread { + int16_t reserved; + HfsPNodeID parent_ID; + HfsPUniStr255 node_name; +}; +typedef struct _HfsPThread HfsPDirTh; +typedef struct _HfsPThread HfsPFileTh; + +/* HFS+ Catalog leaf data */ +struct __attribute__ ((packed)) _HfsPCatalog { + int16_t type; + union { + HfsPDir dir; + HfsPFile file; + HfsPDirTh dir_th; + HfsPFileTh file_th; + } sel; +}; +typedef struct _HfsPCatalog HfsPCatalog; + +/* HFS+ extents file key */ +struct __attribute__ ((packed)) _HfsPExtentKey { + uint16_t key_length; + uint8_t type; + uint8_t pad; + HfsPNodeID file_ID; + uint32_t start; +}; +typedef struct _HfsPExtentKey HfsPExtentKey; + +/* extent file data is HfsPExtDataRec */ + +/* Fork data attribute file */ +struct __attribute__ ((packed)) _HfsPForkDataAttr { + uint32_t record_type; + uint32_t reserved; + union __attribute__ ((packed)) { + HfsPForkData fork; + HfsPExtDataRec extents; + } fork_res; +}; +typedef struct _HfsPForkDataAttr HfsPForkDataAttr; + + +/* ----------- Journal data structures ----------- */ + +/* Info block : stored in a block # defined in the VH */ +struct __attribute__ ((packed)) _HfsJJournalInfoBlock { + uint32_t flags; + uint32_t device_signature[8]; + uint64_t offset; + uint64_t size; + uint32_t reserved[32]; +}; +typedef struct _HfsJJournalInfoBlock HfsJJournalInfoBlock; + +struct __attribute__ ((packed)) _HfsJJournalHeader { + uint32_t magic; + uint32_t endian; + uint64_t start; + uint64_t end; + uint64_t size; + uint32_t blhdr_size; + uint32_t checksum; + uint32_t jhdr_size; +}; +typedef struct _HfsJJournalHeader HfsJJournalHeader; + +struct __attribute__ ((packed)) _HfsJBlockInfo { + uint64_t bnum; /* sector number */ + uint32_t bsize; /* size in bytes */ + uint32_t next; +}; +typedef struct _HfsJBlockInfo HfsJBlockInfo; + +struct __attribute__ ((packed)) _HfsJBlockListHeader { + uint16_t max_blocks; /* reserved */ + uint16_t num_blocks; + uint32_t bytes_used; + uint32_t checksum; + uint32_t pad; + HfsJBlockInfo binfo[]; +}; +typedef struct _HfsJBlockListHeader HfsJBlockListHeader; + + + +/* ---------------------------------------- */ +/* -- INTERNAL DATA STRUCTURES -- */ +/* ---------------------------------------- */ + +/* Data of an opened HFS file */ +struct _HfsPrivateFile { + PedSector sect_nb; + PedFileSystem* fs; + uint32_t CNID; /* disk order (BE) */ + HfsExtDataRec first; /* disk order (BE) */ + HfsExtDataRec cache; /* disk order (BE) */ + uint16_t start_cache; /* CPU order */ +}; +typedef struct _HfsPrivateFile HfsPrivateFile; + +/* To store bad block list */ +struct _HfsPrivateLinkExtent { + HfsExtDescriptor extent; + struct _HfsPrivateLinkExtent* next; +}; +typedef struct _HfsPrivateLinkExtent HfsPrivateLinkExtent; + +/* HFS Filesystem specific data */ +struct _HfsPrivateFSData { + uint8_t alloc_map[(1<<16) / 8]; + HfsMasterDirectoryBlock* mdb; + HfsPrivateFile* extent_file; + HfsPrivateFile* catalog_file; + HfsPrivateLinkExtent* bad_blocks_xtent_list; + unsigned int bad_blocks_xtent_nb; + char bad_blocks_loaded; +}; +typedef struct _HfsPrivateFSData HfsPrivateFSData; + +/* Generic btree key */ +struct __attribute__ ((packed)) _HfsPrivateGenericKey { + uint8_t key_length; + uint8_t key_content[1]; /* we use 1 as a minimum size */ +}; +typedef struct _HfsPrivateGenericKey HfsPrivateGenericKey; + +/* ----- HFS+ ----- */ + +/* Data of an opened HFS file */ +struct _HfsPPrivateFile { + PedSector sect_nb; + PedFileSystem* fs; + HfsPNodeID CNID; /* disk order (BE) */ + HfsPExtDataRec first; /* disk order (BE) */ + HfsPExtDataRec cache; /* disk order (BE) */ + uint32_t start_cache; /* CPU order */ +}; +typedef struct _HfsPPrivateFile HfsPPrivateFile; + +struct _HfsPPrivateExtent { + PedSector start_sector; + PedSector sector_count; +}; +typedef struct _HfsPPrivateExtent HfsPPrivateExtent; + +/* To store bad block list */ +struct _HfsPPrivateLinkExtent { + HfsPExtDescriptor extent; + struct _HfsPPrivateLinkExtent* next; +}; +typedef struct _HfsPPrivateLinkExtent HfsPPrivateLinkExtent; + +/* HFS+ file system specific data */ +struct _HfsPPrivateFSData { + PedFileSystem* wrapper; /* NULL if hfs+ is not embedded */ + PedGeometry* plus_geom; /* Geometry of HFS+ _volume_ */ + uint8_t* alloc_map; + uint8_t* dirty_alloc_map; + HfsPVolumeHeader* vh; + HfsPPrivateFile* extents_file; + HfsPPrivateFile* catalog_file; + HfsPPrivateFile* attributes_file; + HfsPPrivateFile* allocation_file; + HfsPPrivateLinkExtent* bad_blocks_xtent_list; + uint32_t jib_start_block; + uint32_t jl_start_block; + unsigned int bad_blocks_xtent_nb; + char bad_blocks_loaded; + char free_geom; /* 1 = plus_geom must be freed */ +}; +typedef struct _HfsPPrivateFSData HfsPPrivateFSData; + +/* Generic + btree key */ +struct __attribute__ ((packed)) _HfsPPrivateGenericKey { + uint16_t key_length; + uint8_t key_content[1]; /* we use 1 as a minimum size */ +}; +typedef struct _HfsPPrivateGenericKey HfsPPrivateGenericKey; + +/* ---- common ---- */ + +/* node and lead record reference for a BTree search */ +struct _HfsCPrivateLeafRec { + unsigned int node_size; /* in sectors */ + unsigned int node_number; + unsigned int record_pos; + unsigned int record_number; +}; +typedef struct _HfsCPrivateLeafRec HfsCPrivateLeafRec; + +extern uint8_t* hfs_block; +extern uint8_t* hfsp_block; +extern unsigned hfs_block_count; +extern unsigned hfsp_block_count; + +#endif /* _HFS_H */ diff --git a/libparted/fs/r/hfs/journal.c b/libparted/fs/r/hfs/journal.c new file mode 100644 index 0000000..7a2a8dc --- /dev/null +++ b/libparted/fs/r/hfs/journal.c @@ -0,0 +1,392 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 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/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "reloc_plus.h" + +#include "journal.h" + +static int hfsj_vh_replayed = 0; +static int is_le = 0; + +static uint32_t +hfsj_calc_checksum(uint8_t *ptr, int len) +{ + int i; + uint32_t cksum=0; + + for (i=0; i < len; i++, ptr++) { + cksum = (cksum << 8) ^ (cksum + *ptr); + } + + return (~cksum); +} + +int +hfsj_update_jib(PedFileSystem* fs, uint32_t block) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + + priv_data->vh->journal_info_block = PED_CPU_TO_BE32(block); + + if (!hfsplus_update_vh (fs)) + return 0; + + priv_data->jib_start_block = block; + return 1; +} + +int +hfsj_update_jl(PedFileSystem* fs, uint32_t block) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedSector sector; + uint64_t offset; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsJJournalInfoBlock* jib; + int binsect; + + binsect = HFS_32_TO_CPU(priv_data->vh->block_size, is_le) / PED_SECTOR_SIZE_DEFAULT; + sector = (PedSector) priv_data->jib_start_block * binsect; + if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1)) + return 0; + jib = (HfsJJournalInfoBlock*) buf; + + offset = (uint64_t)block * PED_SECTOR_SIZE_DEFAULT * binsect; + jib->offset = HFS_CPU_TO_64(offset, is_le); + + if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1) + || !ped_geometry_sync(priv_data->plus_geom)) + return 0; + + priv_data->jl_start_block = block; + return 1; +} + +/* Return the sector in the journal that is after the area read */ +/* or 0 on error */ +static PedSector +hfsj_journal_read(PedGeometry* geom, HfsJJournalHeader* jh, + PedSector journ_sect, PedSector journ_length, + PedSector read_sect, unsigned int nb_sect, + void* buf) +{ + int r; + + while (nb_sect--) { + r = ped_geometry_read(geom, buf, journ_sect + read_sect, 1); + if (!r) return 0; + + buf = ((uint8_t*)buf) + PED_SECTOR_SIZE_DEFAULT; + read_sect++; + if (read_sect == journ_length) + read_sect = 1; /* skip journal header + which is asserted to be + 1 sector long */ + } + + return read_sect; +} + +static int +hfsj_replay_transaction(PedFileSystem* fs, HfsJJournalHeader* jh, + PedSector jsector, PedSector jlength) +{ + PedSector start, sector; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsJBlockListHeader* blhdr; + uint8_t* block; + unsigned int blhdr_nbsect; + int i, r; + uint32_t cksum, size; + + blhdr_nbsect = HFS_32_TO_CPU(jh->blhdr_size, is_le) / PED_SECTOR_SIZE_DEFAULT; + blhdr = (HfsJBlockListHeader*) + ped_malloc (blhdr_nbsect * PED_SECTOR_SIZE_DEFAULT); + if (!blhdr) return 0; + + start = HFS_64_TO_CPU(jh->start, is_le) / PED_SECTOR_SIZE_DEFAULT; + do { + start = hfsj_journal_read(priv_data->plus_geom, jh, jsector, + jlength, start, blhdr_nbsect, blhdr); + if (!start) goto err_replay; + + cksum = HFS_32_TO_CPU(blhdr->checksum, is_le); + blhdr->checksum = 0; + if (cksum!=hfsj_calc_checksum((uint8_t*)blhdr, sizeof(*blhdr))){ + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad block list header checksum.")); + goto err_replay; + } + blhdr->checksum = HFS_CPU_TO_32(cksum, is_le); + + for (i=1; i < HFS_16_TO_CPU(blhdr->num_blocks, is_le); ++i) { + size = HFS_32_TO_CPU(blhdr->binfo[i].bsize, is_le); + sector = HFS_64_TO_CPU(blhdr->binfo[i].bnum, is_le); + if (!size) continue; + if (size % PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Invalid size of a transaction " + "block while replaying the journal " + "(%i bytes)."), + size); + goto err_replay; + } + block = (uint8_t*) ped_malloc(size); + if (!block) goto err_replay; + start = hfsj_journal_read(priv_data->plus_geom, jh, + jsector, jlength, start, + size / PED_SECTOR_SIZE_DEFAULT, + block); + if (!start) { + free (block); + goto err_replay; + } + /* the sector stored in the journal seems to be + relative to the begin of the block device which + contains the hfs+ journaled volume */ + if (sector != ~0LL) + r = ped_geometry_write (fs->geom, block, sector, + size / PED_SECTOR_SIZE_DEFAULT); + else + r = 1; + free (block); + /* check if wrapper mdb or vh with no wrapper has + changed */ + if ( (sector != ~0LL) + && (2 >= sector) + && (2 < sector + size / PED_SECTOR_SIZE_DEFAULT) ) + hfsj_vh_replayed = 1; + /* check if vh of embedded hfs+ has changed */ + if ( (sector != ~0LL) + && (priv_data->plus_geom != fs->geom) + && (sector + + fs->geom->start + - priv_data->plus_geom->start <= 2) + && (sector + + size / PED_SECTOR_SIZE_DEFAULT + + fs->geom->start + - priv_data->plus_geom->start > 2) ) + hfsj_vh_replayed = 1; + if (!r) goto err_replay; + } + } while (blhdr->binfo[0].next); + + jh->start = HFS_CPU_TO_64(start * PED_SECTOR_SIZE_DEFAULT, is_le); + + free (blhdr); + return (ped_geometry_sync (fs->geom)); + +err_replay: + free (blhdr); + return 0; +} + +/* 0 => Failure, don't continue to open ! */ +/* 1 => Success, the journal has been completly replayed, or don't need to */ +int +hfsj_replay_journal(PedFileSystem* fs) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + PedSector sector, length; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsJJournalInfoBlock* jib; + HfsJJournalHeader* jh; + int binsect; + uint32_t cksum; + + binsect = PED_BE32_TO_CPU(priv_data->vh->block_size) / PED_SECTOR_SIZE_DEFAULT; + priv_data->jib_start_block = + PED_BE32_TO_CPU(priv_data->vh->journal_info_block); + sector = (PedSector) priv_data->jib_start_block * binsect; + if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1)) + return 0; + jib = (HfsJJournalInfoBlock*) buf; + + if ( (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS)) + && !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) { + priv_data->jl_start_block = HFS_64_TO_CPU(jib->offset, is_le) + / ( PED_SECTOR_SIZE_DEFAULT * binsect ); + } + + if (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_NEED_INIT)) + return 1; + + if ( !(jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_IN_FS)) + || (jib->flags & PED_CPU_TO_BE32(1 << HFSJ_JOURN_OTHER_DEV)) ) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Journal stored outside of the volume are " + "not supported. Try to deactivate the " + "journal and run Parted again.")); + return 0; + } + + if ( (PED_BE64_TO_CPU(jib->offset) % PED_SECTOR_SIZE_DEFAULT) + || (PED_BE64_TO_CPU(jib->size) % PED_SECTOR_SIZE_DEFAULT) ) { + ped_exception_throw ( + PED_EXCEPTION_NO_FEATURE, + PED_EXCEPTION_CANCEL, + _("Journal offset or size is not multiple of " + "the sector size.")); + return 0; + } + + sector = PED_BE64_TO_CPU(jib->offset) / PED_SECTOR_SIZE_DEFAULT; + length = PED_BE64_TO_CPU(jib->size) / PED_SECTOR_SIZE_DEFAULT; + + jib = NULL; + if (!ped_geometry_read(priv_data->plus_geom, buf, sector, 1)) + return 0; + jh = (HfsJJournalHeader*) buf; + + if (jh->endian == PED_LE32_TO_CPU(HFSJ_ENDIAN_MAGIC)) + is_le = 1; + + if ( (jh->magic != HFS_32_TO_CPU(HFSJ_HEADER_MAGIC, is_le)) + || (jh->endian != HFS_32_TO_CPU(HFSJ_ENDIAN_MAGIC, is_le)) ) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Incorrect magic values in the journal header.")); + return 0; + } + + if ( (HFS_64_TO_CPU(jh->size, is_le)%PED_SECTOR_SIZE_DEFAULT) + || (HFS_64_TO_CPU(jh->size, is_le)/PED_SECTOR_SIZE_DEFAULT + != (uint64_t)length) ) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Journal size mismatch between journal info block " + "and journal header.")); + return 0; + } + + if ( (HFS_64_TO_CPU(jh->start, is_le) % PED_SECTOR_SIZE_DEFAULT) + || (HFS_64_TO_CPU(jh->end, is_le) % PED_SECTOR_SIZE_DEFAULT) + || (HFS_32_TO_CPU(jh->blhdr_size, is_le) % PED_SECTOR_SIZE_DEFAULT) + || (HFS_32_TO_CPU(jh->jhdr_size, is_le) % PED_SECTOR_SIZE_DEFAULT) ) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Some header fields are not multiple of the sector " + "size.")); + return 0; + } + + if (HFS_32_TO_CPU(jh->jhdr_size, is_le) != PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The sector size stored in the journal is not 512 " + "bytes. Parted only supports 512 bytes length " + "sectors.")); + return 0; + } + + cksum = HFS_32_TO_CPU(jh->checksum, is_le); + jh->checksum = 0; + if (cksum != hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad journal checksum.")); + return 0; + } + jh->checksum = HFS_CPU_TO_32(cksum, is_le); + + /* https://github.com/apple-opensource/hfs/blob/master/core/hfs_journal.c#L1167 + * indicates that this is: + * wrap the start ptr if it points to the very end of the journal + */ + if (jh->start == jh->size) + jh->start = HFS_CPU_TO_64(PED_SECTOR_SIZE_DEFAULT, is_le); + if (jh->end == jh->size) + jh->end = HFS_CPU_TO_64(PED_SECTOR_SIZE_DEFAULT, is_le); + + if (jh->start == jh->end) + return 1; + + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL, + _("The journal is not empty. Parted must replay the " + "transactions before opening the file system. This will " + "modify the file system.")) + != PED_EXCEPTION_FIX) + return 0; + + while (jh->start != jh->end) { + /* Replay one complete transaction */ + if (!hfsj_replay_transaction(fs, jh, sector, length)) + return 0; + + /* Recalculate cksum of the journal header */ + jh->checksum = 0; /* need to be 0 while calculating the cksum */ + cksum = hfsj_calc_checksum((uint8_t*)jh, sizeof(*jh)); + jh->checksum = HFS_CPU_TO_32(cksum, is_le); + + /* Update the Journal Header */ + if (!ped_geometry_write(priv_data->plus_geom, buf, sector, 1) + || !ped_geometry_sync(priv_data->plus_geom)) + return 0; + } + + if (hfsj_vh_replayed) { + /* probe could have reported incorrect info ! */ + /* is there a way to ask parted to quit ? */ + ped_exception_throw( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_OK, + _("The volume header or the master directory block has " + "changed while replaying the journal. You should " + "restart Parted.")); + return 0; + } + + return 1; +} + +#endif /* DISCOVER_ONLY */ diff --git a/libparted/fs/r/hfs/journal.h b/libparted/fs/r/hfs/journal.h new file mode 100644 index 0000000..66eb2b1 --- /dev/null +++ b/libparted/fs/r/hfs/journal.h @@ -0,0 +1,45 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 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/>. +*/ + +#ifndef _JOURNAL_H +#define _JOURNAL_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfsj_replay_journal(PedFileSystem* fs); + +int +hfsj_update_jib(PedFileSystem* fs, uint32_t block); + +int +hfsj_update_jl(PedFileSystem* fs, uint32_t block); + +#define HFS_16_TO_CPU(x, is_little_endian) ((is_little_endian) ? (uint16_t)PED_LE16_TO_CPU(x) : (uint16_t)PED_BE16_TO_CPU(x)) +#define HFS_32_TO_CPU(x, is_little_endian) ((is_little_endian) ? (uint32_t)PED_LE32_TO_CPU(x) : (uint32_t)PED_BE32_TO_CPU(x)) +#define HFS_64_TO_CPU(x, is_little_endian) ((is_little_endian) ? (uint64_t)PED_LE64_TO_CPU(x) : (uint64_t)PED_BE64_TO_CPU(x)) +#define HFS_CPU_TO_16(x, is_little_endian) ((is_little_endian) ? (uint16_t)PED_CPU_TO_LE16(x) : (uint16_t)PED_CPU_TO_BE16(x)) +#define HFS_CPU_TO_32(x, is_little_endian) ((is_little_endian) ? (uint32_t)PED_CPU_TO_LE32(x) : (uint32_t)PED_CPU_TO_BE32(x)) +#define HFS_CPU_TO_64(x, is_little_endian) ((is_little_endian) ? (uint64_t)PED_CPU_TO_LE64(x) : (uint64_t)PED_CPU_TO_BE64(x)) + +#endif /* _JOURNAL_H */ diff --git a/libparted/fs/r/hfs/probe.c b/libparted/fs/r/hfs/probe.c new file mode 100644 index 0000000..ee4ae31 --- /dev/null +++ b/libparted/fs/r/hfs/probe.c @@ -0,0 +1,99 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 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 <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" + +#include "probe.h" + +int +hfsc_can_use_geom (PedGeometry* geom) +{ + PedDevice* dev; + + dev = geom->dev; + PED_ASSERT (geom != NULL); + PED_ASSERT (dev != NULL); + + if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Parted can't use HFS file systems on disks " + "with a sector size not equal to %d bytes."), + (int)PED_SECTOR_SIZE_DEFAULT ); + return 0; + } + + return 1; +} + +/* Probe an HFS volume, detecting it even if +it is in fact a wrapper to an HFS+ volume */ +/* Used by hfsplus_probe and hfs_probe */ +PedGeometry* +hfs_and_wrapper_probe (PedGeometry* geom) +{ + uint8_t buf[PED_SECTOR_SIZE_DEFAULT]; + HfsMasterDirectoryBlock *mdb; + PedGeometry* geom_ret; + PedSector search, max; + + PED_ASSERT (geom != NULL); + PED_ASSERT (hfsc_can_use_geom (geom)); + + mdb = (HfsMasterDirectoryBlock *) buf; + + /* is 5 an intelligent value ? */ + if ((geom->length < 5) + || (!ped_geometry_read (geom, buf, 2, 1)) + || (mdb->signature != PED_CPU_TO_BE16 (HFS_SIGNATURE)) ) + return NULL; + + search = ((PedSector) PED_BE16_TO_CPU (mdb->start_block) + + ((PedSector) PED_BE16_TO_CPU (mdb->total_blocks) + * (PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE_DEFAULT ))); + max = search + (PED_BE32_TO_CPU (mdb->block_size) / PED_SECTOR_SIZE_DEFAULT); + if (!(geom_ret = ped_geometry_new (geom->dev, geom->start, search + 2))) + return NULL; + + for (; search < max; search++) { + if (!ped_geometry_set (geom_ret, geom_ret->start, search + 2) + || !ped_geometry_read (geom_ret, buf, search, 1)) + break; + if (mdb->signature == PED_CPU_TO_BE16 (HFS_SIGNATURE)) + return geom_ret; + } + + ped_geometry_destroy (geom_ret); + return NULL; +} diff --git a/libparted/fs/r/hfs/probe.h b/libparted/fs/r/hfs/probe.h new file mode 100644 index 0000000..21be916 --- /dev/null +++ b/libparted/fs/r/hfs/probe.h @@ -0,0 +1,35 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 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/>. +*/ + +#ifndef _PROBE_H +#define _PROBE_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfsc_can_use_geom (PedGeometry* geom); + +PedGeometry* +hfs_and_wrapper_probe (PedGeometry* geom); + +#endif /* _PROBE_H */ diff --git a/libparted/fs/r/hfs/reloc.c b/libparted/fs/r/hfs/reloc.c new file mode 100644 index 0000000..05ec76a --- /dev/null +++ b/libparted/fs/r/hfs/reloc.c @@ -0,0 +1,676 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 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/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "file.h" +#include "advfs.h" +#include "cache.h" + +#include "reloc.h" + +/* This function moves data of size blocks starting + at block *ptr_fblock to block *ptr_to_fblock */ +/* return new start or -1 on failure */ +static int +hfs_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock, unsigned int size) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + unsigned int i, ok = 0; + unsigned int next_to_fblock; + unsigned int start, stop; + + PED_ASSERT (hfs_block != NULL); + PED_ASSERT (*ptr_to_fblock <= *ptr_fblock); + /* quiet gcc */ + start = stop = 0; + +/* + Try to fit the extent AT or _BEFORE_ the wanted place, + or then in the gap between dest and source. + If failed try to fit the extent after source, for 2 pass relocation + The extent is always copied in a non overlapping way +*/ + + /* Backward search */ + /* 1 pass relocation AT or BEFORE *ptr_to_fblock */ + if (*ptr_to_fblock != *ptr_fblock) { + start = stop = *ptr_fblock < *ptr_to_fblock+size ? + *ptr_fblock : *ptr_to_fblock+size; + while (start && stop-start != size) { + --start; + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start)) + stop = start; + } + ok = (stop-start == size); + } + + /* Forward search */ + /* 1 pass relocation in the gap merged with 2 pass reloc after source */ + if (!ok && *ptr_to_fblock != *ptr_fblock) { + start = stop = *ptr_to_fblock+1; + while (stop < PED_BE16_TO_CPU(priv_data->mdb->total_blocks) + && stop-start != size) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop)) + start = stop + 1; + ++stop; + } + ok = (stop-start == size); + } + + /* new non overlapping room has been found ? */ + if (ok) { + /* enough room */ + unsigned int j; + unsigned int start_block = + PED_BE16_TO_CPU (priv_data->mdb->start_block ); + unsigned int block_sz = + (PED_BE32_TO_CPU (priv_data->mdb->block_size) + / PED_SECTOR_SIZE_DEFAULT); + + if (stop > *ptr_to_fblock && stop <= *ptr_fblock) + /* Fit in the gap */ + next_to_fblock = stop; + else + /* Before or after the gap */ + next_to_fblock = *ptr_to_fblock; + + /* move blocks */ + for (i = 0; i < size; /*i+=j*/) { + PedSector abs_sector; + unsigned int ai; + + j = size - i; j = (j < hfs_block_count) ? + j : hfs_block_count ; + + abs_sector = start_block + + (PedSector) (*ptr_fblock + i) * block_sz; + if (!ped_geometry_read (fs->geom, hfs_block, abs_sector, + block_sz * j)) + return -1; + + abs_sector = start_block + + (PedSector) (start + i) * block_sz; + if (!ped_geometry_write (fs->geom,hfs_block,abs_sector, + block_sz * j)) + return -1; + + for (ai = i+j; i < ai; i++) { + /* free source block */ + CLR_BLOC_OCCUPATION(priv_data->alloc_map, + *ptr_fblock + i); + + /* set dest block */ + SET_BLOC_OCCUPATION(priv_data->alloc_map, + start + i); + } + } + if (!ped_geometry_sync_fast (fs->geom)) + return -1; + + *ptr_fblock += size; + *ptr_to_fblock = next_to_fblock; + } else { + if (*ptr_fblock != *ptr_to_fblock) + /* not enough room, but try to continue */ + ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("An extent has not been relocated.")); + start = *ptr_fblock; + *ptr_fblock = *ptr_to_fblock = start + size; + } + + return start; +} + +/* Update MDB */ +/* Return 0 if an error occurred */ +/* Return 1 if everything ok */ +int +hfs_update_mdb (PedFileSystem *fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + + if (!ped_geometry_read (fs->geom, node, 2, 1)) + return 0; + memcpy (node, priv_data->mdb, sizeof (HfsMasterDirectoryBlock)); + if ( !ped_geometry_write (fs->geom, node, 2, 1) + || !ped_geometry_write (fs->geom, node, fs->geom->length - 2, 1) + || !ped_geometry_sync_fast (fs->geom)) + return 0; + return 1; +} + +/* Generic relocator */ +/* replace previous hfs_do_move_* */ +static int +hfs_do_move (PedFileSystem* fs, unsigned int *ptr_src, + unsigned int *ptr_dest, HfsCPrivateCache* cache, + HfsCPrivateExtent* ref) +{ + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsPrivateFile* file; + HfsExtDescriptor* extent; + HfsCPrivateExtent* move; + int new_start; + + new_start = hfs_effect_move_extent (fs, ptr_src, ptr_dest, + ref->ext_length); + if (new_start == -1) return -1; + + if (ref->ext_start != (unsigned) new_start) { + /* Load, modify & save */ + switch (ref->where) { + /******** MDB *********/ + case CR_PRIM_CAT : + priv_data->catalog_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + goto CR_PRIM; + case CR_PRIM_EXT : + priv_data->extent_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + CR_PRIM : + extent = ( HfsExtDescriptor* ) + ( (uint8_t*)priv_data->mdb + ref->ref_offset ); + extent[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + if (!hfs_update_mdb(fs)) return -1; + break; + + /********* BTREE *******/ + case CR_BTREE_EXT_CAT : + if (priv_data->catalog_file + ->cache[ref->ref_index].start_block + == PED_CPU_TO_BE16(ref->ext_start)) + priv_data->catalog_file + ->cache[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + /* FALLTHROUGH */ + case CR_BTREE_EXT_0 : + file = priv_data->extent_file; + goto CR_BTREE; + case CR_BTREE_CAT : + file = priv_data->catalog_file; + CR_BTREE: + PED_ASSERT(ref->sect_by_block == 1 + && ref->ref_offset < PED_SECTOR_SIZE_DEFAULT); + if (!hfs_file_read_sector(file, node, ref->ref_block)) + return -1; + extent = ( HfsExtDescriptor* ) (node + ref->ref_offset); + extent[ref->ref_index].start_block = + PED_CPU_TO_BE16(new_start); + if (!hfs_file_write_sector(file, node, ref->ref_block) + || !ped_geometry_sync_fast (fs->geom)) + return -1; + break; + + /********** BUG ********/ + default : + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("A reference to an extent comes from a place " + "it should not. You should check the file " + "system!")); + return -1; + break; + } + + /* Update the cache */ + move = hfsc_cache_move_extent(cache, ref->ext_start, new_start); + if (!move) return -1; /* "cleanly" fail */ + PED_ASSERT(move == ref); /* generate a bug */ + } + + return new_start; +} + +/* 0 error, 1 ok */ +static int +hfs_save_allocation(PedFileSystem* fs) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + unsigned int map_sectors; + + map_sectors = ( PED_BE16_TO_CPU (priv_data->mdb->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) + / (PED_SECTOR_SIZE_DEFAULT * 8); + return ( ped_geometry_write (fs->geom, priv_data->alloc_map, + PED_BE16_TO_CPU (priv_data->mdb->volume_bitmap_block), + map_sectors) ); +} + +/* This function moves an extent starting at block fblock to block to_fblock + if there's enough room */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occurred */ +/* Return 0 if no extent was found */ +/* Generic search thanks to the file system cache */ +static int +hfs_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock, + HfsCPrivateCache* cache) +{ + HfsCPrivateExtent* ref; + unsigned int old_start, new_start; + + /* Reference search powered by the cache... */ + /* This is the optimisation secret :) */ + ref = hfsc_cache_search_extent(cache, *ptr_fblock); + if (!ref) return 0; /* not found */ + + old_start = *ptr_fblock; + new_start = hfs_do_move(fs, ptr_fblock, ptr_to_fblock, cache, ref); + if (new_start == (unsigned int) -1) return -1; + if (new_start > old_start) { /* detect 2 pass reloc */ + new_start = hfs_do_move(fs,&new_start,ptr_to_fblock,cache,ref); + if (new_start == (unsigned int) -1 || new_start > old_start) + return -1; + } + + /* allocation bitmap save is not atomic with data relocation */ + /* so we only do it a few times, and without syncing */ + /* The unmounted bit protect us anyway */ + hfs_save_allocation(fs); + return 1; +} + +static int +hfs_cache_from_mdb(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsExtDescriptor* extent; + unsigned int j; + + extent = priv_data->mdb->extents_file_rec; + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + 0, /* unused for mdb */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->mdb), + 1, /* load/save only 1 sector */ + CR_PRIM_EXT, + j ) + ) + return 0; + } + + extent = priv_data->mdb->catalog_file_rec; + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + 0, + ((uint8_t*)extent) - ((uint8_t*)priv_data->mdb), + 1, + CR_PRIM_CAT, + j ) + ) + return 0; + } + + return 1; +} + +static int +hfs_cache_from_catalog(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsHeaderRecord* header; + HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node; + HfsCatalogKey* catalog_key; + HfsCatalog* catalog_data; + HfsExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j; + uint16_t catalog_pos; + + if (!priv_data->catalog_file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This HFS volume has no catalog file. " + "This is very unusual!")); + return 1; + } + + if (!hfs_file_read_sector (priv_data->catalog_file, node, 0)) + return 0; + uint16_t offset; + memcpy(&offset, node+(PED_SECTOR_SIZE_DEFAULT-2), sizeof(uint16_t)); + header = (HfsHeaderRecord*) (node + PED_BE16_TO_CPU(offset)); + + for (leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + leaf_node; + leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfs_file_read_sector (priv_data->catalog_file, + node, leaf_node)) + return 0; + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; ++i) { + /* undocumented alignement */ + uint16_t value; + memcpy(&value, node+(PED_SECTOR_SIZE_DEFAULT - (2*i)), sizeof(uint16_t)); + catalog_pos = PED_BE16_TO_CPU(value); + catalog_key = (HfsCatalogKey*) (node + catalog_pos); + unsigned int skip; + skip = (1 + catalog_key->key_length + 1) & ~1; + catalog_data = (HfsCatalog*)(node+catalog_pos+skip); + /* check for obvious error in FS */ + if ((catalog_pos < HFS_FIRST_REC) + || ((uint8_t*)catalog_data - node + >= PED_SECTOR_SIZE_DEFAULT + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + return 0; + } + + if (catalog_data->type != HFS_CAT_FILE) continue; + + extent = catalog_data->sel.file.extents_data; + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + 1, /* hfs => btree block = 512 b */ + CR_BTREE_CAT, + j ) + ) + return 0; + } + + extent = catalog_data->sel.file.extents_res; + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + 1, /* hfs => btree block = 512 b */ + CR_BTREE_CAT, + j ) + ) + return 0; + } + } + } + + return 1; +} + +static int +hfs_cache_from_extent(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + HfsHeaderRecord* header; + HfsNodeDescriptor* desc = (HfsNodeDescriptor*) node; + HfsExtentKey* extent_key; + HfsExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j; + uint16_t extent_pos; + + if (!priv_data->extent_file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This HFS volume has no extents overflow " + "file. This is quite unusual!")); + return 1; + } + + if (!hfs_file_read_sector (priv_data->extent_file, node, 0)) + return 0; + uint16_t offset; + memcpy(&offset, node+(PED_SECTOR_SIZE_DEFAULT-2), sizeof(uint16_t)); + header = (HfsHeaderRecord*) (node + PED_BE16_TO_CPU(offset)); + + for (leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + leaf_node; + leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfs_file_read_sector (priv_data->extent_file, node, + leaf_node)) + return 0; + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + uint8_t where; + uint16_t value; + memcpy(&value, node+(PED_SECTOR_SIZE_DEFAULT - (2*i)), sizeof(uint16_t)); + extent_pos = PED_BE16_TO_CPU(value); + extent_key = (HfsExtentKey*)(node + extent_pos); + /* size is cst */ + extent = (HfsExtDescriptor*)(node+extent_pos+sizeof(HfsExtentKey)); + /* check for obvious error in FS */ + if ((extent_pos < HFS_FIRST_REC) + || ((uint8_t*)extent - node + >= PED_SECTOR_SIZE_DEFAULT + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + return 0; + } + + switch (extent_key->file_ID) { + case PED_CPU_TO_BE32 (HFS_XTENT_ID) : + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The extents overflow file should not" + " contain its own extents! You " + "should check the file system.")) + != PED_EXCEPTION_IGNORE) + return 0; + where = CR_BTREE_EXT_EXT; + break; + case PED_CPU_TO_BE32 (HFS_CATALOG_ID) : + where = CR_BTREE_EXT_CAT; + break; + default : + where = CR_BTREE_EXT_0; + break; + } + + for (j = 0; j < HFS_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE16_TO_CPU(extent[j].start_block), + PED_BE16_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + 1, /* hfs => btree block = 512 b */ + where, + j ) + ) + return 0; + } + } + } + + return 1; +} + +/* This function cache every extents start and length stored in any + fs structure into the adt defined in cache.[ch] + Returns NULL on failure */ +static HfsCPrivateCache* +hfs_cache_extents(PedFileSystem *fs, PedTimer* timer) +{ + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsCPrivateCache* ret; + unsigned int file_number, block_number; + + file_number = PED_BE32_TO_CPU(priv_data->mdb->file_count); + block_number = PED_BE16_TO_CPU(priv_data->mdb->total_blocks); + ret = hfsc_new_cache(block_number, file_number); + if (!ret) return NULL; + + if (!hfs_cache_from_mdb(ret, fs, timer) || + !hfs_cache_from_catalog(ret, fs, timer) || + !hfs_cache_from_extent(ret, fs, timer)) { + ped_exception_throw( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not cache the file system in memory.")); + hfsc_delete_cache(ret); + return NULL; + } + + return ret; +} + +/* This function moves file's data to compact used and free space, + starting at fblock block */ +/* return 0 on error */ +int +hfs_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, + PedTimer* timer, unsigned int to_free) +{ + PedSector bytes_buff; + HfsPrivateFSData* priv_data = (HfsPrivateFSData*) + fs->type_specific; + HfsMasterDirectoryBlock* mdb = priv_data->mdb; + HfsCPrivateCache* cache; + unsigned int to_fblock = fblock; + unsigned int start = fblock; + unsigned int divisor = PED_BE16_TO_CPU (mdb->total_blocks) + + 1 - start - to_free; + int ret; + + PED_ASSERT (!hfs_block); + + cache = hfs_cache_extents (fs, timer); + if (!cache) + return 0; + + /* Calculate the size of the copy buffer : + * Takes BLOCK_MAX_BUFF HFS blocks, but if > BYTES_MAX_BUFF + * takes the maximum number of HFS blocks so that the buffer + * will remain smaller than or equal to BYTES_MAX_BUFF, with + * a minimum of 1 HFS block */ + bytes_buff = PED_BE32_TO_CPU (priv_data->mdb->block_size) + * (PedSector) BLOCK_MAX_BUFF; + if (bytes_buff > BYTES_MAX_BUFF) { + hfs_block_count = BYTES_MAX_BUFF + / PED_BE32_TO_CPU (priv_data->mdb->block_size); + if (!hfs_block_count) + hfs_block_count = 1; + bytes_buff = (PedSector) hfs_block_count + * PED_BE32_TO_CPU (priv_data->mdb->block_size); + } else + hfs_block_count = BLOCK_MAX_BUFF; + + /* If the cache code requests more space, give it to him */ + if (bytes_buff < hfsc_cache_needed_buffer (cache)) + bytes_buff = hfsc_cache_needed_buffer (cache); + + hfs_block = (uint8_t*) ped_malloc (bytes_buff); + if (!hfs_block) + goto error_cache; + + if (!hfs_read_bad_blocks (fs)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad blocks list could not be loaded.")); + goto error_alloc; + } + + while (fblock < PED_BE16_TO_CPU (mdb->total_blocks)) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,fblock) + && (!hfs_is_bad_block (fs, fblock))) { + if (!(ret = hfs_move_extent_starting_at (fs, &fblock, + &to_fblock, cache))) + to_fblock = ++fblock; + else if (ret == -1) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("An error occurred during extent " + "relocation.")); + goto error_alloc; + } + } else { + fblock++; + } + + ped_timer_update(timer, (float)(to_fblock - start)/divisor); + } + + free (hfs_block); hfs_block = NULL; hfs_block_count = 0; + hfsc_delete_cache (cache); + return 1; + +error_alloc: + free (hfs_block); hfs_block = NULL; hfs_block_count = 0; +error_cache: + hfsc_delete_cache (cache); + return 0; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/hfs/reloc.h b/libparted/fs/r/hfs/reloc.h new file mode 100644 index 0000000..d8b1e6d --- /dev/null +++ b/libparted/fs/r/hfs/reloc.h @@ -0,0 +1,36 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 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/>. +*/ + +#ifndef _RELOC_H +#define _RELOC_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfs_update_mdb (PedFileSystem *fs); + +int +hfs_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, + PedTimer* timer, unsigned int to_free); + +#endif /* _RELOC_H */ diff --git a/libparted/fs/r/hfs/reloc_plus.c b/libparted/fs/r/hfs/reloc_plus.c new file mode 100644 index 0000000..904929c --- /dev/null +++ b/libparted/fs/r/hfs/reloc_plus.c @@ -0,0 +1,948 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004-2005, 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/>. +*/ + +#ifndef DISCOVER_ONLY + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> +#include <stdint.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "hfs.h" +#include "file_plus.h" +#include "advfs_plus.h" +#include "cache.h" +#include "journal.h" + +#include "reloc_plus.h" + +/* This function moves data of size blocks starting at block *ptr_fblock + to block *ptr_to_fblock */ +/* return new start or -1 on failure */ +/* -1 is ok because there can only be 2^32-1 blocks, so the max possible + last one is 2^32-2 (and anyway it contains Alternate VH), so + -1 (== 2^32-1[2^32]) never represent a valid block */ +static int +hfsplus_effect_move_extent (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock, unsigned int size) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + unsigned int i, ok = 0; + unsigned int next_to_fblock; + unsigned int start, stop; + + PED_ASSERT (hfsp_block != NULL); + PED_ASSERT (*ptr_to_fblock <= *ptr_fblock); + /* quiet GCC */ + start = stop = 0; + +/* + Try to fit the extent AT or _BEFORE_ the wanted place, + or then in the gap between dest and source. + If failed try to fit the extent after source, for 2 pass relocation + The extent is always copied in a non overlapping way +*/ + + /* Backward search */ + /* 1 pass relocation AT or BEFORE *ptr_to_fblock */ + if (*ptr_to_fblock != *ptr_fblock) { + start = stop = *ptr_fblock < *ptr_to_fblock+size ? + *ptr_fblock : *ptr_to_fblock+size; + while (start && stop-start != size) { + --start; + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,start)) + stop = start; + } + ok = (stop-start == size); + } + + /* Forward search */ + /* 1 pass relocation in the gap merged with 2 pass reloc after source */ + if (!ok && *ptr_to_fblock != *ptr_fblock) { + start = stop = *ptr_to_fblock+1; + while (stop < PED_BE32_TO_CPU(priv_data->vh->total_blocks) + && stop-start != size) { + if (TST_BLOC_OCCUPATION(priv_data->alloc_map,stop)) + start = stop + 1; + ++stop; + } + ok = (stop-start == size); + } + + /* new non overlapping room has been found ? */ + if (ok) { + /* enough room */ + PedSector abs_sector; + unsigned int ai, j, block; + unsigned int block_sz = (PED_BE32_TO_CPU ( + priv_data->vh->block_size) + / PED_SECTOR_SIZE_DEFAULT); + + if (stop > *ptr_to_fblock && stop <= *ptr_fblock) + /* Fit in the gap */ + next_to_fblock = stop; + else + /* Before or after the gap */ + next_to_fblock = *ptr_to_fblock; + + /* move blocks */ + for (i = 0; i < size; /*i++*/) { + j = size - i; j = (j < hfsp_block_count) ? + j : hfsp_block_count ; + + abs_sector = (PedSector) (*ptr_fblock + i) * block_sz; + if (!ped_geometry_read (priv_data->plus_geom, + hfsp_block, abs_sector, + block_sz * j)) + return -1; + + abs_sector = (PedSector) (start + i) * block_sz; + if (!ped_geometry_write (priv_data->plus_geom, + hfsp_block, abs_sector, + block_sz * j)) + return -1; + + for (ai = i+j; i < ai; i++) { + /* free source block */ + block = *ptr_fblock + i; + CLR_BLOC_OCCUPATION(priv_data->alloc_map,block); + SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map, + block/(PED_SECTOR_SIZE_DEFAULT*8)); + + /* set dest block */ + block = start + i; + SET_BLOC_OCCUPATION(priv_data->alloc_map,block); + SET_BLOC_OCCUPATION(priv_data->dirty_alloc_map, + block/(PED_SECTOR_SIZE_DEFAULT*8)); + } + } + if (!ped_geometry_sync_fast (priv_data->plus_geom)) + return -1; + + *ptr_fblock += size; + *ptr_to_fblock = next_to_fblock; + } else { + if (*ptr_fblock != *ptr_to_fblock) + /* not enough room */ + ped_exception_throw (PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE, + _("An extent has not been relocated.")); + start = *ptr_fblock; + *ptr_fblock = *ptr_to_fblock = start + size; + } + + return start; +} + +/* Returns 0 on error */ +/* 1 on succes */ +int +hfsplus_update_vh (PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node[PED_SECTOR_SIZE_DEFAULT]; + + if (!ped_geometry_read (priv_data->plus_geom, node, 2, 1)) + return 0; + memcpy (node, priv_data->vh, sizeof (HfsPVolumeHeader)); + if (!ped_geometry_write (priv_data->plus_geom, node, 2, 1) + || !ped_geometry_write (priv_data->plus_geom, node, + priv_data->plus_geom->length - 2, 1) + || !ped_geometry_sync_fast (priv_data->plus_geom)) + return 0; + return 1; +} + +static int +hfsplus_do_move (PedFileSystem* fs, unsigned int *ptr_src, + unsigned int *ptr_dest, HfsCPrivateCache* cache, + HfsCPrivateExtent* ref) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPPrivateFile* file; + HfsPExtDescriptor* extent; + HfsCPrivateExtent* move; + int new_start; + + new_start = hfsplus_effect_move_extent (fs, ptr_src, ptr_dest, + ref->ext_length); + + if (new_start == -1) return -1; + + if (ref->ext_start != (unsigned) new_start) { + switch (ref->where) { + /************ VH ************/ + case CR_PRIM_CAT : + priv_data->catalog_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_PRIM; + case CR_PRIM_EXT : + priv_data->extents_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_PRIM; + case CR_PRIM_ATTR : + priv_data->attributes_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_PRIM; + case CR_PRIM_ALLOC : + priv_data->allocation_file + ->first[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_PRIM; + case CR_PRIM_START : + /* No startup file opened */ + CR_PRIM : + extent = ( HfsPExtDescriptor* ) + ( (uint8_t*)priv_data->vh + ref->ref_offset ); + extent[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + if (!hfsplus_update_vh(fs)) + return -1; + break; + + /************** BTREE *************/ + case CR_BTREE_CAT_JIB : + if (!hfsj_update_jib(fs, new_start)) + return -1; + goto BTREE_CAT; + + case CR_BTREE_CAT_JL : + if (!hfsj_update_jl(fs, new_start)) + return -1; + goto BTREE_CAT; + + BTREE_CAT: + case CR_BTREE_CAT : + file = priv_data->catalog_file; + goto CR_BTREE; + + case CR_BTREE_ATTR : + file = priv_data->attributes_file; + goto CR_BTREE; + + case CR_BTREE_EXT_ATTR : + if (priv_data->attributes_file + ->cache[ref->ref_index].start_block + == PED_CPU_TO_BE32(ref->ext_start)) + priv_data->attributes_file + ->cache[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_BTREE_EXT; + case CR_BTREE_EXT_CAT : + if (priv_data->catalog_file + ->cache[ref->ref_index].start_block + == PED_CPU_TO_BE32(ref->ext_start)) + priv_data->catalog_file + ->cache[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_BTREE_EXT; + case CR_BTREE_EXT_ALLOC : + if (priv_data->allocation_file + ->cache[ref->ref_index].start_block + == PED_CPU_TO_BE32(ref->ext_start)) + priv_data->allocation_file + ->cache[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + goto CR_BTREE_EXT; + case CR_BTREE_EXT_START : + /* No startup file opened */ + CR_BTREE_EXT : + case CR_BTREE_EXT_0 : + file = priv_data->extents_file; + + CR_BTREE : + PED_ASSERT(PED_SECTOR_SIZE_DEFAULT * ref->sect_by_block + > ref->ref_offset); + if (!hfsplus_file_read(file, hfsp_block, + (PedSector)ref->ref_block * ref->sect_by_block, + ref->sect_by_block)) + return -1; + extent = ( HfsPExtDescriptor* ) + ( hfsp_block + ref->ref_offset ); + extent[ref->ref_index].start_block = + PED_CPU_TO_BE32(new_start); + if (!hfsplus_file_write(file, hfsp_block, + (PedSector)ref->ref_block * ref->sect_by_block, + ref->sect_by_block) + || !ped_geometry_sync_fast (priv_data->plus_geom)) + return -1; + break; + + /********** BUG *********/ + default : + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("A reference to an extent comes from a place " + "it should not. You should check the file " + "system!")); + return -1; + break; + } + + move = hfsc_cache_move_extent(cache, ref->ext_start, new_start); + if (!move) return -1; + PED_ASSERT(move == ref); + } + + return new_start; +} + +/* save any dirty sector of the allocation bitmap file */ +static int +hfsplus_save_allocation(PedFileSystem *fs) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + unsigned int map_sectors, i, j; + int ret = 1; + + map_sectors = ( PED_BE32_TO_CPU (priv_data->vh->total_blocks) + + PED_SECTOR_SIZE_DEFAULT * 8 - 1 ) / (PED_SECTOR_SIZE_DEFAULT * 8); + + for (i = 0; i < map_sectors;) { + for (j = i; + (TST_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j)); + ++j) + CLR_BLOC_OCCUPATION(priv_data->dirty_alloc_map,j); + if (j-i) { + ret = hfsplus_file_write(priv_data->allocation_file, + priv_data->alloc_map + i * PED_SECTOR_SIZE_DEFAULT, + i, j-i) && ret; + i = j; + } else + ++i; + } + + return ret; +} + +/* This function moves an extent starting at block fblock + to block to_fblock if there's enough room */ +/* Return 1 if everything was fine */ +/* Return -1 if an error occurred */ +/* Return 0 if no extent was found */ +static int +hfsplus_move_extent_starting_at (PedFileSystem *fs, unsigned int *ptr_fblock, + unsigned int *ptr_to_fblock, + HfsCPrivateCache* cache) +{ + HfsCPrivateExtent* ref; + unsigned int old_start, new_start; + + ref = hfsc_cache_search_extent(cache, *ptr_fblock); + if (!ref) return 0; + + old_start = *ptr_fblock; + new_start = hfsplus_do_move(fs, ptr_fblock, ptr_to_fblock, cache, ref); + if (new_start == (unsigned)-1) return -1; + if (new_start > old_start) { + new_start = hfsplus_do_move(fs, &new_start, ptr_to_fblock, + cache, ref); + if (new_start == (unsigned)-1 || new_start > old_start) + return -1; + } + + hfsplus_save_allocation(fs); + return 1; +} + +static int +hfsplus_cache_from_vh(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPExtDescriptor* extent; + unsigned int j; + + extent = priv_data->vh->allocation_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_ALLOC, + j ) + ) + return 0; + } + + extent = priv_data->vh->extents_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_EXT, + j ) + ) + return 0; + } + + extent = priv_data->vh->catalog_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_CAT, + j ) + ) + return 0; + } + + extent = priv_data->vh->attributes_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_ATTR, + j ) + ) + return 0; + } + + extent = priv_data->vh->startup_file.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + 0, /* unused for vh */ + ((uint8_t*)extent) - ((uint8_t*)priv_data->vh), + 1, /* load / save 1 sector */ + CR_PRIM_START, + j ) + ) + return 0; + } + + return 1; +} + +static int +hfsplus_cache_from_catalog(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPCatalogKey* catalog_key; + HfsPCatalog* catalog_data; + HfsPExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j, size, bsize; + uint32_t jib = priv_data->jib_start_block, + jl = priv_data->jl_start_block; + uint16_t catalog_pos; + + if (!priv_data->catalog_file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This HFS+ volume has no catalog file. " + "This is very unusual!")); + return 1; + } + + /* Search the extent starting at *ptr_block in the catalog file */ + if (!hfsplus_file_read_sector (priv_data->catalog_file, node_1, 0)) + return 0; + header = (HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC); + leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + bsize = PED_BE16_TO_CPU (header->node_size); + size = bsize / PED_SECTOR_SIZE_DEFAULT; + PED_ASSERT(size < 256); + + node = (uint8_t*) ped_malloc(bsize); + if (!node) return 0; + HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node; + + for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfsplus_file_read (priv_data->catalog_file, node, + (PedSector) leaf_node * size, size)) { + free (node); + return 0; + } + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + unsigned int skip; + uint8_t where; + + uint16_t value; + memcpy(&value, node+(bsize - (2*i)), sizeof(uint16_t)); + catalog_pos = PED_BE16_TO_CPU(value); + catalog_key = (HfsPCatalogKey*)(node + catalog_pos); + skip = ( 2 + PED_BE16_TO_CPU (catalog_key->key_length) + + 1) & ~1; + catalog_data = (HfsPCatalog*) + (((uint8_t*)catalog_key) + skip); + /* check for obvious error in FS */ + if ((catalog_pos < HFS_FIRST_REC) + || ((uint8_t*)catalog_data - node + >= (signed) bsize + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + free (node); + return 0; + } + + if (PED_BE16_TO_CPU(catalog_data->type)!=HFS_CAT_FILE) + continue; + + extent = catalog_data->sel.file.data_fork.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + where = CR_BTREE_CAT; + if ( PED_BE32_TO_CPU(extent[j].start_block) + == jib ) { + jib = 0; + where = CR_BTREE_CAT_JIB; + } else + if ( PED_BE32_TO_CPU(extent[j].start_block) + == jl ) { + jl = 0; + where = CR_BTREE_CAT_JL; + } + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + size, + where, + j ) + ) { + free (node); + return 0; + } + } + + extent = catalog_data->sel.file.res_fork.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + size, + CR_BTREE_CAT, + j ) + ) { + free (node); + return 0; + } + } + } + } + + free (node); + return 1; +} + +static int +hfsplus_cache_from_extent(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPExtentKey* extent_key; + HfsPExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j, size, bsize; + uint16_t extent_pos; + + if (!priv_data->extents_file->sect_nb) { + ped_exception_throw ( + PED_EXCEPTION_INFORMATION, + PED_EXCEPTION_OK, + _("This HFS+ volume has no extents overflow " + "file. This is quite unusual!")); + return 1; + } + + if (!hfsplus_file_read_sector (priv_data->extents_file, node_1, 0)) + return 0; + header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC)); + leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + bsize = PED_BE16_TO_CPU (header->node_size); + size = bsize / PED_SECTOR_SIZE_DEFAULT; + PED_ASSERT(size < 256); + + node = (uint8_t*) ped_malloc (bsize); + if (!node) return -1; + HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node; + + for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfsplus_file_read (priv_data->extents_file, node, + (PedSector) leaf_node * size, size)) { + free (node); + return 0; + } + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + uint8_t where; + uint16_t value; + memcpy(&value, node+(bsize - (2*i)), sizeof(uint16_t)); + extent_pos = PED_BE16_TO_CPU(value); + extent_key = (HfsPExtentKey*)(node + extent_pos); + extent = (HfsPExtDescriptor*) + (((uint8_t*)extent_key) + sizeof (HfsPExtentKey)); + /* check for obvious error in FS */ + if ((extent_pos < HFS_FIRST_REC) + || ((uint8_t*)extent - node + >= (signed)bsize + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + free (node); + return -1; + } + + switch (extent_key->file_ID) { + case PED_CPU_TO_BE32 (HFS_XTENT_ID) : + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The extents overflow file should not" + " contain its own extents! You should " + "check the file system.")) + != PED_EXCEPTION_IGNORE) + return 0; + where = CR_BTREE_EXT_EXT; + break; + case PED_CPU_TO_BE32 (HFS_CATALOG_ID) : + where = CR_BTREE_EXT_CAT; + break; + case PED_CPU_TO_BE32 (HFSP_ALLOC_ID) : + where = CR_BTREE_EXT_ALLOC; + break; + case PED_CPU_TO_BE32 (HFSP_STARTUP_ID) : + where = CR_BTREE_EXT_START; + break; + case PED_CPU_TO_BE32 (HFSP_ATTRIB_ID) : + where = CR_BTREE_EXT_ATTR; + break; + default : + where = CR_BTREE_EXT_0; + break; + } + + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU(extent[j].start_block), + PED_BE32_TO_CPU(extent[j].block_count), + leaf_node, + (uint8_t*)extent - node, + size, + where, + j ) + ) { + free (node); + return 0; + } + } + } + } + + free (node); + return 1; +} + +static int +hfsplus_cache_from_attributes(HfsCPrivateCache* cache, PedFileSystem* fs, + PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + uint8_t node_1[PED_SECTOR_SIZE_DEFAULT]; + uint8_t* node; + HfsPHeaderRecord* header; + HfsPPrivateGenericKey* generic_key; + HfsPForkDataAttr* fork_ext_data; + HfsPExtDescriptor* extent; + unsigned int leaf_node, record_number; + unsigned int i, j, size, bsize; + uint16_t generic_pos; + + /* attributes file is facultative */ + if (!priv_data->attributes_file->sect_nb) + return 1; + + /* Search the extent starting at *ptr_block in the catalog file */ + if (!hfsplus_file_read_sector (priv_data->attributes_file, node_1, 0)) + return 0; + header = ((HfsPHeaderRecord*) (node_1 + HFS_FIRST_REC)); + leaf_node = PED_BE32_TO_CPU (header->first_leaf_node); + bsize = PED_BE16_TO_CPU (header->node_size); + size = bsize / PED_SECTOR_SIZE_DEFAULT; + PED_ASSERT(size < 256); + + node = (uint8_t*) ped_malloc(bsize); + if (!node) return 0; + HfsPNodeDescriptor *desc = (HfsPNodeDescriptor*) node; + + for (; leaf_node; leaf_node = PED_BE32_TO_CPU (desc->next)) { + if (!hfsplus_file_read (priv_data->attributes_file, node, + (PedSector) leaf_node * size, size)) { + free (node); + return 0; + } + record_number = PED_BE16_TO_CPU (desc->rec_nb); + for (i = 1; i <= record_number; i++) { + unsigned int skip; + uint16_t value; + memcpy(&value, node+(bsize - (2*i)), sizeof(uint16_t)); + generic_pos = PED_BE16_TO_CPU(value); + generic_key = (HfsPPrivateGenericKey*)(node + generic_pos); + skip = ( 2 + PED_BE16_TO_CPU (generic_key->key_length) + + 1 ) & ~1; + fork_ext_data = (HfsPForkDataAttr*)(node+generic_pos+skip); + /* check for obvious error in FS */ + if ((generic_pos < HFS_FIRST_REC) + || ((uint8_t*)fork_ext_data - node + >= (signed) bsize + - 2 * (signed)(record_number+1))) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The file system contains errors.")); + free (node); + return 0; + } + + if (fork_ext_data->record_type + == PED_CPU_TO_BE32 ( HFSP_ATTR_FORK ) ) { + extent = fork_ext_data->fork_res.fork.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU ( + extent[j].start_block ), + PED_BE32_TO_CPU ( + extent[j].block_count ), + leaf_node, + (uint8_t*)extent-node, + size, + CR_BTREE_ATTR, + j ) + ) { + free(node); + return 0; + } + } + } else if (fork_ext_data->record_type + == PED_CPU_TO_BE32 ( HFSP_ATTR_EXTENTS ) ) { + extent = fork_ext_data->fork_res.extents; + for (j = 0; j < HFSP_EXT_NB; ++j) { + if (!extent[j].block_count) break; + if (!hfsc_cache_add_extent( + cache, + PED_BE32_TO_CPU ( + extent[j].start_block ), + PED_BE32_TO_CPU ( + extent[j].block_count ), + leaf_node, + (uint8_t*)extent-node, + size, + CR_BTREE_ATTR, + j ) + ) { + free(node); + return 0; + } + } + } else continue; + } + } + + free (node); + return 1; +} + +static HfsCPrivateCache* +hfsplus_cache_extents(PedFileSystem* fs, PedTimer* timer) +{ + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsCPrivateCache* ret; + unsigned int file_number, block_number; + + file_number = PED_BE32_TO_CPU(priv_data->vh->file_count); + block_number = PED_BE32_TO_CPU(priv_data->vh->total_blocks); + ret = hfsc_new_cache(block_number, file_number); + if (!ret) return NULL; + + if (!hfsplus_cache_from_vh(ret, fs, timer) || + !hfsplus_cache_from_catalog(ret, fs, timer) || + !hfsplus_cache_from_extent(ret, fs, timer) || + !hfsplus_cache_from_attributes(ret, fs, timer)) { + ped_exception_throw( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Could not cache the file system in memory.")); + hfsc_delete_cache(ret); + return NULL; + } + + return ret; +} + +/* This function moves file's data to compact used and free space, + starting at fblock block */ +/* return 0 on error */ +int +hfsplus_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, + PedTimer* timer, unsigned int to_free) +{ + PedSector bytes_buff; + HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*) + fs->type_specific; + HfsPVolumeHeader* vh = priv_data->vh; + HfsCPrivateCache* cache; + unsigned int to_fblock = fblock; + unsigned int start = fblock; + unsigned int divisor = PED_BE32_TO_CPU (vh->total_blocks) + + 1 - start - to_free; + int ret; + + PED_ASSERT (!hfsp_block); + + cache = hfsplus_cache_extents (fs, timer); + if (!cache) + return 0; + + /* Calculate the size of the copy buffer : + * Takes BLOCK_MAX_BUFF HFS blocks, but if > BYTES_MAX_BUFF + * takes the maximum number of HFS blocks so that the buffer + * will remain smaller than or equal to BYTES_MAX_BUFF, with + * a minimum of 1 HFS block */ + bytes_buff = PED_BE32_TO_CPU (priv_data->vh->block_size) + * (PedSector) BLOCK_MAX_BUFF; + if (bytes_buff > BYTES_MAX_BUFF) { + hfsp_block_count = BYTES_MAX_BUFF + / PED_BE32_TO_CPU (priv_data->vh->block_size); + if (!hfsp_block_count) + hfsp_block_count = 1; + bytes_buff = (PedSector) hfsp_block_count + * PED_BE32_TO_CPU (priv_data->vh->block_size); + } else + hfsp_block_count = BLOCK_MAX_BUFF; + + /* If the cache code requests more space, give it to him */ + if (bytes_buff < hfsc_cache_needed_buffer (cache)) + bytes_buff = hfsc_cache_needed_buffer (cache); + + hfsp_block = (uint8_t*) ped_malloc (bytes_buff); + if (!hfsp_block) + goto error_cache; + + if (!hfsplus_read_bad_blocks (fs)) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Bad blocks list could not be loaded.")); + goto error_alloc; + } + + while ( fblock < ( priv_data->plus_geom->length - 2 ) + / ( PED_BE32_TO_CPU (vh->block_size) + / PED_SECTOR_SIZE_DEFAULT ) ) { + if (TST_BLOC_OCCUPATION (priv_data->alloc_map, fblock) + && (!hfsplus_is_bad_block (fs, fblock))) { + if (!(ret = hfsplus_move_extent_starting_at (fs, + &fblock, &to_fblock, cache))) + to_fblock = ++fblock; + else if (ret == -1) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("An error occurred during extent " + "relocation.")); + goto error_alloc; + } + } else { + fblock++; + } + + ped_timer_update(timer, (float)(to_fblock - start) / divisor); + } + + free (hfsp_block); hfsp_block = NULL; hfsp_block_count = 0; + hfsc_delete_cache (cache); + return 1; + +error_alloc: + free (hfsp_block); hfsp_block = NULL; hfsp_block_count = 0; +error_cache: + hfsc_delete_cache (cache); + return 0; +} + +#endif /* !DISCOVER_ONLY */ diff --git a/libparted/fs/r/hfs/reloc_plus.h b/libparted/fs/r/hfs/reloc_plus.h new file mode 100644 index 0000000..8c5998a --- /dev/null +++ b/libparted/fs/r/hfs/reloc_plus.h @@ -0,0 +1,37 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2004, 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/>. +*/ + +#ifndef _RELOC_PLUS_H +#define _RELOC_PLUS_H + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#include "hfs.h" + +int +hfsplus_update_vh (PedFileSystem *fs); + +int +hfsplus_pack_free_space_from_block (PedFileSystem *fs, unsigned int fblock, + PedTimer* timer, unsigned int to_free); + + +#endif /* _RELOC_PLUS_H */ diff --git a/libparted/fs/reiserfs/reiserfs.c b/libparted/fs/reiserfs/reiserfs.c new file mode 100644 index 0000000..0638f3e --- /dev/null +++ b/libparted/fs/reiserfs/reiserfs.c @@ -0,0 +1,94 @@ +/* + reiserfs.c -- ReiserFS detection + Copyright (C) 2001-2002, 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 <config.h> + +#include <uuid/uuid.h> +#include <fcntl.h> +#include <errno.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 + +#include "reiserfs.h" + +static PedSector reiserfs_super_offset[] = { 128, 16, -1 }; +static PedFileSystemType* reiserfs_type; + +static PedGeometry *reiserfs_probe(PedGeometry *geom) +{ + int i; + + PED_ASSERT(geom != NULL); + reiserfs_super_block_t *sb = alloca (geom->dev->sector_size); + + for (i = 0; reiserfs_super_offset[i] != -1; i++) { + if (reiserfs_super_offset[i] >= geom->length) + continue; + if (!ped_geometry_read (geom, sb, reiserfs_super_offset[i], 1)) + continue; + + if (strncmp(REISERFS_SIGNATURE, sb->s_magic, + strlen(REISERFS_SIGNATURE)) == 0 + || strncmp(REISER2FS_SIGNATURE, sb->s_magic, + strlen(REISER2FS_SIGNATURE)) == 0 + || strncmp(REISER3FS_SIGNATURE, sb->s_magic, + strlen(REISER3FS_SIGNATURE)) == 0) { + PedSector block_size; + PedSector block_count; + + block_size = PED_LE16_TO_CPU(sb->s_blocksize) + / geom->dev->sector_size; + block_count = PED_LE32_TO_CPU(sb->s_block_count); + return ped_geometry_new(geom->dev, geom->start, + block_size * block_count); + } + } + return NULL; +} + + +static PedFileSystemOps reiserfs_simple_ops = { + probe: reiserfs_probe, +}; + +static PedFileSystemType reiserfs_simple_type = { + next: NULL, + ops: &reiserfs_simple_ops, + name: "reiserfs", +}; + +void ped_file_system_reiserfs_init() +{ + reiserfs_type = &reiserfs_simple_type; + ped_file_system_type_register(reiserfs_type); +} + +void ped_file_system_reiserfs_done() +{ + ped_file_system_type_unregister(reiserfs_type); +} diff --git a/libparted/fs/reiserfs/reiserfs.h b/libparted/fs/reiserfs/reiserfs.h new file mode 100644 index 0000000..ee92b62 --- /dev/null +++ b/libparted/fs/reiserfs/reiserfs.h @@ -0,0 +1,109 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 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/>. +*/ + +#ifndef REISERFS_H +#define REISERFS_H + +#define REISERFS_API_VERSION 0 + +#define REISERFS_SIGNATURE "ReIsErFs" +#define REISER2FS_SIGNATURE "ReIsEr2Fs" +#define REISER3FS_SIGNATURE "ReIsEr3Fs" + +#define DEFAULT_BLOCK_SIZE 4096 + +struct reiserfs_super_block { + uint32_t s_block_count; + uint32_t s_free_blocks; + uint32_t s_root_block; + uint32_t s_journal_block; + uint32_t s_journal_dev; + uint32_t s_orig_journal_size; + uint32_t s_journal_trans_max; + uint32_t s_journal_block_count; + uint32_t s_journal_max_batch; + uint32_t s_journal_max_commit_age; + uint32_t s_journal_max_trans_age; + uint16_t s_blocksize; + uint16_t s_oid_maxsize; + uint16_t s_oid_cursize; + uint16_t s_state; + char s_magic[10]; + uint16_t s_fsck_state; + uint32_t s_hash_function_code; + uint16_t s_tree_height; + uint16_t s_bmap_nr; + uint16_t s_version; + char padding[438]; +}; + +typedef struct reiserfs_super_block reiserfs_super_block_t; + +enum reiserfs_exception_type { + EXCEPTION_INFORMATION = 1, + EXCEPTION_WARNING = 2, + EXCEPTION_ERROR = 3, + EXCEPTION_FATAL = 4, + EXCEPTION_BUG = 5, + EXCEPTION_NO_FEATURE = 6 +}; + +typedef enum reiserfs_exception_type reiserfs_exception_type_t; + +enum reiserfs_exception_option { + EXCEPTION_UNHANDLED = 1 << 0, + EXCEPTION_FIX = 1 << 1, + EXCEPTION_YES = 1 << 2, + EXCEPTION_NO = 1 << 3, + EXCEPTION_OK = 1 << 4, + EXCEPTION_RETRY = 1 << 5, + EXCEPTION_IGNORE = 1 << 6, + EXCEPTION_CANCEL = 1 << 7 +}; + +typedef enum reiserfs_exception_option reiserfs_exception_option_t; + +typedef void (reiserfs_gauge_handler_t)(const char *, unsigned int, void *, int, int, int); + +typedef void * reiserfs_exception_t; +typedef void * reiserfs_gauge_t; +typedef void * reiserfs_fs_t; + +#define FS_FORMAT_3_5 0 +#define FS_FORMAT_3_6 2 + +#define SUPER_OFFSET_IN_BYTES 64*1024 + +#define DEFAULT_JOURNAL_SIZE 8192 + +#define JOURNAL_MIN_SIZE 512 +#define JOURNAL_MIN_TRANS 256 +#define JOURNAL_MAX_TRANS 1024 + +#define JOURNAL_DEF_RATIO 8 +#define JOURNAL_MIN_RATIO 2 +#define JOURNAL_MAX_BATCH 900 +#define JOURNAL_MAX_COMMIT_AGE 30 +#define JOURNAL_MAX_TRANS_AGE 30 + +#define TEA_HASH 1 +#define YURA_HASH 2 +#define R5_HASH 3 + +#endif diff --git a/libparted/fs/udf/udf.c b/libparted/fs/udf/udf.c new file mode 100644 index 0000000..a48a42e --- /dev/null +++ b/libparted/fs/udf/udf.c @@ -0,0 +1,175 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2018-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> + +/* Read bytes using ped_geometry_read() function */ +static int read_bytes (const PedGeometry* geom, void* buffer, PedSector offset, PedSector count) +{ + char* sector_buffer; + PedSector sector_offset, sector_count, buffer_offset; + + sector_offset = offset / geom->dev->sector_size; + sector_count = (offset + count + geom->dev->sector_size - 1) / geom->dev->sector_size - sector_offset; + buffer_offset = offset - sector_offset * geom->dev->sector_size; + + sector_buffer = alloca (sector_count * geom->dev->sector_size); + + if (!ped_geometry_read (geom, sector_buffer, sector_offset, sector_count)) + return 0; + + memcpy (buffer, sector_buffer + buffer_offset, count); + return 1; +} + +/* Scan VSR and check for UDF VSD */ +static int check_vrs (const PedGeometry* geom, unsigned int vsdsize) +{ + PedSector block; + PedSector offset; + unsigned char ident[5]; + + /* Check only first 64 blocks, but theoretically standard does not define upper limit */ + for (block = 0; block < 64; block++) { + /* VRS starts at fixed offset 32kB, it is independent of block size or vsd size */ + offset = 32768 + block * vsdsize; + + /* Read VSD identifier, it is at offset 1 */ + if (!read_bytes (geom, ident, offset + 1, 5)) + return 0; + + /* Check for UDF identifier */ + if (memcmp (ident, "NSR02", 5) == 0 || + memcmp (ident, "NSR03", 5) == 0) + return 1; + + /* Unknown VSD identifier means end of VRS */ + if (memcmp (ident, "BEA01", 5) != 0 && + memcmp (ident, "TEA01", 5) != 0 && + memcmp (ident, "BOOT2", 5) != 0 && + memcmp (ident, "CD001", 5) != 0 && + memcmp (ident, "CDW02", 5) != 0) + break; + } + + return 0; +} + +/* Check for UDF AVDP */ +static int check_anchor (const PedGeometry* geom, unsigned int blocksize, int rel_block) +{ + PedSector block; + unsigned char tag[16]; + + /* Negative block means relative to the end of device */ + if (rel_block < 0) { + block = geom->length * geom->dev->sector_size / blocksize; + if (block <= (PedSector)(-rel_block)) + return 0; + block -= (PedSector)(-rel_block); + if (block < 257) + return 0; + } else { + block = rel_block; + } + + if (!read_bytes (geom, tag, block * blocksize, 16)) + return 0; + + /* Check for AVDP type (0x0002) */ + if (((unsigned short)tag[0] | ((unsigned short)tag[1] << 8)) != 0x0002) + return 0; + + /* Check that location stored in AVDP matches */ + if (((unsigned long)tag[12] | ((unsigned long)tag[13] << 8) | ((unsigned long)tag[14] << 16) | ((unsigned long)tag[15] << 24)) != block) + return 0; + + return 1; +} + +/* Detect presence of UDF AVDP */ +static int detect_anchor(const PedGeometry* geom, unsigned int blocksize) +{ + /* All possible AVDP locations in preferred order */ + static int anchors[] = { 256, -257, -1, 512 }; + size_t i; + + for (i = 0; i < sizeof (anchors)/sizeof (*anchors); i++) { + if (check_anchor (geom, blocksize, anchors[i])) + return 1; + } + + return 0; +} + +/* Detect UDF filesystem, it must have VRS and AVDP */ +static int detect_udf (const PedGeometry* geom) +{ + unsigned int blocksize; + + /* VSD size is min(2048, UDF block size), check for block sizes <= 2048 */ + if (check_vrs (geom, 2048)) { + for (blocksize = 512; blocksize <= 2048; blocksize *= 2) { + if (detect_anchor (geom, blocksize)) + return 1; + } + } + + /* Check for block sizes larger then 2048, maximal theoretical block size is 32kB */ + for (blocksize = 4096; blocksize <= 32768; blocksize *= 2) { + if (!check_vrs (geom, blocksize)) + continue; + if (detect_anchor (geom, blocksize)) + return 1; + } + + return 0; +} + +PedGeometry* +udf_probe (PedGeometry* geom) +{ + if (!detect_udf (geom)) + return NULL; + + return ped_geometry_duplicate (geom); +} + +static PedFileSystemOps udf_ops = { + probe: udf_probe, +}; + +static PedFileSystemType udf_type = { + next: NULL, + ops: &udf_ops, + name: "udf", +}; + +void +ped_file_system_udf_init () +{ + ped_file_system_type_register (&udf_type); +} + +void +ped_file_system_udf_done () +{ + ped_file_system_type_unregister (&udf_type); +} diff --git a/libparted/fs/ufs/ufs.c b/libparted/fs/ufs/ufs.c new file mode 100644 index 0000000..d2bf421 --- /dev/null +++ b/libparted/fs/ufs/ufs.c @@ -0,0 +1,281 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2001, 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/>. + + Contributor: Ben Collins <bcollins@debian.org> +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> +#include <parted/debug.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include <unistd.h> +#include <string.h> + +/* taken from ufs_fs.h in Linux */ +#define UFS_MAXNAMLEN 255 +#define UFS_MAXMNTLEN 512 +#define UFS_MAXCSBUFS 31 +#define UFS_LINK_MAX 32000 + +#define UFS_MAGIC 0x00011954 +#define UFS_MAGIC_LFN 0x00095014 +#define UFS_MAGIC_FEA 0x00195612 +#define UFS_MAGIC_4GB 0x05231994 + +struct __attribute__ ((packed)) ufs_csum { + uint32_t cs_ndir; /* number of directories */ + uint32_t cs_nbfree; /* number of free blocks */ + uint32_t cs_nifree; /* number of free inodes */ + uint32_t cs_nffree; /* number of free frags */ +}; + +struct __attribute__ ((packed)) ufs_super_block { + uint32_t fs_link; /* UNUSED */ + uint32_t fs_rlink; /* UNUSED */ + uint32_t fs_sblkno; /* addr of super-block in filesys */ + uint32_t fs_cblkno; /* offset of cyl-block in filesys */ + uint32_t fs_iblkno; /* offset of inode-blocks in filesys */ + uint32_t fs_dblkno; /* offset of first data after cg */ + uint32_t fs_cgoffset; /* cylinder group offset in cylinder */ + uint32_t fs_cgmask; /* used to calc mod fs_ntrak */ + uint32_t fs_time; /* last time written -- time_t */ + uint32_t fs_size; /* number of blocks in fs */ + uint32_t fs_dsize; /* number of data blocks in fs */ + uint32_t fs_ncg; /* number of cylinder groups */ + uint32_t fs_bsize; /* size of basic blocks in fs */ + uint32_t fs_fsize; /* size of frag blocks in fs */ + uint32_t fs_frag; /* number of frags in a block in fs */ +/* these are configuration parameters */ + uint32_t fs_minfree; /* minimum percentage of free blocks */ + uint32_t fs_rotdelay; /* num of ms for optimal next block */ + uint32_t fs_rps; /* disk revolutions per second */ +/* these fields can be computed from the others */ + uint32_t fs_bmask; /* ``blkoff'' calc of blk offsets */ + uint32_t fs_fmask; /* ``fragoff'' calc of frag offsets */ + uint32_t fs_bshift; /* ``lblkno'' calc of logical blkno */ + uint32_t fs_fshift; /* ``numfrags'' calc number of frags */ +/* these are configuration parameters */ + uint32_t fs_maxcontig; /* max number of contiguous blks */ + uint32_t fs_maxbpg; /* max number of blks per cyl group */ +/* these fields can be computed from the others */ + uint32_t fs_fragshift; /* block to frag shift */ + uint32_t fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */ + uint32_t fs_sbsize; /* actual size of super block */ + uint32_t fs_csmask; /* csum block offset */ + uint32_t fs_csshift; /* csum block number */ + uint32_t fs_nindir; /* value of NINDIR */ + uint32_t fs_inopb; /* value of INOPB */ + uint32_t fs_nspf; /* value of NSPF */ +/* yet another configuration parameter */ + uint32_t fs_optim; /* optimization preference, see below */ +/* these fields are derived from the hardware */ + union { + struct { + uint32_t fs_npsect; /* # sectors/track including spares */ + } fs_sun; + struct { + int32_t fs_state; /* file system state timestamp */ + } fs_sunx86; + } fs_u1; + uint32_t fs_interleave; /* hardware sector interleave */ + uint32_t fs_trackskew; /* sector 0 skew, per track */ +/* a unique id for this file system (currently unused and unmaintained) */ +/* In 4.3 Tahoe this space is used by fs_headswitch and fs_trkseek */ +/* Neither of those fields is used in the Tahoe code right now but */ +/* there could be problems if they are. */ + uint32_t fs_id[2]; /* file system id */ +/* sizes determined by number of cylinder groups and their sizes */ + uint32_t fs_csaddr; /* blk addr of cyl grp summary area */ + uint32_t fs_cssize; /* size of cyl grp summary area */ + uint32_t fs_cgsize; /* cylinder group size */ +/* these fields are derived from the hardware */ + uint32_t fs_ntrak; /* tracks per cylinder */ + uint32_t fs_nsect; /* sectors per track */ + uint32_t fs_spc; /* sectors per cylinder */ +/* this comes from the disk driver partitioning */ + uint32_t fs_ncyl; /* cylinders in file system */ +/* these fields can be computed from the others */ + uint32_t fs_cpg; /* cylinders per group */ + uint32_t fs_ipg; /* inodes per group */ + uint32_t fs_fpg; /* blocks per group * fs_frag */ +/* this data must be re-computed after crashes */ + struct ufs_csum fs_cstotal; /* cylinder summary information */ +/* these fields are cleared at mount time */ + int8_t fs_fmod; /* super block modified flag */ + int8_t fs_clean; /* file system is clean flag */ + int8_t fs_ronly; /* mounted read-only flag */ + int8_t fs_flags; /* currently unused flag */ + int8_t fs_fsmnt[UFS_MAXMNTLEN]; /* name mounted on */ +/* these fields retain the current block allocation info */ + uint32_t fs_cgrotor; /* last cg searched */ + uint32_t fs_csp[UFS_MAXCSBUFS]; /* list of fs_cs info buffers */ + uint32_t fs_maxcluster; + uint32_t fs_cpc; /* cyl per cycle in postbl */ + uint16_t fs_opostbl[16][8]; /* old rotation block list head */ + union { + struct { + int32_t fs_sparecon[53];/* reserved for future constants */ + int32_t fs_reclaim; + int32_t fs_sparecon2[1]; + int32_t fs_state; /* file system state timestamp */ + uint32_t fs_qbmask[2]; /* ~usb_bmask */ + uint32_t fs_qfmask[2]; /* ~usb_fmask */ + } fs_sun; + struct { + int32_t fs_sparecon[53];/* reserved for future constants */ + int32_t fs_reclaim; + int32_t fs_sparecon2[1]; + uint32_t fs_npsect; /* # sectors/track including spares */ + uint32_t fs_qbmask[2]; /* ~usb_bmask */ + uint32_t fs_qfmask[2]; /* ~usb_fmask */ + } fs_sunx86; + struct { + int32_t fs_sparecon[50];/* reserved for future constants */ + int32_t fs_contigsumsize;/* size of cluster summary array */ + int32_t fs_maxsymlinklen;/* max length of an internal symlink */ + int32_t fs_inodefmt; /* format of on-disk inodes */ + uint32_t fs_maxfilesize[2]; /* max representable file size */ + uint32_t fs_qbmask[2]; /* ~usb_bmask */ + uint32_t fs_qfmask[2]; /* ~usb_fmask */ + int32_t fs_state; /* file system state timestamp */ + } fs_44; + } fs_u2; + int32_t fs_postblformat; /* format of positional layout tables */ + int32_t fs_nrpos; /* number of rotational positions */ + int32_t fs_postbloff; /* (__s16) rotation block list head */ + int32_t fs_rotbloff; /* (uint8_t) blocks for each rotation */ + int32_t fs_magic; /* magic number */ + uint8_t fs_space[4]; /* list of blocks for each rotation */ +}; + +static PedGeometry* +ufs_probe_sun (PedGeometry* geom) +{ + const int sectors = ((3 * 512) + geom->dev->sector_size - 1) / + geom->dev->sector_size; + uint8_t* buf = alloca (sectors * geom->dev->sector_size); + struct ufs_super_block *sb; + + if (geom->length < 5) + return 0; + if (!ped_geometry_read (geom, buf, 16 * 512 / geom->dev->sector_size, sectors)) + return 0; + + sb = (struct ufs_super_block *)buf; + + if (PED_BE32_TO_CPU(sb->fs_magic) == UFS_MAGIC) { + PedSector block_size = PED_BE32_TO_CPU(sb->fs_bsize) / geom->dev->sector_size; + PedSector block_count = PED_BE32_TO_CPU(sb->fs_size); + return ped_geometry_new (geom->dev, geom->start, + block_size * block_count); + } + if (PED_LE32_TO_CPU(sb->fs_magic) == UFS_MAGIC) { + PedSector block_size = PED_LE32_TO_CPU(sb->fs_bsize) / geom->dev->sector_size; + PedSector block_count = PED_LE32_TO_CPU(sb->fs_size); + return ped_geometry_new (geom->dev, geom->start, + block_size * block_count); + } + return NULL; +} + +static PedGeometry* +ufs_probe_hp (PedGeometry* geom) +{ + struct ufs_super_block *sb; + PedSector block_size; + PedSector block_count; + + if (geom->length < 5) + return 0; + const int sectors = ((3 * 512) + geom->dev->sector_size - 1) / + geom->dev->sector_size; + uint8_t* buf = alloca (sectors * geom->dev->sector_size); + + if (!ped_geometry_read (geom, buf, 16 * 512 / geom->dev->sector_size, sectors)) + return 0; + + sb = (struct ufs_super_block *)buf; + + /* Try sane bytesex */ + switch (PED_BE32_TO_CPU(sb->fs_magic)) { + case UFS_MAGIC_LFN: + case UFS_MAGIC_FEA: + case UFS_MAGIC_4GB: + block_size = PED_BE32_TO_CPU(sb->fs_bsize) / geom->dev->sector_size; + block_count = PED_BE32_TO_CPU(sb->fs_size); + return ped_geometry_new (geom->dev, geom->start, + block_size * block_count); + } + + /* Try perverted bytesex */ + switch (PED_LE32_TO_CPU(sb->fs_magic)) { + case UFS_MAGIC_LFN: + case UFS_MAGIC_FEA: + case UFS_MAGIC_4GB: + block_size = PED_LE32_TO_CPU(sb->fs_bsize) / geom->dev->sector_size; + block_count = PED_LE32_TO_CPU(sb->fs_size); + return ped_geometry_new (geom->dev, geom->start, + block_size * block_count); + } + return NULL; +} + +static PedFileSystemOps ufs_ops_sun = { + probe: ufs_probe_sun, +}; + +static PedFileSystemOps ufs_ops_hp = { + probe: ufs_probe_hp, +}; + +static PedFileSystemType ufs_type_sun = { + next: NULL, + ops: &ufs_ops_sun, + name: "sun-ufs", +}; + +static PedFileSystemType ufs_type_hp = { + next: NULL, + ops: &ufs_ops_hp, + name: "hp-ufs", +}; + +void +ped_file_system_ufs_init () +{ + PED_ASSERT (sizeof (struct ufs_super_block) == 1380); + + ped_file_system_type_register (&ufs_type_sun); + ped_file_system_type_register (&ufs_type_hp); +} + +void +ped_file_system_ufs_done () +{ + ped_file_system_type_unregister (&ufs_type_hp); + ped_file_system_type_unregister (&ufs_type_sun); +} diff --git a/libparted/fs/xfs/platform_defs.h b/libparted/fs/xfs/platform_defs.h new file mode 100644 index 0000000..a6ec8fb --- /dev/null +++ b/libparted/fs/xfs/platform_defs.h @@ -0,0 +1,109 @@ +/* include/platform_defs.h. Generated automatically by configure. */ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 3 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + * + * @configure_input@ + */ +#ifndef __XFS_PLATFORM_DEFS_H__ +#define __XFS_PLATFORM_DEFS_H__ + +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include <endian.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/types.h> + +#if (__GLIBC__ <= 2) && (__GLIBC_MINOR__ <= 1) +# define constpp const char * const * +#else +# define constpp char * const * +#endif + +typedef loff_t xfs_off_t; +typedef uint64_t xfs_ino_t; +typedef uint32_t xfs_dev_t; +typedef int64_t xfs_daddr_t; +typedef char* xfs_caddr_t; + +/* long and pointer must be either 32 bit or 64 bit */ +/* #undef HAVE_64BIT_LONG */ +#define HAVE_32BIT_LONG 1 +#define HAVE_32BIT_PTR 1 +/* #undef HAVE_64BIT_PTR */ + +/* Check if __psint_t is set to something meaningful */ +/* #undef HAVE___PSINT_T */ +#ifndef HAVE___PSINT_T +# ifdef HAVE_32BIT_PTR +typedef int __psint_t; +# elif defined HAVE_64BIT_PTR +# ifdef HAVE_64BIT_LONG +typedef long __psint_t; +# else +/* This is a very strange architecture, which has 64 bit pointers but + * not 64 bit longs. So, I'd just punt here and assume long long is Ok */ +typedef long long __psint_t; +# endif +# else +# error Unknown pointer size +# endif +#endif + +/* Check if __psunsigned_t is set to something meaningful */ +/* #undef HAVE___PSUNSIGNED_T */ +#ifndef HAVE___PSUNSIGNED_T +# ifdef HAVE_32BIT_PTR +typedef unsigned int __psunsigned_t; +# elif defined HAVE_64BIT_PTR +# ifdef HAVE_64BIT_LONG +typedef long __psunsigned_t; +# else +/* This is a very strange architecture, which has 64 bit pointers but + * not 64 bit longs. So, I'd just punt here and assume long long is Ok */ +typedef unsigned long long __psunsigned_t; +# endif +# else +# error Unknown pointer size +# endif +#endif + +#ifdef DEBUG +# define ASSERT assert +#else +# define ASSERT(EX) ((void) 0) +#endif + +#endif /* __XFS_PLATFORM_DEFS_H__ */ diff --git a/libparted/fs/xfs/xfs.c b/libparted/fs/xfs/xfs.c new file mode 100644 index 0000000..f5cf96a --- /dev/null +++ b/libparted/fs/xfs/xfs.c @@ -0,0 +1,87 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2001, 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 <config.h> + +#include <parted/parted.h> +#include <parted/endian.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include <uuid/uuid.h> +#include "platform_defs.h" +#include "xfs_types.h" +#include "xfs_sb.h" + +static PedGeometry* +xfs_probe (PedGeometry* geom) +{ + PedSector block_size; + PedSector block_count; + struct xfs_sb *sb = alloca (geom->dev->sector_size); + + if (geom->length < XFS_SB_DADDR + 1) + return NULL; + if (!ped_geometry_read (geom, sb, XFS_SB_DADDR, 1)) + return NULL; + + if (PED_LE32_TO_CPU (sb->sb_magicnum) == XFS_SB_MAGIC) { + block_size = PED_LE32_TO_CPU (sb->sb_blocksize) / geom->dev->sector_size; + block_count = PED_LE64_TO_CPU (sb->sb_dblocks); + + return ped_geometry_new (geom->dev, geom->start, + block_size * block_count); + } + + if (PED_BE32_TO_CPU (sb->sb_magicnum) == XFS_SB_MAGIC) { + block_size = PED_BE32_TO_CPU (sb->sb_blocksize) / geom->dev->sector_size; + block_count = PED_BE64_TO_CPU (sb->sb_dblocks); + + geom = ped_geometry_new (geom->dev, geom->start, + block_size * block_count); + return geom; + } + return NULL; +} + +static PedFileSystemOps xfs_ops = { + probe: xfs_probe, +}; + +static PedFileSystemType xfs_type = { + next: NULL, + ops: &xfs_ops, + name: "xfs", +}; + +void +ped_file_system_xfs_init () +{ + ped_file_system_type_register (&xfs_type); +} + +void +ped_file_system_xfs_done () +{ + ped_file_system_type_unregister (&xfs_type); +} diff --git a/libparted/fs/xfs/xfs_sb.h b/libparted/fs/xfs/xfs_sb.h new file mode 100644 index 0000000..3484145 --- /dev/null +++ b/libparted/fs/xfs/xfs_sb.h @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 3 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ +#ifndef __XFS_SB_H__ +#define __XFS_SB_H__ + +/* + * Super block + * Fits into a 512-byte buffer at daddr_t 0 of each allocation group. + * Only the first of these is ever updated except during growfs. + */ + +struct xfs_buf; +struct xfs_mount; + +#define XFS_SB_MAGIC 0x58465342 /* 'XFSB' */ +#define XFS_SB_VERSION_1 1 /* 5.3, 6.0.1, 6.1 */ +#define XFS_SB_VERSION_2 2 /* 6.2 - attributes */ +#define XFS_SB_VERSION_3 3 /* 6.2 - new inode version */ +#define XFS_SB_VERSION_4 4 /* 6.2+ - bitmask version */ +#define XFS_SB_VERSION_NUMBITS 0x000f +#define XFS_SB_VERSION_ALLFBITS 0xfff0 +#define XFS_SB_VERSION_SASHFBITS 0xf000 +#define XFS_SB_VERSION_REALFBITS 0x0ff0 +#define XFS_SB_VERSION_ATTRBIT 0x0010 +#define XFS_SB_VERSION_NLINKBIT 0x0020 +#define XFS_SB_VERSION_QUOTABIT 0x0040 +#define XFS_SB_VERSION_ALIGNBIT 0x0080 +#define XFS_SB_VERSION_DALIGNBIT 0x0100 +#define XFS_SB_VERSION_SHAREDBIT 0x0200 +#define XFS_SB_VERSION_EXTFLGBIT 0x1000 +#define XFS_SB_VERSION_DIRV2BIT 0x2000 +#define XFS_SB_VERSION_OKSASHFBITS \ + (XFS_SB_VERSION_EXTFLGBIT | \ + XFS_SB_VERSION_DIRV2BIT) +#define XFS_SB_VERSION_OKREALFBITS \ + (XFS_SB_VERSION_ATTRBIT | \ + XFS_SB_VERSION_NLINKBIT | \ + XFS_SB_VERSION_QUOTABIT | \ + XFS_SB_VERSION_ALIGNBIT | \ + XFS_SB_VERSION_DALIGNBIT | \ + XFS_SB_VERSION_SHAREDBIT) +#define XFS_SB_VERSION_OKSASHBITS \ + (XFS_SB_VERSION_NUMBITS | \ + XFS_SB_VERSION_REALFBITS | \ + XFS_SB_VERSION_OKSASHFBITS) +#define XFS_SB_VERSION_OKREALBITS \ + (XFS_SB_VERSION_NUMBITS | \ + XFS_SB_VERSION_OKREALFBITS | \ + XFS_SB_VERSION_OKSASHFBITS) +#define XFS_SB_VERSION_MKFS(ia,dia,extflag,dirv2) \ + (((ia) || (dia) || (extflag) || (dirv2)) ? \ + (XFS_SB_VERSION_4 | \ + ((ia) ? XFS_SB_VERSION_ALIGNBIT : 0) | \ + ((dia) ? XFS_SB_VERSION_DALIGNBIT : 0) | \ + ((extflag) ? XFS_SB_VERSION_EXTFLGBIT : 0) | \ + ((dirv2) ? XFS_SB_VERSION_DIRV2BIT : 0)) : \ + XFS_SB_VERSION_1) + +typedef struct xfs_sb +{ + uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */ + uint32_t sb_blocksize; /* logical block size, bytes */ + xfs_drfsbno_t sb_dblocks; /* number of data blocks */ + xfs_drfsbno_t sb_rblocks; /* number of realtime blocks */ + xfs_drtbno_t sb_rextents; /* number of realtime extents */ + uuid_t sb_uuid; /* file system unique id */ + xfs_dfsbno_t sb_logstart; /* starting block of log if internal */ + xfs_ino_t sb_rootino; /* root inode number */ + xfs_ino_t sb_rbmino; /* bitmap inode for realtime extents */ + xfs_ino_t sb_rsumino; /* summary inode for rt bitmap */ + xfs_agblock_t sb_rextsize; /* realtime extent size, blocks */ + xfs_agblock_t sb_agblocks; /* size of an allocation group */ + xfs_agnumber_t sb_agcount; /* number of allocation groups */ + xfs_extlen_t sb_rbmblocks; /* number of rt bitmap blocks */ + xfs_extlen_t sb_logblocks; /* number of log blocks */ + uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */ + uint16_t sb_sectsize; /* volume sector size, bytes */ + uint16_t sb_inodesize; /* inode size, bytes */ + uint16_t sb_inopblock; /* inodes per block */ + char sb_fname[12]; /* file system name */ + uint8_t sb_blocklog; /* log2 of sb_blocksize */ + uint8_t sb_sectlog; /* log2 of sb_sectsize */ + uint8_t sb_inodelog; /* log2 of sb_inodesize */ + uint8_t sb_inopblog; /* log2 of sb_inopblock */ + uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */ + uint8_t sb_rextslog; /* log2 of sb_rextents */ + uint8_t sb_inprogress; /* mkfs is in progress, don't mount */ + uint8_t sb_imax_pct; /* max % of fs for inode space */ + /* statistics */ + /* + * These fields must remain contiguous. If you really + * want to change their layout, make sure you fix the + * code in xfs_trans_apply_sb_deltas(). + */ + uint64_t sb_icount; /* allocated inodes */ + uint64_t sb_ifree; /* free inodes */ + uint64_t sb_fdblocks; /* free data blocks */ + uint64_t sb_frextents; /* free realtime extents */ + /* + * End contiguous fields. + */ + xfs_ino_t sb_uquotino; /* user quota inode */ + xfs_ino_t sb_gquotino; /* group quota inode */ + uint16_t sb_qflags; /* quota flags */ + uint8_t sb_flags; /* misc. flags */ + uint8_t sb_shared_vn; /* shared version number */ + xfs_extlen_t sb_inoalignmt; /* inode chunk alignment, fsblocks */ + uint32_t sb_unit; /* stripe or raid unit */ + uint32_t sb_width; /* stripe or raid width */ + uint8_t sb_dirblklog; /* log2 of dir block size (fsbs) */ + uint8_t sb_dummy[7]; /* padding */ +} xfs_sb_t; + +/* + * Sequence number values for the fields. + */ +typedef enum { + XFS_SBS_MAGICNUM, XFS_SBS_BLOCKSIZE, XFS_SBS_DBLOCKS, XFS_SBS_RBLOCKS, + XFS_SBS_REXTENTS, XFS_SBS_UUID, XFS_SBS_LOGSTART, XFS_SBS_ROOTINO, + XFS_SBS_RBMINO, XFS_SBS_RSUMINO, XFS_SBS_REXTSIZE, XFS_SBS_AGBLOCKS, + XFS_SBS_AGCOUNT, XFS_SBS_RBMBLOCKS, XFS_SBS_LOGBLOCKS, + XFS_SBS_VERSIONNUM, XFS_SBS_SECTSIZE, XFS_SBS_INODESIZE, + XFS_SBS_INOPBLOCK, XFS_SBS_FNAME, XFS_SBS_BLOCKLOG, + XFS_SBS_SECTLOG, XFS_SBS_INODELOG, XFS_SBS_INOPBLOG, XFS_SBS_AGBLKLOG, + XFS_SBS_REXTSLOG, XFS_SBS_INPROGRESS, XFS_SBS_IMAX_PCT, XFS_SBS_ICOUNT, + XFS_SBS_IFREE, XFS_SBS_FDBLOCKS, XFS_SBS_FREXTENTS, XFS_SBS_UQUOTINO, + XFS_SBS_GQUOTINO, XFS_SBS_QFLAGS, XFS_SBS_FLAGS, XFS_SBS_SHARED_VN, + XFS_SBS_INOALIGNMT, XFS_SBS_UNIT, XFS_SBS_WIDTH, XFS_SBS_DIRBLKLOG, + XFS_SBS_DUMMY, + XFS_SBS_FIELDCOUNT +} xfs_sb_field_t; + +/* + * Mask values, defined based on the xfs_sb_field_t values. + * Only define the ones we're using. + */ +#define XFS_SB_MVAL(x) (1LL << XFS_SBS_ ## x) +#define XFS_SB_UUID XFS_SB_MVAL(UUID) +#define XFS_SB_FNAME XFS_SB_MVAL(FNAME) +#define XFS_SB_ROOTINO XFS_SB_MVAL(ROOTINO) +#define XFS_SB_RBMINO XFS_SB_MVAL(RBMINO) +#define XFS_SB_RSUMINO XFS_SB_MVAL(RSUMINO) +#define XFS_SB_VERSIONNUM XFS_SB_MVAL(VERSIONNUM) +#define XFS_SB_UQUOTINO XFS_SB_MVAL(UQUOTINO) +#define XFS_SB_GQUOTINO XFS_SB_MVAL(GQUOTINO) +#define XFS_SB_QFLAGS XFS_SB_MVAL(QFLAGS) +#define XFS_SB_SHARED_VN XFS_SB_MVAL(SHARED_VN) +#define XFS_SB_UNIT XFS_SB_MVAL(UNIT) +#define XFS_SB_WIDTH XFS_SB_MVAL(WIDTH) +#define XFS_SB_NUM_BITS ((int)XFS_SBS_FIELDCOUNT) +#define XFS_SB_ALL_BITS ((1LL << XFS_SB_NUM_BITS) - 1) +#define XFS_SB_MOD_BITS \ + (XFS_SB_UUID | XFS_SB_ROOTINO | XFS_SB_RBMINO | XFS_SB_RSUMINO | \ + XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \ + XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH) + +/* + * Misc. Flags - warning - these will be cleared by xfs_repair unless + * a feature bit is set when the flag is used. + */ +#define XFS_SBF_NOFLAGS 0x00 /* no flags set */ +#define XFS_SBF_READONLY 0x01 /* only read-only mounts allowed */ + +/* + * define max. shared version we can interoperate with + */ +#define XFS_SB_MAX_SHARED_VN 0 + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_NUM) +int xfs_sb_version_num(xfs_sb_t *sbp); +#define XFS_SB_VERSION_NUM(sbp) xfs_sb_version_num(sbp) +#else +#define XFS_SB_VERSION_NUM(sbp) ((sbp)->sb_versionnum & XFS_SB_VERSION_NUMBITS) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_GOOD_VERSION) +int xfs_sb_good_version(xfs_sb_t *sbp); +#define XFS_SB_GOOD_VERSION(sbp) xfs_sb_good_version(sbp) +#else +#define XFS_SB_GOOD_VERSION_INT(sbp) \ + ((((sbp)->sb_versionnum >= XFS_SB_VERSION_1) && \ + ((sbp)->sb_versionnum <= XFS_SB_VERSION_3)) || \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + !((sbp)->sb_versionnum & ~XFS_SB_VERSION_OKREALBITS) +#ifdef __KERNEL__ +#define XFS_SB_GOOD_VERSION(sbp) \ + (XFS_SB_GOOD_VERSION_INT(sbp) && \ + (sbp)->sb_shared_vn <= XFS_SB_MAX_SHARED_VN) )) +#else +/* + * extra 2 paren's here (( to unconfuse paren-matching editors + * like vi because XFS_SB_GOOD_VERSION_INT is a partial expression + * and the two XFS_SB_GOOD_VERSION's each 2 more close paren's to + * complete the expression. + */ +#define XFS_SB_GOOD_VERSION(sbp) \ + (XFS_SB_GOOD_VERSION_INT(sbp) && \ + (!((sbp)->sb_versionnum & XFS_SB_VERSION_SHAREDBIT) || \ + (sbp)->sb_shared_vn <= XFS_SB_MAX_SHARED_VN)) )) +#endif /* __KERNEL__ */ +#endif + +#define XFS_SB_GOOD_SASH_VERSION(sbp) \ + ((((sbp)->sb_versionnum >= XFS_SB_VERSION_1) && \ + ((sbp)->sb_versionnum <= XFS_SB_VERSION_3)) || \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + !((sbp)->sb_versionnum & ~XFS_SB_VERSION_OKSASHBITS))) + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_TONEW) +unsigned xfs_sb_version_tonew(unsigned v); +#define XFS_SB_VERSION_TONEW(v) xfs_sb_version_tonew(v) +#else +#define XFS_SB_VERSION_TONEW(v) \ + ((((v) == XFS_SB_VERSION_1) ? \ + 0 : \ + (((v) == XFS_SB_VERSION_2) ? \ + XFS_SB_VERSION_ATTRBIT : \ + (XFS_SB_VERSION_ATTRBIT | XFS_SB_VERSION_NLINKBIT))) | \ + XFS_SB_VERSION_4) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_TOOLD) +unsigned xfs_sb_version_toold(unsigned v); +#define XFS_SB_VERSION_TOOLD(v) xfs_sb_version_toold(v) +#else +#define XFS_SB_VERSION_TOOLD(v) \ + (((v) & (XFS_SB_VERSION_QUOTABIT | XFS_SB_VERSION_ALIGNBIT)) ? \ + 0 : \ + (((v) & XFS_SB_VERSION_NLINKBIT) ? \ + XFS_SB_VERSION_3 : \ + (((v) & XFS_SB_VERSION_ATTRBIT) ? \ + XFS_SB_VERSION_2 : \ + XFS_SB_VERSION_1))) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASATTR) +int xfs_sb_version_hasattr(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASATTR(sbp) xfs_sb_version_hasattr(sbp) +#else +#define XFS_SB_VERSION_HASATTR(sbp) \ + (((sbp)->sb_versionnum == XFS_SB_VERSION_2) || \ + ((sbp)->sb_versionnum == XFS_SB_VERSION_3) || \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_ATTRBIT))) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDATTR) +void xfs_sb_version_addattr(xfs_sb_t *sbp); +#define XFS_SB_VERSION_ADDATTR(sbp) xfs_sb_version_addattr(sbp) +#else +#define XFS_SB_VERSION_ADDATTR(sbp) \ + ((sbp)->sb_versionnum = \ + (((sbp)->sb_versionnum == XFS_SB_VERSION_1) ? \ + XFS_SB_VERSION_2 : \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) ? \ + ((sbp)->sb_versionnum | XFS_SB_VERSION_ATTRBIT) : \ + (XFS_SB_VERSION_4 | XFS_SB_VERSION_ATTRBIT)))) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASNLINK) +int xfs_sb_version_hasnlink(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASNLINK(sbp) xfs_sb_version_hasnlink(sbp) +#else +#define XFS_SB_VERSION_HASNLINK(sbp) \ + (((sbp)->sb_versionnum == XFS_SB_VERSION_3) || \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_NLINKBIT))) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDNLINK) +void xfs_sb_version_addnlink(xfs_sb_t *sbp); +#define XFS_SB_VERSION_ADDNLINK(sbp) xfs_sb_version_addnlink(sbp) +#else +#define XFS_SB_VERSION_ADDNLINK(sbp) \ + ((sbp)->sb_versionnum = \ + ((sbp)->sb_versionnum <= XFS_SB_VERSION_2 ? \ + XFS_SB_VERSION_3 : \ + ((sbp)->sb_versionnum | XFS_SB_VERSION_NLINKBIT))) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASQUOTA) +int xfs_sb_version_hasquota(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASQUOTA(sbp) xfs_sb_version_hasquota(sbp) +#else +#define XFS_SB_VERSION_HASQUOTA(sbp) \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_QUOTABIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDQUOTA) +void xfs_sb_version_addquota(xfs_sb_t *sbp); +#define XFS_SB_VERSION_ADDQUOTA(sbp) xfs_sb_version_addquota(sbp) +#else +#define XFS_SB_VERSION_ADDQUOTA(sbp) \ + ((sbp)->sb_versionnum = \ + (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4 ? \ + ((sbp)->sb_versionnum | XFS_SB_VERSION_QUOTABIT) : \ + (XFS_SB_VERSION_TONEW((sbp)->sb_versionnum) | \ + XFS_SB_VERSION_QUOTABIT))) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASALIGN) +int xfs_sb_version_hasalign(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASALIGN(sbp) xfs_sb_version_hasalign(sbp) +#else +#define XFS_SB_VERSION_HASALIGN(sbp) \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_ALIGNBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_SUBALIGN) +void xfs_sb_version_subalign(xfs_sb_t *sbp); +#define XFS_SB_VERSION_SUBALIGN(sbp) xfs_sb_version_subalign(sbp) +#else +#define XFS_SB_VERSION_SUBALIGN(sbp) \ + ((sbp)->sb_versionnum = \ + XFS_SB_VERSION_TOOLD((sbp)->sb_versionnum & ~XFS_SB_VERSION_ALIGNBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASDALIGN) +int xfs_sb_version_hasdalign(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASDALIGN(sbp) xfs_sb_version_hasdalign(sbp) +#else +#define XFS_SB_VERSION_HASDALIGN(sbp) \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_DALIGNBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDDALIGN) +int xfs_sb_version_adddalign(xfs_sb_t *sbp); +#define XFS_SB_VERSION_ADDDALIGN(sbp) xfs_sb_version_adddalign(sbp) +#else +#define XFS_SB_VERSION_ADDDALIGN(sbp) \ + ((sbp)->sb_versionnum = \ + ((sbp)->sb_versionnum | XFS_SB_VERSION_DALIGNBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASSHARED) +int xfs_sb_version_hasshared(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASSHARED(sbp) xfs_sb_version_hasshared(sbp) +#else +#define XFS_SB_VERSION_HASSHARED(sbp) \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_SHAREDBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDSHARED) +int xfs_sb_version_addshared(xfs_sb_t *sbp); +#define XFS_SB_VERSION_ADDSHARED(sbp) xfs_sb_version_addshared(sbp) +#else +#define XFS_SB_VERSION_ADDSHARED(sbp) \ + ((sbp)->sb_versionnum = \ + ((sbp)->sb_versionnum | XFS_SB_VERSION_SHAREDBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_SUBSHARED) +int xfs_sb_version_subshared(xfs_sb_t *sbp); +#define XFS_SB_VERSION_SUBSHARED(sbp) xfs_sb_version_subshared(sbp) +#else +#define XFS_SB_VERSION_SUBSHARED(sbp) \ + ((sbp)->sb_versionnum = \ + ((sbp)->sb_versionnum & ~XFS_SB_VERSION_SHAREDBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASDIRV2) +int xfs_sb_version_hasdirv2(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASDIRV2(sbp) xfs_sb_version_hasdirv2(sbp) +#else +#define XFS_SB_VERSION_HASDIRV2(sbp) \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_DIRV2BIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_HASEXTFLGBIT) +int xfs_sb_version_hasextflgbit(xfs_sb_t *sbp); +#define XFS_SB_VERSION_HASEXTFLGBIT(sbp) xfs_sb_version_hasextflgbit(sbp) +#else +#define XFS_SB_VERSION_HASEXTFLGBIT(sbp) \ + ((XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \ + ((sbp)->sb_versionnum & XFS_SB_VERSION_EXTFLGBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_ADDEXTFLGBIT) +int xfs_sb_version_addextflgbit(xfs_sb_t *sbp); +#define XFS_SB_VERSION_ADDEXTFLGBIT(sbp) xfs_sb_version_addextflgbit(sbp) +#else +#define XFS_SB_VERSION_ADDEXTFLGBIT(sbp) \ + ((sbp)->sb_versionnum = \ + ((sbp)->sb_versionnum | XFS_SB_VERSION_EXTFLGBIT)) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_VERSION_SUBEXTFLGBIT) +int xfs_sb_version_subextflgbit(xfs_sb_t *sbp); +#define XFS_SB_VERSION_SUBEXTFLGBIT(sbp) xfs_sb_version_subextflgbit(sbp) +#else +#define XFS_SB_VERSION_SUBEXTFLGBIT(sbp) \ + ((sbp)->sb_versionnum = \ + ((sbp)->sb_versionnum & ~XFS_SB_VERSION_EXTFLGBIT)) +#endif + +/* + * end of superblock version macros + */ + +#define XFS_SB_DADDR ((xfs_daddr_t)0) /* daddr in file system/ag */ +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_SB_BLOCK) +xfs_agblock_t xfs_sb_block(struct xfs_mount *mp); +#define XFS_SB_BLOCK(mp) xfs_sb_block(mp) +#else +#define XFS_SB_BLOCK(mp) XFS_HDR_BLOCK(mp, XFS_SB_DADDR) +#endif + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_HDR_BLOCK) +xfs_agblock_t xfs_hdr_block(struct xfs_mount *mp, xfs_daddr_t d); +#define XFS_HDR_BLOCK(mp,d) xfs_hdr_block(mp,d) +#else +#define XFS_HDR_BLOCK(mp,d) ((xfs_agblock_t)(XFS_BB_TO_FSBT(mp,d))) +#endif +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_DADDR_TO_FSB) +xfs_fsblock_t xfs_daddr_to_fsb(struct xfs_mount *mp, xfs_daddr_t d); +#define XFS_DADDR_TO_FSB(mp,d) xfs_daddr_to_fsb(mp,d) +#else +#define XFS_DADDR_TO_FSB(mp,d) \ + XFS_AGB_TO_FSB(mp, XFS_DADDR_TO_AGNO(mp,d), XFS_DADDR_TO_AGBNO(mp,d)) +#endif +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_FSB_TO_DADDR) +xfs_daddr_t xfs_fsb_to_daddr(struct xfs_mount *mp, xfs_fsblock_t fsbno); +#define XFS_FSB_TO_DADDR(mp,fsbno) xfs_fsb_to_daddr(mp,fsbno) +#else +#define XFS_FSB_TO_DADDR(mp,fsbno) \ + XFS_AGB_TO_DADDR(mp, XFS_FSB_TO_AGNO(mp,fsbno), \ + XFS_FSB_TO_AGBNO(mp,fsbno)) +#endif + +/* + * File system block to basic block conversions. + */ +#define XFS_FSB_TO_BB(mp,fsbno) ((fsbno) << (mp)->m_blkbb_log) +#define XFS_BB_TO_FSB(mp,bb) \ + (((bb) + (XFS_FSB_TO_BB(mp,1) - 1)) >> (mp)->m_blkbb_log) +#define XFS_BB_TO_FSBT(mp,bb) ((bb) >> (mp)->m_blkbb_log) +#define XFS_BB_FSB_OFFSET(mp,bb) ((bb) & ((mp)->m_bsize - 1)) + +/* + * File system block to byte conversions. + */ +#define XFS_FSB_TO_B(mp,fsbno) ((xfs_fsize_t)(fsbno) << \ + (mp)->m_sb.sb_blocklog) +#define XFS_B_TO_FSB(mp,b) \ + ((((uint64_t)(b)) + (mp)->m_blockmask) >> (mp)->m_sb.sb_blocklog) +#define XFS_B_TO_FSBT(mp,b) (((uint64_t)(b)) >> (mp)->m_sb.sb_blocklog) +#define XFS_B_FSB_OFFSET(mp,b) ((b) & (mp)->m_blockmask) + +#if XFS_WANT_FUNCS || (XFS_WANT_SPACE && XFSSO_XFS_BUF_TO_SBP) +xfs_sb_t *xfs_buf_to_sbp(struct xfs_buf *bp); +#define XFS_BUF_TO_SBP(bp) xfs_buf_to_sbp(bp) +#else +#define XFS_BUF_TO_SBP(bp) ((xfs_sb_t *)XFS_BUF_PTR(bp)) +#endif + +#endif /* __XFS_SB_H__ */ diff --git a/libparted/fs/xfs/xfs_types.h b/libparted/fs/xfs/xfs_types.h new file mode 100644 index 0000000..89a0d61 --- /dev/null +++ b/libparted/fs/xfs/xfs_types.h @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 3 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ + */ +#ifndef __XFS_TYPES_H__ +#define __XFS_TYPES_H__ + +/* + * Some types are conditional based on the selected configuration. + * Set XFS_BIG_FILES=1 or 0 and XFS_BIG_FILESYSTEMS=1 or 0 depending + * on the desired configuration. + * XFS_BIG_FILES needs pgno_t to be 64 bits (64-bit kernels). + * XFS_BIG_FILESYSTEMS needs daddr_t to be 64 bits (N32 and 64-bit kernels). + * + * Expect these to be set from klocaldefs, or from the machine-type + * defs files for the normal case. + */ + +#define XFS_BIG_FILES 1 +#define XFS_BIG_FILESYSTEMS 1 + +typedef uint32_t xfs_agblock_t; /* blockno in alloc. group */ +typedef uint32_t xfs_extlen_t; /* extent length in blocks */ +typedef uint32_t xfs_agnumber_t; /* allocation group number */ +typedef int32_t xfs_extnum_t; /* # of extents in a file */ +typedef int16_t xfs_aextnum_t; /* # extents in an attribute fork */ +typedef int64_t xfs_fsize_t; /* bytes in a file */ +typedef uint64_t xfs_ufsize_t; /* unsigned bytes in a file */ + +typedef int32_t xfs_suminfo_t; /* type of bitmap summary info */ +typedef int32_t xfs_rtword_t; /* word type for bitmap manipulations */ + +typedef int64_t xfs_lsn_t; /* log sequence number */ +typedef int32_t xfs_tid_t; /* transaction identifier */ + +typedef uint32_t xfs_dablk_t; /* dir/attr block number (in file) */ +typedef uint32_t xfs_dahash_t; /* dir/attr hash value */ + +typedef uint16_t xfs_prid_t; /* prid_t truncated to 16bits in XFS */ + +/* + * These types are 64 bits on disk but are either 32 or 64 bits in memory. + * Disk based types: + */ +typedef uint64_t xfs_dfsbno_t; /* blockno in filesystem (agno|agbno) */ +typedef uint64_t xfs_drfsbno_t; /* blockno in filesystem (raw) */ +typedef uint64_t xfs_drtbno_t; /* extent (block) in realtime area */ +typedef uint64_t xfs_dfiloff_t; /* block number in a file */ +typedef uint64_t xfs_dfilblks_t; /* number of blocks in a file */ + +/* + * Memory based types are conditional. + */ +#if XFS_BIG_FILESYSTEMS +typedef uint64_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */ +typedef uint64_t xfs_rfsblock_t; /* blockno in filesystem (raw) */ +typedef uint64_t xfs_rtblock_t; /* extent (block) in realtime area */ +typedef int64_t xfs_srtblock_t; /* signed version of xfs_rtblock_t */ +#else +typedef uint32_t xfs_fsblock_t; /* blockno in filesystem (agno|agbno) */ +typedef uint32_t xfs_rfsblock_t; /* blockno in filesystem (raw) */ +typedef uint32_t xfs_rtblock_t; /* extent (block) in realtime area */ +typedef int32_t xfs_srtblock_t; /* signed version of xfs_rtblock_t */ +#endif +#if XFS_BIG_FILES +typedef uint64_t xfs_fileoff_t; /* block number in a file */ +typedef int64_t xfs_sfiloff_t; /* signed block number in a file */ +typedef uint64_t xfs_filblks_t; /* number of blocks in a file */ +#else +typedef uint32_t xfs_fileoff_t; /* block number in a file */ +typedef int32_t xfs_sfiloff_t; /* signed block number in a file */ +typedef uint32_t xfs_filblks_t; /* number of blocks in a file */ +#endif + +typedef uint8_t xfs_arch_t; /* architecutre of an xfs fs */ + +/* + * Null values for the types. + */ +#define NULLDFSBNO ((xfs_dfsbno_t)-1) +#define NULLDRFSBNO ((xfs_drfsbno_t)-1) +#define NULLDRTBNO ((xfs_drtbno_t)-1) +#define NULLDFILOFF ((xfs_dfiloff_t)-1) + +#define NULLFSBLOCK ((xfs_fsblock_t)-1) +#define NULLRFSBLOCK ((xfs_rfsblock_t)-1) +#define NULLRTBLOCK ((xfs_rtblock_t)-1) +#define NULLFILEOFF ((xfs_fileoff_t)-1) + +#define NULLAGBLOCK ((xfs_agblock_t)-1) +#define NULLAGNUMBER ((xfs_agnumber_t)-1) +#define NULLEXTNUM ((xfs_extnum_t)-1) + +#define NULLCOMMITLSN ((xfs_lsn_t)-1) + +/* + * Max values for extlen, extnum, aextnum. + */ +#define MAXEXTLEN ((xfs_extlen_t)0x001fffff) /* 21 bits */ +#define MAXEXTNUM ((xfs_extnum_t)0x7fffffff) /* signed int */ +#define MAXAEXTNUM ((xfs_aextnum_t)0x7fff) /* signed short */ + +/* + * MAXNAMELEN is the length (including the terminating null) of + * the longest permissible file (component) name. + */ +#define MAXNAMELEN 256 + +typedef enum { + XFS_LOOKUP_EQi, XFS_LOOKUP_LEi, XFS_LOOKUP_GEi +} xfs_lookup_t; + +typedef enum { + XFS_BTNUM_BNOi, XFS_BTNUM_CNTi, XFS_BTNUM_BMAPi, XFS_BTNUM_INOi, + XFS_BTNUM_MAX +} xfs_btnum_t; + + +#ifdef CONFIG_PROC_FS +/* + * XFS global statistics + */ +struct xfsstats { +# define XFSSTAT_END_EXTENT_ALLOC 4 + uint32_t xs_allocx; + uint32_t xs_allocb; + uint32_t xs_freex; + uint32_t xs_freeb; +# define XFSSTAT_END_ALLOC_BTREE (XFSSTAT_END_EXTENT_ALLOC+4) + uint32_t xs_abt_lookup; + uint32_t xs_abt_compare; + uint32_t xs_abt_insrec; + uint32_t xs_abt_delrec; +# define XFSSTAT_END_BLOCK_MAPPING (XFSSTAT_END_ALLOC_BTREE+7) + uint32_t xs_blk_mapr; + uint32_t xs_blk_mapw; + uint32_t xs_blk_unmap; + uint32_t xs_add_exlist; + uint32_t xs_del_exlist; + uint32_t xs_look_exlist; + uint32_t xs_cmp_exlist; +# define XFSSTAT_END_BLOCK_MAP_BTREE (XFSSTAT_END_BLOCK_MAPPING+4) + uint32_t xs_bmbt_lookup; + uint32_t xs_bmbt_compare; + uint32_t xs_bmbt_insrec; + uint32_t xs_bmbt_delrec; +# define XFSSTAT_END_DIRECTORY_OPS (XFSSTAT_END_BLOCK_MAP_BTREE+4) + uint32_t xs_dir_lookup; + uint32_t xs_dir_create; + uint32_t xs_dir_remove; + uint32_t xs_dir_getdents; +# define XFSSTAT_END_TRANSACTIONS (XFSSTAT_END_DIRECTORY_OPS+3) + uint32_t xs_trans_sync; + uint32_t xs_trans_async; + uint32_t xs_trans_empty; +# define XFSSTAT_END_INODE_OPS (XFSSTAT_END_TRANSACTIONS+7) + uint32_t xs_ig_attempts; + uint32_t xs_ig_found; + uint32_t xs_ig_frecycle; + uint32_t xs_ig_missed; + uint32_t xs_ig_dup; + uint32_t xs_ig_reclaims; + uint32_t xs_ig_attrchg; +# define XFSSTAT_END_LOG_OPS (XFSSTAT_END_INODE_OPS+5) + uint32_t xs_log_writes; + uint32_t xs_log_blocks; + uint32_t xs_log_noiclogs; + uint32_t xs_log_force; + uint32_t xs_log_force_sleep; +# define XFSSTAT_END_TAIL_PUSHING (XFSSTAT_END_LOG_OPS+10) + uint32_t xs_try_logspace; + uint32_t xs_sleep_logspace; + uint32_t xs_push_ail; + uint32_t xs_push_ail_success; + uint32_t xs_push_ail_pushbuf; + uint32_t xs_push_ail_pinned; + uint32_t xs_push_ail_locked; + uint32_t xs_push_ail_flushing; + uint32_t xs_push_ail_restarts; + uint32_t xs_push_ail_flush; +# define XFSSTAT_END_WRITE_CONVERT (XFSSTAT_END_TAIL_PUSHING+2) + uint32_t xs_xstrat_quick; + uint32_t xs_xstrat_split; +# define XFSSTAT_END_READ_WRITE_OPS (XFSSTAT_END_WRITE_CONVERT+2) + uint32_t xs_write_calls; + uint32_t xs_read_calls; +# define XFSSTAT_END_ATTRIBUTE_OPS (XFSSTAT_END_READ_WRITE_OPS+4) + uint32_t xs_attr_get; + uint32_t xs_attr_set; + uint32_t xs_attr_remove; + uint32_t xs_attr_list; +# define XFSSTAT_END_QUOTA_OPS (XFSSTAT_END_ATTRIBUTE_OPS+8) + uint32_t xs_qm_dqreclaims; + uint32_t xs_qm_dqreclaim_misses; + uint32_t xs_qm_dquot_dups; + uint32_t xs_qm_dqcachemisses; + uint32_t xs_qm_dqcachehits; + uint32_t xs_qm_dqwants; + uint32_t xs_qm_dqshake_reclaims; + uint32_t xs_qm_dqinact_reclaims; +# define XFSSTAT_END_INODE_CLUSTER (XFSSTAT_END_QUOTA_OPS+3) + uint32_t xs_iflush_count; + uint32_t xs_icluster_flushcnt; + uint32_t xs_icluster_flushinode; +# define XFSSTAT_END_VNODE_OPS (XFSSTAT_END_INODE_CLUSTER+8) + uint32_t vn_active; /* # vnodes not on free lists */ + uint32_t vn_alloc; /* # times vn_alloc called */ + uint32_t vn_get; /* # times vn_get called */ + uint32_t vn_hold; /* # times vn_hold called */ + uint32_t vn_rele; /* # times vn_rele called */ + uint32_t vn_reclaim; /* # times vn_reclaim called */ + uint32_t vn_remove; /* # times vn_remove called */ + uint32_t vn_free; /* # times vn_free called */ + struct xfsstats_xpc { + uint64_t xs_xstrat_bytes; + uint64_t xs_write_bytes; + uint64_t xs_read_bytes; + } xpc; +} xfsstats; + +# define XFS_STATS_INC(count) ( xfsstats.##count ++ ) +# define XFS_STATS_DEC(count) ( xfsstats.##count -- ) +# define XFS_STATS_ADD(count, inc) ( xfsstats.##count += (inc) ) +# define XFS_STATS64_INC(count) ( xfsstats.xpc.##count ++ ) +# define XFS_STATS64_ADD(count, inc) ( xfsstats.xpc.##count += (inc) ) +#else /* !CONFIG_PROC_FS */ +# define XFS_STATS_INC(count) +# define XFS_STATS_DEC(count) +# define XFS_STATS_ADD(count, inc) +# define XFS_STATS64_INC(count) +# define XFS_STATS64_ADD(count, inc) +#endif /* !CONFIG_PROC_FS */ + + +#ifdef __KERNEL__ + +/* juggle IRIX device numbers - still used in ondisk structures */ + +#define IRIX_DEV_BITSMAJOR 14 +#define IRIX_DEV_BITSMINOR 18 +#define IRIX_DEV_MAXMAJ 0x1ff +#define IRIX_DEV_MAXMIN 0x3ffff +#define IRIX_DEV_MAJOR(dev) ((int)(((unsigned)(dev)>>IRIX_DEV_BITSMINOR) \ + & IRIX_DEV_MAXMAJ)) +#define IRIX_DEV_MINOR(dev) ((int)((dev)&IRIX_DEV_MAXMIN)) +#define IRIX_MKDEV(major,minor) ((xfs_dev_t)(((major)<<IRIX_DEV_BITSMINOR) \ + | (minor&IRIX_DEV_MAXMIN))) + +#define IRIX_DEV_TO_KDEVT(dev) MKDEV(IRIX_DEV_MAJOR(dev),IRIX_DEV_MINOR(dev)) +#define IRIX_DEV_TO_DEVT(dev) ((IRIX_DEV_MAJOR(dev)<<8)|IRIX_DEV_MINOR(dev)) + +/* __psint_t is the same size as a pointer */ +#if (BITS_PER_LONG == 32) +typedef int32_t __psint_t; +typedef uint32_t __psunsigned_t; +#elif (BITS_PER_LONG == 64) +typedef int64_t __psint_t; +typedef uint64_t __psunsigned_t; +#else +#error BITS_PER_LONG must be 32 or 64 +#endif + + +/* + * struct for passing owner/requestor id + */ +typedef struct flid { +#ifdef CELL_CAPABLE + pid_t fl_pid; + sysid_t fl_sysid; +#endif +} flid_t; + +#endif /* __KERNEL__ */ + +#endif /* !__XFS_TYPES_H */ 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); + } +} diff --git a/libparted/libparted.c b/libparted/libparted.c new file mode 100644 index 0000000..204ce00 --- /dev/null +++ b/libparted/libparted.c @@ -0,0 +1,262 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999-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 "configmake.h" + +#include <parted/parted.h> +#include <parted/debug.h> + +#include "architecture.h" + +#if ENABLE_NLS +# include <locale.h> +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +/* ped_malloc() debugging. Stick the address and size of memory blocks that + * weren't free()d in here, and an exception will be thrown when it is + * allocated. That way, you can find out what, exactly, the allocated thing + * is, and where it is created. + */ +typedef struct +{ + void* pointer; + size_t size; +} pointer_size_type; + +/* IMHO, none of the DEBUG-related code below is useful, and the + ped_malloc memset code is actually quite harmful: it masked at + least two nasty bugs that were fixed in June of 2007. */ +#undef DEBUG +#ifdef DEBUG +static pointer_size_type dodgy_malloc_list[] = { + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} +}; + +static int dodgy_memory_active[100]; +#endif /* DEBUG */ + +extern void ped_disk_aix_init (); +extern void ped_disk_bsd_init (); +extern void ped_disk_dvh_init (); +extern void ped_disk_gpt_init (); +extern void ped_disk_loop_init (); +extern void ped_disk_mac_init (); +extern void ped_disk_msdos_init (); +extern void ped_disk_pc98_init (); +extern void ped_disk_sun_init (); +extern void ped_disk_amiga_init (); +extern void ped_disk_dasd_init (); +extern void ped_disk_atari_init (); + +static void +init_disk_types () +{ + /* Note that probing is done in the reverse order of init */ + ped_disk_loop_init (); /* must be last in the probe list */ + +#if defined __s390__ || defined __s390x__ + ped_disk_dasd_init(); +#endif + ped_disk_atari_init (); /* easy false positives, so probe others first */ + ped_disk_sun_init (); +#ifdef ENABLE_PC98 + ped_disk_pc98_init (); +#endif + ped_disk_msdos_init (); + ped_disk_mac_init (); + ped_disk_gpt_init (); + ped_disk_dvh_init (); + ped_disk_bsd_init (); + ped_disk_amiga_init (); + ped_disk_aix_init (); +} + +extern void ped_file_system_amiga_init (void); +extern void ped_file_system_xfs_init (void); +extern void ped_file_system_ufs_init (void); +extern void ped_file_system_reiserfs_init (void); +extern void ped_file_system_ntfs_init (void); +extern void ped_file_system_linux_swap_init (void); +extern void ped_file_system_jfs_init (void); +extern void ped_file_system_hfs_init (void); +extern void ped_file_system_fat_init (void); +extern void ped_file_system_f2fs_init (void); +extern void ped_file_system_ext2_init (void); +extern void ped_file_system_nilfs2_init (void); +extern void ped_file_system_btrfs_init (void); +extern void ped_file_system_udf_init (void); + +static void +init_file_system_types () +{ + ped_file_system_amiga_init (); + ped_file_system_xfs_init (); + ped_file_system_ufs_init (); + ped_file_system_reiserfs_init (); + ped_file_system_ntfs_init (); + ped_file_system_linux_swap_init (); + ped_file_system_jfs_init (); + ped_file_system_hfs_init (); + ped_file_system_fat_init (); + ped_file_system_f2fs_init (); + ped_file_system_ext2_init (); + ped_file_system_nilfs2_init (); + ped_file_system_btrfs_init (); + ped_file_system_udf_init (); +} + +extern void ped_disk_aix_done (); +extern void ped_disk_bsd_done (); +extern void ped_disk_dvh_done (); +extern void ped_disk_gpt_done (); +extern void ped_disk_loop_done (); +extern void ped_disk_mac_done (); +extern void ped_disk_msdos_done (); +extern void ped_disk_pc98_done (); +extern void ped_disk_sun_done (); +extern void ped_disk_amiga_done (); +extern void ped_disk_dasd_done (); +extern void ped_disk_atari_done (); + +static void +done_disk_types () +{ +#if defined __s390__ || __s390x__ + ped_disk_dasd_done (); +#endif + ped_disk_sun_done (); +#ifdef ENABLE_PC98 + ped_disk_pc98_done (); +#endif + ped_disk_msdos_done (); + ped_disk_mac_done (); + ped_disk_loop_done (); + ped_disk_gpt_done (); + ped_disk_dvh_done (); + ped_disk_bsd_done (); + ped_disk_amiga_done (); + ped_disk_aix_done (); + ped_disk_atari_done (); +} + +static void _init() __attribute__ ((constructor)); + +static void +_init() +{ +#ifdef ENABLE_NLS + bindtextdomain (PACKAGE, LOCALEDIR); +#endif + + init_disk_types (); + init_file_system_types (); + ped_set_architecture (); +#ifdef DEBUG + memset (dodgy_memory_active, 0, sizeof (dodgy_memory_active)); +#endif +} + +extern void ped_file_system_nilfs2_done (void); +extern void ped_file_system_ext2_done (void); +extern void ped_file_system_fat_done (void); +extern void ped_file_system_f2fs_done (void); +extern void ped_file_system_hfs_done (void); +extern void ped_file_system_jfs_done (void); +extern void ped_file_system_linux_swap_done (void); +extern void ped_file_system_ntfs_done (void); +extern void ped_file_system_reiserfs_done (void); +extern void ped_file_system_ufs_done (void); +extern void ped_file_system_xfs_done (void); +extern void ped_file_system_amiga_done (void); +extern void ped_file_system_btrfs_done (void); +extern void ped_file_system_udf_done (void); + +static void +done_file_system_types () +{ + ped_file_system_nilfs2_done (); + ped_file_system_ext2_done (); + ped_file_system_f2fs_done (); + ped_file_system_fat_done (); + ped_file_system_hfs_done (); + ped_file_system_jfs_done (); + ped_file_system_linux_swap_done (); + ped_file_system_ntfs_done (); + ped_file_system_reiserfs_done (); + ped_file_system_ufs_done (); + ped_file_system_xfs_done (); + ped_file_system_amiga_done (); + ped_file_system_btrfs_done (); + ped_file_system_udf_done (); +} + +static void _done() __attribute__ ((destructor)); + +static void +_done() +{ + ped_device_free_all (); + done_disk_types (); + done_file_system_types (); +} + +const char* +ped_get_version () +{ + return VERSION; +} + +void* +ped_malloc (size_t size) +{ + void* mem; + + mem = (void*) malloc (size); + if (!mem) { + ped_exception_throw (PED_EXCEPTION_FATAL, PED_EXCEPTION_CANCEL, + _("Out of memory.")); + return NULL; + } + + return mem; +} + + +void* ped_calloc (size_t size) +{ + void* buf = ped_malloc (size); + + memset (buf, 0, size); + + return buf; +} diff --git a/libparted/mbr.s b/libparted/mbr.s new file mode 100644 index 0000000..8ac2240 --- /dev/null +++ b/libparted/mbr.s @@ -0,0 +1,85 @@ +; libparted - a library for manipulating disk partitions +; Copyright (C) 1999-2000, 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/>. + +; NOTE: I build this with: +; $ as86 -b /dev/stdout mbr.s | hexdump -e '8/1 "0x%02x, " "\n"' +; +; The build isn't done automagically by make, because as86 may not be on many +; machines (particularly non-x86). Also, it seems rather difficult to get +; as86 to build object files that can be linked, especially as it's 16 bit +; code... + +USE16 + +; This code, plus the partition table is loaded into 0000:7C00 by the BIOS + +.text + +; set top of stack to 1000:B000 + + cli + + mov ax, #0x1000 + mov ss, ax + mov sp, #0xB000 + + mov ax, #0x0000 + mov ds, ax + mov es, ax + + sti + +; Copy what the BIOS loaded (i.e. the MBR + head of partition table) from +; 0000:7c00 to 0000:0600 + + mov si, #0x7c00 + mov di, #0x0600 + mov cx, #0x200 + rep + movsb + +; Jump to the copy of the MBR + + jmp 0x0000:find_boot_partition + 0x0600 + +find_boot_partition: + mov si, #0x07BE + +check_next_bootable: + cmp [si], al + jnz found_bootable + add si, #0x0010 + cmp si, #0x07FE + jnz check_next_bootable + jmp error + +found_bootable: + +; Load in the boot sector at 0000:7c00 + + mov ah, #2 ; BIOS command (read) + mov al, #1 ; count + mov bx, #0x7c00 ; destination pointer + mov dl, #0x80 ; drive + mov dh, byte ptr [si + 1] ; head + mov cx, word ptr [si + 2] ; sector / cylinder + int #0x13 ; BIOS read interrupt + + jmp 0x0000:0x7c00 ; hand control to boot sector + +error: + jmp error diff --git a/libparted/tests/Makefile.am b/libparted/tests/Makefile.am new file mode 100644 index 0000000..1097743 --- /dev/null +++ b/libparted/tests/Makefile.am @@ -0,0 +1,40 @@ +# 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. + +TESTS = t1000-label.sh t1001-flags.sh t2000-disk.sh t2100-zerolen.sh \ + t3000-symlink.sh t4000-volser.sh +EXTRA_DIST = $(TESTS) +check_PROGRAMS = label disk zerolen symlink volser flags +AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS) + +LDADD = \ + $(top_builddir)/libparted/libparted.la \ + $(CHECK_LIBS) \ + -lpthread + +AM_CPPFLAGS = \ + $(CHECK_CFLAGS) \ + -I$(top_srcdir)/lib \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/include + +label_SOURCES = common.h common.c label.c +disk_SOURCES = common.h common.c disk.c +zerolen_SOURCES = common.h common.c zerolen.c +symlink_SOURCES = common.h common.c symlink.c +volser_SOURCES = common.h common.c volser.c +flags_SOURCES = common.h common.c flags.c + +# Arrange to symlink to tests/init.sh. +CLEANFILES = init.sh +.PHONY: prereq +prereq: + $(AM_V_GEN)ln -sf $(abs_top_srcdir)/tests/init.sh . +$(TEST_LOGS): prereq + +TESTS_ENVIRONMENT = \ + top_srcdir='$(top_srcdir)' \ + abs_top_srcdir='$(abs_top_srcdir)' \ + ENABLE_DEVICE_MAPPER=$(ENABLE_DEVICE_MAPPER) diff --git a/libparted/tests/Makefile.in b/libparted/tests/Makefile.in new file mode 100644 index 0000000..48fd115 --- /dev/null +++ b/libparted/tests/Makefile.in @@ -0,0 +1,2464 @@ +# 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@ +check_PROGRAMS = label$(EXEEXT) disk$(EXEEXT) zerolen$(EXEEXT) \ + symlink$(EXEEXT) volser$(EXEEXT) flags$(EXEEXT) +subdir = libparted/tests +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 = +am_disk_OBJECTS = common.$(OBJEXT) disk.$(OBJEXT) +disk_OBJECTS = $(am_disk_OBJECTS) +disk_LDADD = $(LDADD) +am__DEPENDENCIES_1 = +disk_DEPENDENCIES = $(top_builddir)/libparted/libparted.la \ + $(am__DEPENDENCIES_1) +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_flags_OBJECTS = common.$(OBJEXT) flags.$(OBJEXT) +flags_OBJECTS = $(am_flags_OBJECTS) +flags_LDADD = $(LDADD) +flags_DEPENDENCIES = $(top_builddir)/libparted/libparted.la \ + $(am__DEPENDENCIES_1) +am_label_OBJECTS = common.$(OBJEXT) label.$(OBJEXT) +label_OBJECTS = $(am_label_OBJECTS) +label_LDADD = $(LDADD) +label_DEPENDENCIES = $(top_builddir)/libparted/libparted.la \ + $(am__DEPENDENCIES_1) +am_symlink_OBJECTS = common.$(OBJEXT) symlink.$(OBJEXT) +symlink_OBJECTS = $(am_symlink_OBJECTS) +symlink_LDADD = $(LDADD) +symlink_DEPENDENCIES = $(top_builddir)/libparted/libparted.la \ + $(am__DEPENDENCIES_1) +am_volser_OBJECTS = common.$(OBJEXT) volser.$(OBJEXT) +volser_OBJECTS = $(am_volser_OBJECTS) +volser_LDADD = $(LDADD) +volser_DEPENDENCIES = $(top_builddir)/libparted/libparted.la \ + $(am__DEPENDENCIES_1) +am_zerolen_OBJECTS = common.$(OBJEXT) zerolen.$(OBJEXT) +zerolen_OBJECTS = $(am_zerolen_OBJECTS) +zerolen_LDADD = $(LDADD) +zerolen_DEPENDENCIES = $(top_builddir)/libparted/libparted.la \ + $(am__DEPENDENCIES_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)/common.Po ./$(DEPDIR)/disk.Po \ + ./$(DEPDIR)/flags.Po ./$(DEPDIR)/label.Po \ + ./$(DEPDIR)/symlink.Po ./$(DEPDIR)/volser.Po \ + ./$(DEPDIR)/zerolen.Po +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 = $(disk_SOURCES) $(flags_SOURCES) $(label_SOURCES) \ + $(symlink_SOURCES) $(volser_SOURCES) $(zerolen_SOURCES) +DIST_SOURCES = $(disk_SOURCES) $(flags_SOURCES) $(label_SOURCES) \ + $(symlink_SOURCES) $(volser_SOURCES) $(zerolen_SOURCES) +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__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/build-aux/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(top_srcdir)/build-aux/depcomp \ + $(top_srcdir)/build-aux/test-driver +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@ +TESTS = t1000-label.sh t1001-flags.sh t2000-disk.sh t2100-zerolen.sh \ + t3000-symlink.sh t4000-volser.sh + +EXTRA_DIST = $(TESTS) +AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS) +LDADD = \ + $(top_builddir)/libparted/libparted.la \ + $(CHECK_LIBS) \ + -lpthread + +AM_CPPFLAGS = \ + $(CHECK_CFLAGS) \ + -I$(top_srcdir)/lib \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/include + +label_SOURCES = common.h common.c label.c +disk_SOURCES = common.h common.c disk.c +zerolen_SOURCES = common.h common.c zerolen.c +symlink_SOURCES = common.h common.c symlink.c +volser_SOURCES = common.h common.c volser.c +flags_SOURCES = common.h common.c flags.c + +# Arrange to symlink to tests/init.sh. +CLEANFILES = init.sh +TESTS_ENVIRONMENT = \ + top_srcdir='$(top_srcdir)' \ + abs_top_srcdir='$(abs_top_srcdir)' \ + ENABLE_DEVICE_MAPPER=$(ENABLE_DEVICE_MAPPER) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs +$(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/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu libparted/tests/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-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +disk$(EXEEXT): $(disk_OBJECTS) $(disk_DEPENDENCIES) $(EXTRA_disk_DEPENDENCIES) + @rm -f disk$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(disk_OBJECTS) $(disk_LDADD) $(LIBS) + +flags$(EXEEXT): $(flags_OBJECTS) $(flags_DEPENDENCIES) $(EXTRA_flags_DEPENDENCIES) + @rm -f flags$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(flags_OBJECTS) $(flags_LDADD) $(LIBS) + +label$(EXEEXT): $(label_OBJECTS) $(label_DEPENDENCIES) $(EXTRA_label_DEPENDENCIES) + @rm -f label$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(label_OBJECTS) $(label_LDADD) $(LIBS) + +symlink$(EXEEXT): $(symlink_OBJECTS) $(symlink_DEPENDENCIES) $(EXTRA_symlink_DEPENDENCIES) + @rm -f symlink$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(symlink_OBJECTS) $(symlink_LDADD) $(LIBS) + +volser$(EXEEXT): $(volser_OBJECTS) $(volser_DEPENDENCIES) $(EXTRA_volser_DEPENDENCIES) + @rm -f volser$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(volser_OBJECTS) $(volser_LDADD) $(LIBS) + +zerolen$(EXEEXT): $(zerolen_OBJECTS) $(zerolen_DEPENDENCIES) $(EXTRA_zerolen_DEPENDENCIES) + @rm -f zerolen$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(zerolen_OBJECTS) $(zerolen_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disk.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flags.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/label.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symlink.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/volser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zerolen.Po@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 + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: $(check_PROGRAMS) + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all $(check_PROGRAMS) + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +t1000-label.sh.log: t1000-label.sh + @p='t1000-label.sh'; \ + b='t1000-label.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +t1001-flags.sh.log: t1001-flags.sh + @p='t1001-flags.sh'; \ + b='t1001-flags.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +t2000-disk.sh.log: t2000-disk.sh + @p='t2000-disk.sh'; \ + b='t2000-disk.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +t2100-zerolen.sh.log: t2100-zerolen.sh + @p='t2100-zerolen.sh'; \ + b='t2100-zerolen.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +t3000-symlink.sh.log: t3000-symlink.sh + @p='t3000-symlink.sh'; \ + b='t3000-symlink.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +t4000-volser.sh.log: t4000-volser.sh + @p='t4000-volser.sh'; \ + b='t4000-volser.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) +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 + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: 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: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +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." +clean: clean-am + +clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/common.Po + -rm -f ./$(DEPDIR)/disk.Po + -rm -f ./$(DEPDIR)/flags.Po + -rm -f ./$(DEPDIR)/label.Po + -rm -f ./$(DEPDIR)/symlink.Po + -rm -f ./$(DEPDIR)/volser.Po + -rm -f ./$(DEPDIR)/zerolen.Po + -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)/common.Po + -rm -f ./$(DEPDIR)/disk.Po + -rm -f ./$(DEPDIR)/flags.Po + -rm -f ./$(DEPDIR)/label.Po + -rm -f ./$(DEPDIR)/symlink.Po + -rm -f ./$(DEPDIR)/volser.Po + -rm -f ./$(DEPDIR)/zerolen.Po + -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: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \ + check-am clean clean-checkPROGRAMS clean-generic clean-libtool \ + 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 \ + recheck tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + +.PHONY: prereq +prereq: + $(AM_V_GEN)ln -sf $(abs_top_srcdir)/tests/init.sh . +$(TEST_LOGS): prereq + +# 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/tests/common.c b/libparted/tests/common.c new file mode 100644 index 0000000..8c42ece --- /dev/null +++ b/libparted/tests/common.c @@ -0,0 +1,94 @@ +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <string.h> + +#include <check.h> + +#include "common.h" +#include "xstrtol.h" + +#define STREQ(a, b) (strcmp (a, b) == 0) + +size_t get_sector_size (void) +{ + char *p = getenv ("PARTED_SECTOR_SIZE"); + size_t ss = 512; + unsigned long val; + if (p + && xstrtoul (p, NULL, 10, &val, NULL) == LONGINT_OK + && val % 512 == 0) + ss = val; + + return ss; +} + +PedExceptionOption +_test_exception_handler (PedException* e) +{ + ck_abort_msg("Exception of type %s has been raised: %s", + ped_exception_get_type_string (e->type), + e->message); + + return PED_EXCEPTION_UNHANDLED; +} + +char* +_create_disk (const off_t n_bytes) +{ + char* filename = strdup ("parted-test-XXXXXX"); + + if (filename == NULL) + return NULL; + + int fd = mkstemp (filename); + if (fd < 0) { + free_filename: + free (filename); + return NULL; + } + + FILE* disk = fdopen (fd, "w"); + if (disk == NULL) + goto free_filename; + + int fail = (fseek (disk, n_bytes, SEEK_SET) != 0 + || fwrite ("", sizeof (char), 1, disk) != 1); + + if (fclose (disk) != 0 || fail) + goto free_filename; + + return filename; +} + +PedDisk* +_create_disk_label (PedDevice *dev, PedDiskType *type) +{ + PedDisk* disk = NULL; + + /* Create the label */ + disk = ped_disk_new_fresh (dev, type); + ck_assert_msg(disk != NULL, "Failed to create a label of type: %s", + type->name); + ck_assert_msg(ped_disk_commit(disk) != 0, + "Failed to commit label to device"); + + return disk; +} + +int +_implemented_disk_label (const char *label) +{ + /* FIXME: these have minor problems, so skip them, temporarily. */ + if (STREQ (label, "amiga")) return 0; + + if (STREQ (label, "atari") && get_sector_size() != 512) + return 0; + + /* Not implemented yet */ + if (STREQ (label, "aix")) return 0; + if (STREQ (label, "pc98")) return 0; + + return 1; +} diff --git a/libparted/tests/common.h b/libparted/tests/common.h new file mode 100644 index 0000000..5d7485e --- /dev/null +++ b/libparted/tests/common.h @@ -0,0 +1,31 @@ +#include <parted/parted.h> + +/* Determine sector size from environment + * + */ +size_t get_sector_size (void); + +/* Create an empty disk image + * + * filename: file (with full path) where to write the disk image + * size: size of disk image (megabytes) + */ +char* _create_disk (const off_t size); + +/* Create a disk label + * + * dev: device to use when creating the label + * type: label type + */ +PedDisk* _create_disk_label (PedDevice* dev, PedDiskType* type); + +/* Return if a disk label is implemented + * + * label: disk label name + */ +int _implemented_disk_label (const char* label) _GL_ATTRIBUTE_PURE; + +/* Test specific exception handler + * + */ +PedExceptionOption _test_exception_handler (PedException* e); diff --git a/libparted/tests/disk.c b/libparted/tests/disk.c new file mode 100644 index 0000000..a2e304c --- /dev/null +++ b/libparted/tests/disk.c @@ -0,0 +1,112 @@ +#include <config.h> +#include <unistd.h> + +#include <check.h> + +#include <parted/parted.h> + +#include "common.h" +#include "progname.h" + +static char* temporary_disk; + +static void +create_disk (void) +{ + temporary_disk = _create_disk (get_sector_size () * 4 * 10 * 1024); + ck_assert_msg(temporary_disk != NULL, "Failed to create temporary disk"); +} + +static void +destroy_disk (void) +{ + unlink (temporary_disk); + free (temporary_disk); +} + +/* TEST: Create a disklabel on a simple disk image */ +START_TEST (test_duplicate) +{ + PedDevice* dev = ped_device_get (temporary_disk); + if (dev == NULL) + return; + + PedDisk* disk; + PedDisk* disk_dup; + PedPartition *part; + PedPartition *part_dup; + PedConstraint *constraint; + + int part_num[] = {1, 5, 6, 0}; + + disk = _create_disk_label (dev, ped_disk_type_get ("msdos")); + + constraint = ped_constraint_any (dev); + + /* Primary partition from 16,4kB to 15MB */ + part = ped_partition_new (disk, PED_PARTITION_EXTENDED, + NULL, + 32, 29311); + ped_disk_add_partition (disk, part, constraint); + + /* Logical partition from 10MB to 15MB */ + part = ped_partition_new (disk, PED_PARTITION_LOGICAL, + ped_file_system_type_get ("ext2"), + 19584, 29311); + ped_disk_add_partition (disk, part, constraint); + + /* Logical partition from 16,4kB to 4981kB */ + part = ped_partition_new (disk, PED_PARTITION_LOGICAL, + ped_file_system_type_get ("ext2"), + 32, 9727); + ped_disk_add_partition (disk, part, constraint); + + ped_disk_commit (disk); + + ped_constraint_destroy (constraint); + + disk_dup = ped_disk_duplicate (disk); + + /* Checks if both partitions match */ + for (int *i = part_num; *i != 0; i++) { + part = ped_disk_get_partition (disk, *i); + part_dup = ped_disk_get_partition (disk_dup, *i); + + ck_assert_msg(part->geom.start == part_dup->geom.start && + part->geom.end == part_dup->geom.end, + "Duplicated partition %d doesn't match. " + "Details are start: %lld/%lld end: %lld/%lld\n", + *i, part->geom.start, part_dup->geom.start, + part->geom.end, part_dup->geom.end); + } + + ped_disk_destroy (disk); + ped_device_destroy (dev); +} +END_TEST + +int +main (int argc, char **argv) +{ + set_program_name (argv[0]); + int number_failed; + Suite* suite = suite_create ("Disk"); + TCase* tcase_duplicate = tcase_create ("Duplicate"); + + /* Fail when an exception is raised */ + ped_exception_set_handler (_test_exception_handler); + + tcase_add_checked_fixture (tcase_duplicate, create_disk, destroy_disk); + tcase_add_test (tcase_duplicate, test_duplicate); + /* Disable timeout for this test */ + tcase_set_timeout (tcase_duplicate, 0); + suite_add_tcase (suite, tcase_duplicate); + + SRunner* srunner = srunner_create (suite); + srunner_run_all (srunner, CK_VERBOSE); + + number_failed = srunner_ntests_failed (srunner); + srunner_free (srunner); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/libparted/tests/flags.c b/libparted/tests/flags.c new file mode 100644 index 0000000..ff4ae71 --- /dev/null +++ b/libparted/tests/flags.c @@ -0,0 +1,116 @@ +#include <config.h> +#include <unistd.h> + +#include <check.h> + +#include <parted/parted.h> + +#include "common.h" +#include "progname.h" + +#define STREQ(a, b) (strcmp (a, b) == 0) + +static char* temporary_disk; + +static void +create_disk (void) +{ + temporary_disk = _create_disk (80 * 1024 * 1024); + ck_assert_msg(temporary_disk != NULL, "Failed to create temporary disk"); +} + +static void +destroy_disk (void) +{ + unlink (temporary_disk); + free (temporary_disk); +} + +/* TEST: Test partition type flag on gpt disklabel */ +START_TEST (test_gpt_flag) +{ + PedDevice* dev = ped_device_get (temporary_disk); + if (dev == NULL) + return; + + PedDisk* disk = ped_disk_new_fresh (dev, ped_disk_type_get ("gpt")); + PedConstraint *constraint = ped_constraint_any (dev); + PedPartition *part = ped_partition_new (disk, PED_PARTITION_NORMAL, + ped_file_system_type_get("ext4"), 2048, 4096); + ped_partition_set_flag(part, PED_PARTITION_BIOS_GRUB, 1); + // Type should remain set to BIOS_GRUB + ped_partition_set_system(part, ped_file_system_type_get("ext4")); + + ped_disk_add_partition (disk, part, constraint); + ped_disk_commit (disk); + ped_constraint_destroy (constraint); + + // Check flag to confirm it is still set + part = ped_disk_get_partition (disk, 1); + ck_assert_msg(ped_partition_get_flag(part, PED_PARTITION_BIOS_GRUB) == 1, "BIOS_GRUB flag not set"); + + ped_disk_destroy (disk); + ped_device_destroy (dev); +} +END_TEST + +/* TEST: Test partition type flag on msdos disklabel */ +START_TEST (test_msdos_flag) +{ + PedDevice* dev = ped_device_get (temporary_disk); + if (dev == NULL) + return; + + PedDisk* disk = ped_disk_new_fresh (dev, ped_disk_type_get ("msdos")); + PedConstraint *constraint = ped_constraint_any (dev); + PedPartition *part = ped_partition_new (disk, PED_PARTITION_NORMAL, + ped_file_system_type_get("ext4"), 2048, 4096); + ped_partition_set_flag(part, PED_PARTITION_BLS_BOOT, 1); + // Type should remain set to BIOS_GRUB + ped_partition_set_system(part, ped_file_system_type_get("ext4")); + + ped_disk_add_partition (disk, part, constraint); + ped_disk_commit (disk); + ped_constraint_destroy (constraint); + + // Check flag to confirm it is still set + part = ped_disk_get_partition (disk, 1); + ck_assert_msg(ped_partition_get_flag(part, PED_PARTITION_BLS_BOOT) == 1, "BLS_BOOT flag not set"); + + ped_disk_destroy (disk); + ped_device_destroy (dev); +} +END_TEST + +int +main (int argc, char **argv) +{ + set_program_name (argv[0]); + int number_failed; + Suite* suite = suite_create ("Partition Flags"); + TCase* tcase_gpt = tcase_create ("GPT"); + TCase* tcase_msdos = tcase_create ("MSDOS"); + + /* Fail when an exception is raised */ + ped_exception_set_handler (_test_exception_handler); + + tcase_add_checked_fixture (tcase_gpt, create_disk, destroy_disk); + tcase_add_test (tcase_gpt, test_gpt_flag); + /* Disable timeout for this test */ + tcase_set_timeout (tcase_gpt, 0); + suite_add_tcase (suite, tcase_gpt); + + tcase_add_checked_fixture (tcase_msdos, create_disk, destroy_disk); + tcase_add_test (tcase_msdos, test_msdos_flag); + /* Disable timeout for this test */ + tcase_set_timeout (tcase_msdos, 0); + suite_add_tcase (suite, tcase_msdos); + + SRunner* srunner = srunner_create (suite); + srunner_run_all (srunner, CK_VERBOSE); + + number_failed = srunner_ntests_failed (srunner); + srunner_free (srunner); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/libparted/tests/label.c b/libparted/tests/label.c new file mode 100644 index 0000000..67b1b07 --- /dev/null +++ b/libparted/tests/label.c @@ -0,0 +1,195 @@ +#include <config.h> +#include <unistd.h> + +#include <check.h> + +#include <parted/parted.h> + +#include "common.h" +#include "progname.h" + +#define STREQ(a, b) (strcmp (a, b) == 0) + +static char* temporary_disk; + +static void +create_disk (void) +{ + temporary_disk = _create_disk (80 * 1024 * 1024); + ck_assert_msg(temporary_disk != NULL, "Failed to create temporary disk"); +} + +static void +destroy_disk (void) +{ + unlink (temporary_disk); + free (temporary_disk); +} + +/* TEST: Create a disklabel on a simple disk image */ +START_TEST (test_create_label) +{ + PedDevice* dev = ped_device_get (temporary_disk); + if (dev == NULL) + return; + + PedDiskType* type; + PedDisk* disk; + + for (type = ped_disk_type_get_next (NULL); type; + type = ped_disk_type_get_next (type)) { + fprintf (stderr, "create label: %s\n", type->name); fflush (stderr); + + if (!_implemented_disk_label (type->name)) + continue; + + disk = _create_disk_label (dev, type); + ped_disk_destroy (disk); + } + ped_device_destroy (dev); +} +END_TEST + +/* TEST: Probe the disk label of a loop device. */ +START_TEST (test_probe_label) +{ + PedDevice* dev = ped_device_get (temporary_disk); + if (dev == NULL) + return; + + PedDiskType* probed; + PedDiskType* type; + PedDisk* disk; + + for (type = ped_disk_type_get_next (NULL); type; + type = ped_disk_type_get_next (type)) { + fprintf (stderr, "PROBE label: %s\n", type->name); fflush (stderr); + if (!_implemented_disk_label (type->name)) + continue; + + disk = _create_disk_label (dev, type); + ped_disk_destroy (disk); + + /* Try to probe the disk label. */ + probed = ped_disk_probe (dev); + ck_assert_msg(probed, + "Failed to probe the just created label of type: %s", + type->name); + if (probed && !STREQ (probed->name, type->name)) + ck_abort_msg("Probe returned label of type: %s as type: %s", + type->name, probed->name); + } + ped_device_destroy (dev); +} +END_TEST + +/* TEST: Read the disk label of a loop device. */ +START_TEST (test_read_label) +{ + PedDevice* dev = ped_device_get (temporary_disk); + if (dev == NULL) + return; + + PedDiskType* type; + PedDisk* disk; + + for (type = ped_disk_type_get_next (NULL); type; + type = ped_disk_type_get_next (type)) { + fprintf (stderr, "read label: %s\n", type->name); fflush (stderr); + if (!_implemented_disk_label (type->name)) + continue; + + disk = _create_disk_label (dev, type); + ped_disk_destroy (disk); + + /* Try to read the disk label. */ + disk = ped_disk_new (dev); + ck_assert_msg(disk, + "Failed to read the just created label of type: %s", + type->name); + if (disk && !STREQ (disk->type->name, type->name)) + ck_abort_msg("Read returned label of type: %s as type: %s", + type->name, disk->type->name); + + ped_disk_destroy (disk); + } + ped_device_destroy (dev); +} +END_TEST + +/* TEST: Clone the disk label of a loop device. */ +START_TEST (test_clone_label) +{ + PedDevice* dev = ped_device_get (temporary_disk); + if (dev == NULL) + return; + + PedDiskType* type; + + for (type = ped_disk_type_get_next (NULL); type; + type = ped_disk_type_get_next (type)) { + fprintf (stderr, "clone label: %s\n", type->name); fflush (stderr); + if (!_implemented_disk_label (type->name)) + continue; + + PedDisk* disk = _create_disk_label (dev, type); + + /* Try to clone the disk label. */ + PedDisk* clone = ped_disk_duplicate (disk); + ck_assert_msg(clone, + "Failed to clone the just created label of type: %s", + type->name); + + ped_disk_destroy (clone); + ped_disk_destroy (disk); + } + ped_device_destroy (dev); +} +END_TEST + +int +main (int argc, char **argv) +{ + set_program_name (argv[0]); + int number_failed; + Suite* suite = suite_create ("Disk Label"); + TCase* tcase_basic = tcase_create ("Create"); + TCase* tcase_probe = tcase_create ("Probe"); + TCase* tcase_read = tcase_create ("Read"); + TCase* tcase_clone = tcase_create ("Clone"); + + /* Fail when an exception is raised */ + ped_exception_set_handler (_test_exception_handler); + + tcase_add_checked_fixture (tcase_basic, create_disk, destroy_disk); + tcase_add_test (tcase_basic, test_create_label); + /* Disable timeout for this test */ + tcase_set_timeout (tcase_basic, 0); + suite_add_tcase (suite, tcase_basic); + + tcase_add_checked_fixture (tcase_probe, create_disk, destroy_disk); + tcase_add_test (tcase_probe, test_probe_label); + /* Disable timeout for this test. */ + tcase_set_timeout (tcase_probe, 0); + suite_add_tcase (suite, tcase_probe); + + tcase_add_checked_fixture (tcase_read, create_disk, destroy_disk); + tcase_add_test (tcase_read, test_read_label); + /* Disable timeout for this test. */ + tcase_set_timeout (tcase_read, 0); + suite_add_tcase (suite, tcase_read); + + tcase_add_checked_fixture (tcase_clone, create_disk, destroy_disk); + tcase_add_test (tcase_clone, test_clone_label); + /* Disable timeout for this test. */ + tcase_set_timeout (tcase_clone, 0); + suite_add_tcase (suite, tcase_clone); + + SRunner* srunner = srunner_create (suite); + srunner_run_all (srunner, CK_VERBOSE); + + number_failed = srunner_ntests_failed (srunner); + srunner_free (srunner); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/libparted/tests/symlink.c b/libparted/tests/symlink.c new file mode 100644 index 0000000..7be02cd --- /dev/null +++ b/libparted/tests/symlink.c @@ -0,0 +1,146 @@ +/* Sometimes libparted operates on device mapper files, with a path of + /dev/mapper/foo. With newer lvm versions /dev/mapper/foo is a symlink to + /dev/dm-#. However some storage administration programs (anaconda for + example) may do the following: + 1) Create a ped_device for /dev/mapper/foo + 2) ped_get_device resolves the symlink to /dev/dm-#, and the path + in the PedDevice struct points to /dev/dm-# + 3) The program does some things to lvm, causing the symlink to + point to a different /dev/dm-# node + 4) The program does something with the PedDevice, which results + in an operation on the wrong device + + Newer libparted versions do not suffer from this problem, as they + do not canonicalize device names under /dev/mapper. This test checks + for this bug. */ + +#include <config.h> +#include <unistd.h> + +#include <check.h> + +#include <parted/parted.h> + +#include "common.h" +#include "progname.h" + +static char *temporary_disk; + +static void +create_disk (void) +{ + temporary_disk = _create_disk (4096 * 1024); + ck_assert_msg(temporary_disk != NULL, "Failed to create temporary disk"); +} + +static void +destroy_disk (void) +{ + unlink (temporary_disk); + free (temporary_disk); +} + +START_TEST (test_symlink) +{ + char cwd[256], ln[256] = "/dev/mapper/parted-test-XXXXXX"; + + if (!getcwd (cwd, sizeof cwd)) { + ck_abort_msg("Could not get cwd"); + return; + } + + /* Create a symlink under /dev/mapper to our + temporary disk */ + int tmp_fd = mkstemp (ln); + if (tmp_fd == -1) { + ck_abort_msg("Could not create tempfile"); + return; + } + + /* There is a temp file vulnerability symlink attack possibility + here, but as /dev/mapper is root owned this is a non issue */ + close (tmp_fd); + unlink (ln); + char temp_disk_path[259]; + int r = snprintf(temp_disk_path, sizeof temp_disk_path, "%s/%s", + cwd, + temporary_disk); + if (r < 0 || r >= sizeof temp_disk_path) { + ck_abort_msg("symlink truncated"); + return; + } + + int res = symlink (temp_disk_path, ln); + if (res) { + ck_abort_msg("could not create symlink"); + return; + } + + PedDevice *dev = ped_device_get (ln); + if (dev == NULL) + goto exit_unlink_ln; + + /* Create a second temporary_disk */ + char *temporary_disk2 = _create_disk (4096 * 1024); + if (temporary_disk2 == NULL) { + ck_abort_msg("Failed to create 2nd temporary disk"); + goto exit_destroy_dev; + } + + /* Remove first temporary disk, and make temporary_disk point to + temporary_disk2 (for destroy_disk()). */ + unlink (temporary_disk); + free (temporary_disk); + temporary_disk = temporary_disk2; + + /* Update symlink to point to our new / second temporary disk */ + unlink (ln); + r = snprintf (temp_disk_path, sizeof temp_disk_path, "%s/%s", + cwd, temporary_disk); + if (r < 0 || r >= sizeof temp_disk_path) { + ck_abort_msg("2nd symlink truncated"); + goto exit_destroy_dev; + } + + res = symlink (temp_disk_path, ln); + if (res) { + ck_abort_msg("could not create 2nd symlink"); + goto exit_destroy_dev; + } + + /* Do something to our PedDevice, if the symlink was resolved, + instead of remembering the /dev/mapper/foo name, this will fail */ + ped_disk_clobber (dev); + +exit_destroy_dev: + ped_device_destroy (dev); +exit_unlink_ln: + unlink (ln); +} +END_TEST + +int +main (int argc, char **argv) +{ + set_program_name (argv[0]); + int number_failed; + Suite* suite = suite_create ("Symlink"); + TCase* tcase_symlink = tcase_create ("/dev/mapper symlink"); + + /* Fail when an exception is raised */ + ped_exception_set_handler (_test_exception_handler); + + tcase_add_checked_fixture (tcase_symlink, create_disk, destroy_disk); + tcase_add_test (tcase_symlink, test_symlink); + /* Disable timeout for this test */ + tcase_set_timeout (tcase_symlink, 0); + suite_add_tcase (suite, tcase_symlink); + + SRunner* srunner = srunner_create (suite); + srunner_run_all (srunner, CK_VERBOSE); + + number_failed = srunner_ntests_failed (srunner); + srunner_free (srunner); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/libparted/tests/t1000-label.sh b/libparted/tests/t1000-label.sh new file mode 100755 index 0000000..c71fe55 --- /dev/null +++ b/libparted/tests/t1000-label.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# run the label unit tests in a directory supporting O_DIRECT + +# Copyright (C) 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/>. + +# This wrapper around the ./label binary is used to find a directory +# in which one can open a file with the O_DIRECT flag. + +. "${top_srcdir=../..}/tests/init.sh"; path_prepend_ . + +label || fail=1 + +Exit $fail diff --git a/libparted/tests/t1001-flags.sh b/libparted/tests/t1001-flags.sh new file mode 100755 index 0000000..7ceffe8 --- /dev/null +++ b/libparted/tests/t1001-flags.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# run the flags unittest + +# Copyright (C) 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/>. + +. "${top_srcdir=../..}/tests/init.sh"; path_prepend_ . + +flags || fail=1 + +Exit $fail diff --git a/libparted/tests/t2000-disk.sh b/libparted/tests/t2000-disk.sh new file mode 100755 index 0000000..0e7c774 --- /dev/null +++ b/libparted/tests/t2000-disk.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# run the disk unit tests in a directory supporting O_DIRECT + +# Copyright (C) 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/>. + +# This wrapper around the ./label binary is used to find a directory +# in which one can open a file with the O_DIRECT flag. + +. "${top_srcdir=../..}/tests/init.sh"; path_prepend_ . + +disk || fail=1 + +Exit $fail diff --git a/libparted/tests/t2100-zerolen.sh b/libparted/tests/t2100-zerolen.sh new file mode 100755 index 0000000..8b9bfe0 --- /dev/null +++ b/libparted/tests/t2100-zerolen.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# run the zerolen unit tests in a directory supporting O_DIRECT + +# Copyright (C) 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/>. + +. "${abs_top_srcdir=../..}/tests/init.sh"; path_prepend_ . +. $abs_top_srcdir/tests/t-lib-helpers.sh +# Need root privileges to create a device-mapper device. +require_root_ +device_mapper_required_ + +init_root_dir_ + +# This test only makes sense on Linux. +test "$(uname -s)" = Linux \ + || skip_ "not on Linux" + +test "x$ENABLE_DEVICE_MAPPER" = xyes \ + || skip_ "no device-mapper support" + +# Device map name - should be random to not conflict with existing ones on +# the system +linear_=plinear-$$ + +cleanup_fn_() +{ + # 'dmsetup remove' may fail because udev is still processing the device. + # Try it repeatedly for 2s. + i=0 + incr=1 + while :; do + dmsetup remove $linear_ > /dev/null 2>&1 && break + sleep .1 2>/dev/null || { sleep 1; incr=10; } + i=$(expr $i + $incr); test $i = 20 && break + done + if test $i = 20; then + dmsetup remove $linear_ + fi + + test -n "$d1" && losetup -d "$d1" + rm -f "$f1" +} + +f1=$(pwd)/1 +d1=$(loop_setup_ "$f1") \ + || skip_ "is this partition mounted with 'nodev'?" + +echo "0 1024 linear $d1 0" | dmsetup create "$linear_" \ + || skip_ "unable to create dm device" + +wait_for_dev_to_appear_ "/dev/mapper/$linear_" \ + || skip_ "dm device did not appear" + +zerolen /dev/mapper/$linear_ || fail=1 + +Exit $fail diff --git a/libparted/tests/t3000-symlink.sh b/libparted/tests/t3000-symlink.sh new file mode 100755 index 0000000..cd92879 --- /dev/null +++ b/libparted/tests/t3000-symlink.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# run the /dev/mapper symlink test + +# Copyright (C) 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/>. + +. "${abs_top_srcdir=../..}/tests/init.sh"; path_prepend_ . + +. $abs_top_srcdir/tests/t-lib-helpers.sh +# Need root privileges to create a symlink under /dev/mapper. +require_root_ + +symlink || fail=1 + +Exit $fail diff --git a/libparted/tests/t4000-volser.sh b/libparted/tests/t4000-volser.sh new file mode 100755 index 0000000..89688ba --- /dev/null +++ b/libparted/tests/t4000-volser.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# 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/>. + +. "${top_srcdir=../..}/tests/init.sh"; path_prepend_ . + +volser || fail=1 + +Exit $fail diff --git a/libparted/tests/volser.c b/libparted/tests/volser.c new file mode 100644 index 0000000..4b6e2d1 --- /dev/null +++ b/libparted/tests/volser.c @@ -0,0 +1,191 @@ +/* + * Author: Wang Dong <dongdwdw@cn.ibm.com> + */ + +#include <config.h> +#include <unistd.h> +#include <check.h> + +#include <parted/vtoc.h> +#include <parted/device.h> +#include <parted/fdasd.h> +#include <parted/vtoc.h> +#include "../arch/linux.h" +#include "common.h" +#include "progname.h" + +#if defined __s390__ || defined __s390x__ + +/* set dasd first */ +static char vol_devno[7] = {0}; +static char *tmp_disk; +static int fd; + +static PedDisk *disk; +static struct fdasd_anchor anc; +static fdasd_anchor_t *anchor = &anc; +static LinuxSpecific *arch_specific; + +/* set the enviroment */ +static void set_test (void) +{ + PedDevice *dev; + PedDiskType *type; + type = ped_disk_type_get ("dasd"); + + tmp_disk = _create_disk (20*1024*1024); + ck_assert_msg(tmp_disk != NULL, "Failed to create temporary disk"); + dev = ped_device_get (tmp_disk); + if (dev == NULL) + return; + + disk = _create_disk_label (dev, type); + if (!ped_device_open (disk->dev)) + return; + + fdasd_initialize_anchor (anchor); + arch_specific = LINUX_SPECIFIC (disk->dev); + fd = arch_specific->fd; + if (!fdasd_get_geometry (dev, anchor, fd)) + return; + + fdasd_check_volume (anchor, fd); + sprintf (vol_devno, "0X%04x", anchor->devno); + ck_assert (strlen (vol_devno) == VOLSER_LENGTH); +} + +static void free_test (void) +{ + ped_device_close (disk->dev); + ped_device_destroy (disk->dev); + unlink (tmp_disk); + free (tmp_disk); + fdasd_cleanup (anchor); +} + +/* Test with default volser */ +START_TEST (test_get_volser) +{ + char volser[7] = {0}; + fdasd_change_volser (anchor, vol_devno); + fdasd_write_labels (anchor, fd); + + fdasd_get_volser (anchor, volser, fd); + ck_assert (!strcmp (volser, vol_devno)); +} +END_TEST + +START_TEST (test_check_volser) +{ + char vol_long[] = "abcdefg"; + char vol_short[] = "ab_c "; + char vol_null[] = " "; + char *vol_input = NULL; + + vol_input = vol_long; + fdasd_check_volser (vol_input, anchor->devno); + ck_assert(!strcmp (vol_input, "ABCDEF")); + + vol_input = vol_short; + fdasd_check_volser (vol_input, anchor->devno); + ck_assert (!strcmp (vol_input, "ABC")); + + vol_input = vol_null; + fdasd_check_volser (vol_input, anchor->devno); + ck_assert (!strcmp (vol_input, vol_devno)); +} +END_TEST + +START_TEST (test_change_volser) +{ + + char vol[] = "000000"; + char volser[7] = {0}; + + fdasd_change_volser (anchor, vol); + fdasd_write_labels (anchor, fd); + + fdasd_get_volser (anchor, volser, fd); + ck_assert (!strcmp (volser, vol)); +} +END_TEST + +/* + * fdsad_recreate_vtoc recreate the VTOC with existing one. + * So the partition information should be not changed after recreating + * VTOC. +*/ +START_TEST (test_reuse_vtoc) +{ + ds5ext_t before; + ds5ext_t after; + + memcpy (&before, &anchor->f5->DS5AVEXT, sizeof(ds5ext_t)); + + if (anchor->fspace_trk) { + fdasd_reuse_vtoc (anchor); + memcpy (&after, &anchor->f5->DS5AVEXT, sizeof(ds5ext_t)); + if ((before.t != after.t) && (before.fc != after.fc) && (before.ft != after.ft)) + ck_abort (); + } else { + fdasd_reuse_vtoc (anchor); + memcpy (&after, &anchor->f5->DS5AVEXT, sizeof(ds5ext_t)); + if ((before.t != after.t) && (before.fc != after.fc) && (before.ft != after.ft)) + ck_abort (); + } +} +END_TEST + +#endif + +int main (int argc, char **argv) +{ + + set_program_name (argv[0]); + +#if defined __s390__ || defined __s390x__ + + int number_failed = 0; + + Suite *suite = suite_create ("Volser"); + + TCase *tcase_get = tcase_create ("Get"); + TCase *tcase_check = tcase_create ("Check"); + TCase *tcase_change = tcase_create ("Change"); + TCase *tcase_vtoc = tcase_create ("Vtoc"); + + ped_exception_set_handler (_test_exception_handler); + + tcase_add_checked_fixture (tcase_check, set_test, free_test); + tcase_add_test (tcase_check, test_check_volser); + tcase_set_timeout (tcase_check, 0); + suite_add_tcase (suite, tcase_check); + + tcase_add_checked_fixture (tcase_change, set_test, free_test); + tcase_add_test (tcase_change, test_change_volser); + tcase_set_timeout (tcase_change, 0); + suite_add_tcase (suite, tcase_change); + + tcase_add_checked_fixture (tcase_get, set_test, free_test); + tcase_add_test (tcase_get, test_get_volser); + tcase_set_timeout (tcase_get, 0); + suite_add_tcase (suite, tcase_get); + + tcase_add_checked_fixture (tcase_vtoc, set_test, free_test); + tcase_add_test (tcase_vtoc, test_reuse_vtoc); + tcase_set_timeout (tcase_vtoc, 0); + suite_add_tcase (suite, tcase_vtoc); + + SRunner *srunner = srunner_create (suite); + /* When to debug, uncomment this line */ + /* srunner_set_fork_status (srunner, CK_NOFORK); */ + + srunner_run_all (srunner, CK_VERBOSE); + + number_failed = srunner_ntests_failed (srunner); + srunner_free (srunner); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; + +#endif + return 0; +} diff --git a/libparted/tests/zerolen.c b/libparted/tests/zerolen.c new file mode 100644 index 0000000..2d9b424 --- /dev/null +++ b/libparted/tests/zerolen.c @@ -0,0 +1,52 @@ +#include <config.h> +#include <unistd.h> + +#include <check.h> + +#include <parted/parted.h> + +#include "common.h" +#include "progname.h" + +static const char* temporary_disk; + +/* TEST: Probe a zero-length disk image without raising an exception */ +START_TEST (test_probe) +{ + PedDevice* dev = ped_device_get (temporary_disk); + if (dev) + ped_device_destroy (dev); +} +END_TEST + +int +main (int argc, char **argv) +{ + set_program_name (argv[0]); + int number_failed; + Suite* suite = suite_create ("ZeroLen"); + TCase* tcase_probe = tcase_create ("Probe"); + + if (argc < 2) { + ck_abort_msg("Insufficient arguments"); + return EXIT_FAILURE; + } + temporary_disk = argv[1]; + setenv ("PARTED_TEST_DEVICE_LENGTH", "0", 1); + + /* Fail when an exception is raised */ + ped_exception_set_handler (_test_exception_handler); + + tcase_add_test (tcase_probe, test_probe); + /* Disable timeout for this test */ + tcase_set_timeout (tcase_probe, 0); + suite_add_tcase (suite, tcase_probe); + + SRunner* srunner = srunner_create (suite); + srunner_run_all (srunner, CK_VERBOSE); + + number_failed = srunner_ntests_failed (srunner); + srunner_free (srunner); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/libparted/timer.c b/libparted/timer.c new file mode 100644 index 0000000..b913150 --- /dev/null +++ b/libparted/timer.c @@ -0,0 +1,244 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2001, 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/>. +*/ + +/** \file timer.c */ + +/** + * \addtogroup PedTimer + * + * \brief A PedTimer keeps track of the progress of a single (possibly + * compound) operation. + * + * The user of libparted constructs a PedTimer, and passes it to libparted + * functions that are likely to be expensive operations + * (like ped_file_system_resize). Use of timers is optional... you may + * pass NULL instead. + * + * When you create a PedTimer, you must specify a timer handler function. + * This will be called when there's an update on how work is progressing. + * + * Timers may be nested. When a timer is constructed, you can choose + * to assign it a parent, along with an estimate of what proportion of + * the total (parent's) time will be used in the nested operation. In + * this case, the nested timer's handler is internal to libparted, + * and simply updates the parent's progress, and calls its handler. + * + * @{ + */ + + +#include <config.h> +#include <parted/parted.h> +#include <parted/debug.h> + +typedef struct { + PedTimer* parent; + float nest_frac; + float start_frac; +} NestedContext; + + +/** + * \brief Creates a timer. + * + * Context will be passed in the \p context + * argument to the \p handler, when it is invoked. + * + * \return a new PedTimer + */ +PedTimer* +ped_timer_new (PedTimerHandler* handler, void* context) +{ + PedTimer* timer; + + PED_ASSERT (handler != NULL); + + timer = (PedTimer*) ped_malloc (sizeof (PedTimer)); + if (!timer) + return NULL; + + timer->handler = handler; + timer->context = context; + ped_timer_reset (timer); + return timer; +} + + +/** + * \brief Destroys a \p timer. + */ +void +ped_timer_destroy (PedTimer* timer) +{ + if (!timer) + return; + + free (timer); +} + +/* This function is used by ped_timer_new_nested() as the timer->handler + * function. + */ +static void +_nest_handler (PedTimer* timer, void* context) +{ + NestedContext* ncontext = (NestedContext*) context; + + ped_timer_update ( + ncontext->parent, + ncontext->start_frac + ncontext->nest_frac * timer->frac); +} + + +/** + * \brief Creates a new nested timer. + * + * This function creates a "nested" timer that describes the progress + * of a subtask. \p parent is the parent timer, and \p nested_frac is + * the estimated proportion (between 0 and 1) of the time that will be + * spent doing the nested timer's operation. The timer should only be + * constructed immediately prior to starting the nested operation. + * (It will be inaccurate, otherwise). + * Updates to the progress of the subtask are propagated + * back through to the parent task's timer. + * + * \return nested timer + */ +PedTimer* +ped_timer_new_nested (PedTimer* parent, float nest_frac) +{ + NestedContext* context; + + if (!parent) + return NULL; + + PED_ASSERT (nest_frac >= 0.0f); + PED_ASSERT (nest_frac <= 1.0f); + + context = (NestedContext*) ped_malloc (sizeof (NestedContext)); + if (!context) + return NULL; + context->parent = parent; + context->nest_frac = nest_frac; + context->start_frac = parent->frac; + + return ped_timer_new (_nest_handler, context); +} + +/** + * \brief Destroys a nested \p timer. + */ +void +ped_timer_destroy_nested (PedTimer* timer) +{ + if (!timer) + return; + + free (timer->context); + ped_timer_destroy (timer); +} + +/** + * \internal + * + * \brief This function calls the update handler, making sure that it has + * the latest time. + * + * First it updates \p timer->now and recomputes \p timer->predicted_end, + * and then calls the handler. + */ +void +ped_timer_touch (PedTimer* timer) +{ + if (!timer) + return; + + timer->now = time (NULL); + if (timer->now > timer->predicted_end) + timer->predicted_end = timer->now; + + timer->handler (timer, timer->context); +} + +/** + * \internal + * + * \brief This function sets the \p timer into a "start of task" position. + * + * It resets the \p timer, by setting \p timer->start and \p timer->now + * to the current time. + */ +void +ped_timer_reset (PedTimer* timer) +{ + if (!timer) + return; + + timer->start = timer->now = timer->predicted_end = time (NULL); + timer->state_name = NULL; + timer->frac = 0; + + ped_timer_touch (timer); +} + +/** + * \internal + * + * \brief This function tells a \p timer what fraction \p frac of the task + * has been completed. + * + * Sets the new \p timer->frac, and calls ped_timer_touch(). + */ +void +ped_timer_update (PedTimer* timer, float frac) +{ + if (!timer) + return; + + timer->now = time (NULL); + timer->frac = frac; + + if (frac) + timer->predicted_end + = timer->start + + (long) ((timer->now - timer->start) / frac); + + ped_timer_touch (timer); +} + +/** + * \internal + * + * \brief This function changes the description of the current task that the + * \p timer describes. + * + * Sets a new name - \p state_name - for the current "phase" of the operation, + * and calls ped_timer_touch(). + */ +void +ped_timer_set_state_name (PedTimer* timer, const char* state_name) +{ + if (!timer) + return; + + timer->state_name = state_name; + ped_timer_touch (timer); +} + +/** @} */ diff --git a/libparted/unit.c b/libparted/unit.c new file mode 100644 index 0000000..a63b98d --- /dev/null +++ b/libparted/unit.c @@ -0,0 +1,586 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2005, 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/>. +*/ + +/** \file unit.c */ + +/** + * \addtogroup PedUnit + * + * \brief The PedUnit module provides a standard mechanism for describing + * and parsing locations within devices in human-friendly plain text. + * + * Internally, libparted uses PedSector (which is typedef'ed to be long long + * in <parted/device.h>) to describe device locations such as the start and + * end of partitions. However, sector numbers are often long and unintuitive. + * For example, my extended partition starts at sector 208845. PedUnit allows + * this location to be represented in more intutitive ways, including "106Mb", + * "0Gb" and "0%", as well as "208845s". PedUnit aims to provide facilities + * to provide a consistent system for describing device locations all + * throughout libparted. + * + * PedUnit provides two basic services: converting a PedSector into a text + * representation, and parsing a text representation into a PedSector. + * PedUnit currently supports these units: + * + * sectors, bytes, kilobytes, megabytes, gigabytes, terabytes, compact, + * cylinder and percent. + * + * PedUnit has a global variable that contains the default unit for all + * conversions. + * + * @{ + */ + + + + +#include <config.h> +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/unit.h> + +#include <ctype.h> +#include <stdio.h> +#include <float.h> + +#define N_(String) String +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + + +static PedUnit default_unit = PED_UNIT_COMPACT; +static const char* unit_names[] = { + "s", + "B", + "kB", + "MB", + "GB", + "TB", + "compact", + "cyl", + "chs", + "%", + "kiB", + "MiB", + "GiB", + "TiB" +}; + + +/** + * \brief Set the default \p unit used by subsequent calls to the PedUnit API. + * + * In particular, this affects how locations inside error messages + * (exceptions) are displayed. + */ +void +ped_unit_set_default (PedUnit unit) +{ + default_unit = unit; +} + + +/** + * \brief Get the current default unit. + */ +PedUnit _GL_ATTRIBUTE_PURE +ped_unit_get_default () +{ + return default_unit; +} + +/** + * Get the byte size of a given \p unit. + */ +long long +ped_unit_get_size (const PedDevice* dev, PedUnit unit) +{ + PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors; + + switch (unit) { + case PED_UNIT_SECTOR: return dev->sector_size; + case PED_UNIT_BYTE: return 1; + case PED_UNIT_KILOBYTE: return PED_KILOBYTE_SIZE; + case PED_UNIT_MEGABYTE: return PED_MEGABYTE_SIZE; + case PED_UNIT_GIGABYTE: return PED_GIGABYTE_SIZE; + case PED_UNIT_TERABYTE: return PED_TERABYTE_SIZE; + case PED_UNIT_KIBIBYTE: return PED_KIBIBYTE_SIZE; + case PED_UNIT_MEBIBYTE: return PED_MEBIBYTE_SIZE; + case PED_UNIT_GIBIBYTE: return PED_GIBIBYTE_SIZE; + case PED_UNIT_TEBIBYTE: return PED_TEBIBYTE_SIZE; + case PED_UNIT_CYLINDER: return cyl_size * dev->sector_size; + case PED_UNIT_CHS: return dev->sector_size; + + case PED_UNIT_PERCENT: + return dev->length * dev->sector_size / 100; + + case PED_UNIT_COMPACT: + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Cannot get unit size for special unit " + "'COMPACT'.")); + return 0; + } + + /* never reached */ + PED_ASSERT(0); + return 0; +} + +/** + * Get a textual (non-internationalized) representation of a \p unit. + * + * For example, the textual representation of PED_UNIT_SECTOR is "s". + */ +const char* +ped_unit_get_name (PedUnit unit) +{ + return unit_names[unit]; +} + +/** + * Get a unit based on its textual representation: \p unit_name. + * + * For example, ped_unit_get_by_name("Mb") returns PED_UNIT_MEGABYTE. + */ +PedUnit +ped_unit_get_by_name (const char* unit_name) +{ + PedUnit unit; + for (unit = PED_UNIT_FIRST; unit <= PED_UNIT_LAST; unit++) { + if (!strcasecmp (unit_names[unit], unit_name)) + return unit; + } + return -1; +} + +static char* +ped_strdup (const char *str) +{ + char *result; + result = ped_malloc (strlen (str) + 1); + if (!result) + return NULL; + strcpy (result, str); + return result; +} + +/** + * \brief Get a string that describes the location of the \p byte on + * device \p dev. + * + * The string is described with the desired \p unit. + * The returned string must be freed with free(). + */ +char* +ped_unit_format_custom_byte (const PedDevice* dev, PedSector byte, PedUnit unit) +{ + char buf[100]; + PedSector sector = byte / dev->sector_size; + double d, w; + int p; + + PED_ASSERT (dev != NULL); + + /* CHS has a special comma-separated format. */ + if (unit == PED_UNIT_CHS) { + const PedCHSGeometry *chs = &dev->bios_geom; + snprintf (buf, 100, "%lld,%lld,%lld", + sector / chs->sectors / chs->heads, + (sector / chs->sectors) % chs->heads, + sector % chs->sectors); + return ped_strdup (buf); + } + + /* Cylinders, sectors and bytes should be rounded down... */ + if (unit == PED_UNIT_CYLINDER + || unit == PED_UNIT_SECTOR + || unit == PED_UNIT_BYTE) { + snprintf (buf, 100, "%lld%s", + byte / ped_unit_get_size (dev, unit), + ped_unit_get_name (unit)); + return ped_strdup (buf); + } + + if (unit == PED_UNIT_COMPACT) { + if (byte >= 10LL * PED_TERABYTE_SIZE) + unit = PED_UNIT_TERABYTE; + else if (byte >= 10LL * PED_GIGABYTE_SIZE) + unit = PED_UNIT_GIGABYTE; + else if (byte >= 10LL * PED_MEGABYTE_SIZE) + unit = PED_UNIT_MEGABYTE; + else if (byte >= 10LL * PED_KILOBYTE_SIZE) + unit = PED_UNIT_KILOBYTE; + else + unit = PED_UNIT_BYTE; + } + + /* IEEE754 says that 100.5 has to be rounded to 100 (by printf) */ + /* but 101.5 has to be rounded to 102... so we multiply by 1+E. */ + /* This just divide by 2 the natural IEEE754 extended precision */ + /* and won't cause any trouble before 1000 TB */ + d = ((double)byte / ped_unit_get_size (dev, unit)) + * (1. + DBL_EPSILON); + w = d + ( (d < 10. ) ? 0.005 : + (d < 100.) ? 0.05 : + 0.5 ); + p = (w < 10. ) ? 2 : + (w < 100.) ? 1 : + 0 ; + +#ifdef __BEOS__ + snprintf (buf, 100, "%.*f%s", p, d, ped_unit_get_name(unit)); +#else + snprintf (buf, 100, "%1$.*2$f%3$s", d, p, ped_unit_get_name (unit)); +#endif + + return ped_strdup (buf); +} + +/** + * \brief Get a string that describes the location of the \p byte on + * device \p dev. + * + * The string is described with the default unit, which is set + * by ped_unit_set_default(). + * The returned string must be freed with free(). + */ +char* +ped_unit_format_byte (const PedDevice* dev, PedSector byte) +{ + PED_ASSERT (dev != NULL); + return ped_unit_format_custom_byte (dev, byte, default_unit); +} + +/** + * \brief Get a string that describes the location \p sector on device \p dev. + * + * The string is described with the desired \p unit. + * The returned string must be freed with free(). + */ +char* +ped_unit_format_custom (const PedDevice* dev, PedSector sector, PedUnit unit) +{ + PED_ASSERT (dev != NULL); + return ped_unit_format_custom_byte(dev, sector*dev->sector_size, unit); +} + +/** + * \brief Get a string that describes the location \p sector on device \p dev. + * + * The string is described with the default unit, which is set + * by ped_unit_set_default(). + * The returned string must be freed with free(). + */ +char* +ped_unit_format (const PedDevice* dev, PedSector sector) +{ + PED_ASSERT (dev != NULL); + return ped_unit_format_custom_byte (dev, sector * dev->sector_size, + default_unit); +} + +/** + * If \p str contains a valid description of a location on \p dev, + * then \p *sector is modified to describe the location and a geometry + * is created in \p *range describing a 2 units large area centered on + * \p *sector. If the \p range as described here would be partially outside + * the device \p dev, the geometry returned is the intersection between the + * former and the whole device geometry. If no units are specified, then the + * default unit is assumed. + * + * \return \c 1 if \p str is a valid location description, \c 0 otherwise + */ +int +ped_unit_parse (const char* str, const PedDevice* dev, PedSector *sector, + PedGeometry** range) +{ + return ped_unit_parse_custom (str, dev, default_unit, sector, range); +} + +/* Inefficiently removes all spaces from a string, in-place. */ +static void +strip_string (char* str) +{ + int i; + + for (i = 0; str[i] != 0; i++) { + if (isspace (str[i])) { + int j; + for (j = i + 1; str[j] != 0; j++) + str[j - 1] = str[j]; + } + } +} + + +/* Find non-number suffix. Eg: find_suffix("32Mb") returns a pointer to + * "Mb". */ +static char* _GL_ATTRIBUTE_PURE +find_suffix (const char* str) +{ + while (str[0] != 0 && (isdigit (str[0]) || strchr(",.-", str[0]))) + str++; + return (char *) str; +} + +static void +remove_punct (char* str) +{ + int i = 0; + + for (i = 0; str[i]; i++) { + if (ispunct (str[i])) + str[i] = ' '; + } +} + +static int _GL_ATTRIBUTE_PURE +is_chs (const char* str) +{ + int punct_count = 0; + int i = 0; + + for (i = 0; str[i]; i++) + punct_count += ispunct (str[i]) != 0; + return punct_count == 2; +} + +static int +parse_chs (const char* str, const PedDevice* dev, PedSector* sector, + PedGeometry** range) +{ + PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors; + PedCHSGeometry chs; + + char* copy = ped_strdup (str); + if (!copy) + return 0; + strip_string (copy); + remove_punct (copy); + + if (sscanf (copy, "%d %d %d", + &chs.cylinders, &chs.heads, &chs.sectors) != 3) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("\"%s\" has invalid syntax for locations."), + copy); + goto error_free_copy; + } + + if (chs.heads >= dev->bios_geom.heads) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("The maximum head value is %d."), + dev->bios_geom.heads - 1); + goto error_free_copy; + } + if (chs.sectors >= dev->bios_geom.sectors) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("The maximum sector value is %d."), + dev->bios_geom.sectors - 1); + goto error_free_copy; + } + + *sector = 1LL * chs.cylinders * cyl_size + + chs.heads * dev->bios_geom.sectors + + chs.sectors; + + if (*sector >= dev->length) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("The location %s is outside of the " + "device %s."), + str, dev->path); + goto error_free_copy; + } + if (range) + *range = ped_geometry_new (dev, *sector, 1); + free (copy); + return !range || *range != NULL; + +error_free_copy: + free (copy); + *sector = 0; + if (range) + *range = NULL; + return 0; +} + +static PedSector +clip (const PedDevice* dev, PedSector sector) +{ + if (sector < 0) + return 0; + if (sector > dev->length - 1) + return dev->length - 1; + return sector; +} + +static PedGeometry* +geometry_from_centre_radius (const PedDevice* dev, + PedSector sector, PedSector radius) +{ + PedSector start = clip (dev, sector - radius); + PedSector end = clip (dev, sector + radius); + if (sector - end > radius || start - sector > radius) + return NULL; + return ped_geometry_new (dev, start, end - start + 1); +} + +static PedUnit +parse_unit_suffix (const char* suffix, PedUnit suggested_unit) +{ + if (strlen (suffix) > 1 && tolower (suffix[1]) == 'i') { + switch (tolower (suffix[0])) { + case 'k': return PED_UNIT_KIBIBYTE; + case 'm': return PED_UNIT_MEBIBYTE; + case 'g': return PED_UNIT_GIBIBYTE; + case 't': return PED_UNIT_TEBIBYTE; + } + } else if (strlen (suffix) > 0) { + switch (tolower (suffix[0])) { + case 's': return PED_UNIT_SECTOR; + case 'b': return PED_UNIT_BYTE; + case 'k': return PED_UNIT_KILOBYTE; + case 'm': return PED_UNIT_MEGABYTE; + case 'g': return PED_UNIT_GIGABYTE; + case 't': return PED_UNIT_TERABYTE; + case 'c': return PED_UNIT_CYLINDER; + case '%': return PED_UNIT_PERCENT; + } + } + + if (suggested_unit == PED_UNIT_COMPACT) { + if (default_unit == PED_UNIT_COMPACT) + return PED_UNIT_MEGABYTE; + else + return default_unit; + } + + return suggested_unit; +} + +/** + * If \p str contains a valid description of a location on \p dev, then + * \p *sector is modified to describe the location and a geometry is created + * in \p *range describing a 2 units large area centered on \p *sector. If the + * \p range as described here would be partially outside the device \p dev, the + * geometry returned is the intersection between the former and the whole + * device geometry. If no units are specified, then the default unit is + * assumed. + * + * \throws PED_EXCEPTION_ERROR if \p str contains invalid description of a + * location + * \throws PED_EXCEPTION_ERROR if location described by \p str + * is outside of the device \p dev->path + * + * \return \c 1 if \p str is a valid location description, \c 0 otherwise. + */ +int +ped_unit_parse_custom (const char* str, const PedDevice* dev, PedUnit unit, + PedSector* sector, PedGeometry** range) +{ + char* copy; + char* suffix; + double num; + long long unit_size; + PedSector radius; + + if (is_chs (str)) + return parse_chs (str, dev, sector, range); + + copy = ped_strdup (str); + if (!copy) + goto error; + strip_string (copy); + + suffix = find_suffix (copy); + unit = parse_unit_suffix (suffix, unit); + suffix[0] = 0; + + if (sscanf (copy, "%lf", &num) != 1) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Invalid number.")); + goto error_free_copy; + } + if (num > 0 && num < 1) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("Use a smaller unit instead of a value < 1")); + goto error_free_copy; + } + + unit_size = ped_unit_get_size (dev, unit); + switch (unit) { + /* If the user specifies the address using IEC units e.g., 4MiB, as in + parted -s -- $dev mklabel gpt mkpart P-NAME 4MiB -34s + do not use size of the unit as the range. Rather, presume that they + are specifying precisely the starting or ending number, + and treat "4MiB" just as we would treat "4194304B". */ + case PED_UNIT_KIBIBYTE: + case PED_UNIT_MEBIBYTE: + case PED_UNIT_GIBIBYTE: + case PED_UNIT_TEBIBYTE: + radius = 0; + break; + default: + radius = (ped_div_round_up (unit_size, dev->sector_size) / 2) - 1; + if (radius < 0) + radius = 0; + } + + *sector = num * unit_size / dev->sector_size; + /* negative numbers count from the end */ + if (copy[0] == '-') + *sector += dev->length; + if (range) { + *range = geometry_from_centre_radius (dev, *sector, radius); + if (!*range) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, + _("The location %s is outside of the " + "device %s."), + str, dev->path); + goto error_free_copy; + } + } + *sector = clip (dev, *sector); + + free (copy); + return 1; + +error_free_copy: + free (copy); +error: + *sector = 0; + if (range) + *range = NULL; + return 0; +} + + +/** @} */ |