diff --git a/configure.ac b/configure.ac index 82d953ed..a2bb693b 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # configure.ac - Source file to generate "./configure" to prepare package for # compilation. # -# Copyright (c) 2000-2006 Anton Altaparmakov +# Copyright (c) 2000-2013 Anton Altaparmakov # Copyright (c) 2003 Jan Kratochvil # Copyright (c) 2005-2009 Szabolcs Szakacsits # Copyright (C) 2007-2008 Alon Bar-Lev @@ -417,6 +417,43 @@ if test "x$extrapath" != "x"; then fi fi +# Specify support for obtaining the correct BIOS legacy geometry needed for +# Windows to boot in CHS mode. We check if hd.h header is present and the hd +# library is present that goes with it and then check if the hd_list() function +# is present and usable. +# +# Using the hd library is enabled by default and can be disabled with the +# --disable-hd option to the configure script. +AC_ARG_WITH(hd, [ + --with-hd@<:@=PFX@:>@ use Windows compliant disk geometry, with optional + prefix to hd library and headers @<:@default=detect@:>@ + --without-hd do not use Windows compliant disk geometry], + if test "$with_hd" = "yes"; then + extrapath2=default + elif test "$with_hd" = "no"; then + extrapath2= + else + extrapath2=$with_hd + fi, + extrapath2=default +) +if test "x$extrapath2" != "x"; then + if test "x$extrapath2" != "xdefault"; then + LIBNTFS_CPPFLAGS="$LIBNTFS_CPPFLAGS -I$extrapath2/include" + LIBNTFS_LIBS="$LIBNTFS_LIBS -L$extrapath2/lib" + fi + AC_CHECK_HEADER([hd.h], + AC_CHECK_LIB([hd], [hd_list], + AC_DEFINE([ENABLE_HD], 1, + [Define this to 1 if you want to enable use of Windows + compliant disk geometry.]) + LIBNTFS_LIBS="$LIBNTFS_LIBS -lhd", + AC_MSG_WARN([ntfsprogs Windows compliant geometry code requires the hd library.]), + ), + AC_MSG_WARN([ntfsprogs Windows compliant geometry code requires the hd library.]), + ) +fi + # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([ctype.h fcntl.h libgen.h libintl.h limits.h locale.h \ @@ -554,6 +591,8 @@ AC_SUBST([LIBFUSE_LITE_CFLAGS]) AC_SUBST([LIBFUSE_LITE_LIBS]) AC_SUBST([MKNTFS_CPPFLAGS]) AC_SUBST([MKNTFS_LIBS]) +AC_SUBST([LIBNTFS_CPPFLAGS]) +AC_SUBST([LIBNTFS_LIBS]) AC_SUBST([OUTPUT_FORMAT]) AM_CONDITIONAL([FUSE_INTERNAL], [test "${with_fuse}" = "internal"]) AM_CONDITIONAL([GENERATE_LDSCRIPT], [test "${enable_ldscript}" = "yes"]) diff --git a/include/ntfs-3g/device.h b/include/ntfs-3g/device.h index ad34ac56..c7cc9b64 100644 --- a/include/ntfs-3g/device.h +++ b/include/ntfs-3g/device.h @@ -1,7 +1,8 @@ /* * device.h - Exports for low level device io. Originated from the Linux-NTFS project. * - * Copyright (c) 2000-2006 Anton Altaparmakov + * Copyright (c) 2000-2013 Anton Altaparmakov + * Copyright (c) 2008-2013 Tuxera Inc. * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -73,6 +74,11 @@ typedef enum { * * The ntfs device structure defining all operations needed to access the low * level device underlying the ntfs volume. + * + * Note d_heads and d_sectors_per_track are only set as a result of a call to + * either ntfs_device_heads_get() or ntfs_device_sectors_per_track_get() (both + * calls will set up both fields or if getting them failed they will be left at + * -1). */ struct ntfs_device { struct ntfs_device_operations *d_ops; /* Device operations. */ @@ -80,6 +86,10 @@ struct ntfs_device { char *d_name; /* Name of device. */ void *d_private; /* Private data used by the device operations. */ + int d_heads; /* Disk geometry: number of + heads or -1. */ + int d_sectors_per_track; /* Disk geometry: number of + sectors per track or -1. */ }; struct stat; diff --git a/libntfs-3g/Makefile.am b/libntfs-3g/Makefile.am index b84cf64d..045ca52d 100644 --- a/libntfs-3g/Makefile.am +++ b/libntfs-3g/Makefile.am @@ -10,7 +10,8 @@ noinst_LTLIBRARIES = libntfs-3g.la endif libntfs_3g_la_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/include/ntfs-3g - +libntfs_3g_la_CPPFLAGS= $(AM_CPPFLAGS) $(LIBNTFS_CPPFLAGS) +libntfs_3g_la_LIBADD = $(LIBNTFS_LIBS) libntfs_3g_la_LDFLAGS = -version-info $(LIBNTFS_3G_VERSION) -no-undefined libntfs_3g_la_SOURCES = \ diff --git a/libntfs-3g/device.c b/libntfs-3g/device.c index 274abacb..0e4682d7 100644 --- a/libntfs-3g/device.c +++ b/libntfs-3g/device.c @@ -1,9 +1,10 @@ /** * device.c - Low level device io functions. Originated from the Linux-NTFS project. * - * Copyright (c) 2004-2006 Anton Altaparmakov + * Copyright (c) 2004-2013 Anton Altaparmakov * Copyright (c) 2004-2006 Szabolcs Szakacsits * Copyright (c) 2010 Jean-Pierre Andre + * Copyright (c) 2008-2013 Tuxera Inc. * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -67,6 +68,9 @@ #ifdef HAVE_LINUX_HDREG_H #include #endif +#ifdef ENABLE_HD +#include +#endif #include "types.h" #include "mst.h" @@ -128,6 +132,8 @@ struct ntfs_device *ntfs_device_alloc(const char *name, const long state, dev->d_ops = dops; dev->d_state = state; dev->d_private = priv_data; + dev->d_heads = -1; + dev->d_sectors_per_track = -1; } return dev; } @@ -642,6 +648,131 @@ s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev) return -1; } +static int ntfs_device_get_geo(struct ntfs_device *dev) +{ + int err; + + if (!dev) { + errno = EINVAL; + return -1; + } + err = EOPNOTSUPP; +#ifdef ENABLE_HD + { + hd_data_t *hddata; + hd_t *devlist, *partlist, *hd; + str_list_t *names; + hd_res_t *res; + const int d_name_len = strlen(dev->d_name) + 1; + int done = 0; + + hddata = calloc(1, sizeof(*hddata)); + if (!hddata) { + err = ENOMEM; + goto skip_hd; + } + /* List all "disk" class devices on the system. */ + devlist = hd_list(hddata, hw_disk, 1, NULL); + if (!devlist) { + free(hddata); + err = ENOMEM; + goto skip_hd; + } + /* + * Loop over each disk device looking for the device with the + * same unix name as @dev. + */ + for (hd = devlist; hd; hd = hd->next) { + if (hd->unix_dev_name && !strncmp(dev->d_name, + hd->unix_dev_name, d_name_len)) + goto got_hd; + if (hd->unix_dev_name2 && !strncmp(dev->d_name, + hd->unix_dev_name2, d_name_len)) + goto got_hd; + for (names = hd->unix_dev_names; names; + names = names->next) { + if (names->str && !strncmp(dev->d_name, + names->str, d_name_len)) + goto got_hd; + } + } + /* + * Device was not a whole disk device. Unless it is a file it + * is likely to be a partition device. List all "partition" + * class devices on the system. + */ + partlist = hd_list(hddata, hw_partition, 1, NULL); + for (hd = partlist; hd; hd = hd->next) { + if (hd->unix_dev_name && !strncmp(dev->d_name, + hd->unix_dev_name, d_name_len)) + goto got_part_hd; + if (hd->unix_dev_name2 && !strncmp(dev->d_name, + hd->unix_dev_name2, d_name_len)) + goto got_part_hd; + for (names = hd->unix_dev_names; names; + names = names->next) { + if (names->str && !strncmp(dev->d_name, + names->str, d_name_len)) + goto got_part_hd; + } + } + /* Failed to find the device. Stop trying and clean up. */ + goto end_hd; +got_part_hd: + /* Get the whole block device the partition device is on. */ + hd = hd_get_device_by_idx(hddata, hd->attached_to); + if (!hd) + goto end_hd; +got_hd: + /* + * @hd is now the whole block device either being formatted or + * that the partition being formatted is on. + * + * Loop over each resource of the disk device looking for the + * BIOS legacy geometry obtained from EDD which is what Windows + * needs to boot. + */ + for (res = hd->res; res; res = res->next) { + /* geotype 3 is BIOS legacy. */ + if (res->any.type != res_disk_geo || + res->disk_geo.geotype != 3) + continue; + dev->d_heads = res->disk_geo.heads; + dev->d_sectors_per_track = res->disk_geo.sectors; + done = 1; + } +end_hd: + hd_free_hd_list(partlist); + hd_free_hd_list(devlist); + hd_free_hd_data(hddata); + free(hddata); + if (done) { + ntfs_log_debug("EDD/BIOD legacy heads = %u, sectors " + "per track = %u\n", dev->d_heads, + dev->d_sectors_per_track); + return 0; + } + } +skip_hd: +#endif +#ifdef HDIO_GETGEO + { struct hd_geometry geo; + + if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { + dev->d_heads = geo.heads; + dev->d_sectors_per_track = geo.sectors; + ntfs_log_debug("HDIO_GETGEO heads = %u, sectors per " + "track = %u\n", dev->d_heads, + dev->d_sectors_per_track); + return 0; + } + err = errno; + } +#endif + errno = err; + return -1; +} + /** * ntfs_device_heads_get - get number of heads of device * @dev: open device @@ -653,6 +784,7 @@ s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev) * EINVAL Input parameter error * EOPNOTSUPP System does not support HDIO_GETGEO ioctl * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + * ENOMEM Not enough memory to complete the request */ int ntfs_device_heads_get(struct ntfs_device *dev) { @@ -660,20 +792,15 @@ int ntfs_device_heads_get(struct ntfs_device *dev) errno = EINVAL; return -1; } -#ifdef HDIO_GETGEO - { struct hd_geometry geo; - - if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { - ntfs_log_debug("HDIO_GETGEO heads = %u (0x%x)\n", - (unsigned)geo.heads, - (unsigned)geo.heads); - return geo.heads; + if (dev->d_heads == -1) { + if (ntfs_device_get_geo(dev) == -1) + return -1; + if (dev->d_heads == -1) { + errno = EINVAL; + return -1; } } -#else - errno = EOPNOTSUPP; -#endif - return -1; + return dev->d_heads; } /** @@ -687,6 +814,7 @@ int ntfs_device_heads_get(struct ntfs_device *dev) * EINVAL Input parameter error * EOPNOTSUPP System does not support HDIO_GETGEO ioctl * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + * ENOMEM Not enough memory to complete the request */ int ntfs_device_sectors_per_track_get(struct ntfs_device *dev) { @@ -694,20 +822,15 @@ int ntfs_device_sectors_per_track_get(struct ntfs_device *dev) errno = EINVAL; return -1; } -#ifdef HDIO_GETGEO - { struct hd_geometry geo; - - if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { - ntfs_log_debug("HDIO_GETGEO sectors_per_track = %u (0x%x)\n", - (unsigned)geo.sectors, - (unsigned)geo.sectors); - return geo.sectors; + if (dev->d_sectors_per_track == -1) { + if (ntfs_device_get_geo(dev) == -1) + return -1; + if (dev->d_sectors_per_track == -1) { + errno = EINVAL; + return -1; } } -#else - errno = EOPNOTSUPP; -#endif - return -1; + return dev->d_sectors_per_track; } /**