diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:16:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:16:34 +0000 |
commit | a398d2c2b5fd6ab0545d8bb019f9a970b2309404 (patch) | |
tree | 272fc7ab226258d7ceddee12c8c682c8e711c2b0 /libparted/arch | |
parent | Initial commit. (diff) | |
download | parted-upstream.tar.xz parted-upstream.zip |
Adding upstream version 3.6.upstream/3.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libparted/arch')
-rw-r--r-- | libparted/arch/beos.c | 649 | ||||
-rw-r--r-- | libparted/arch/gnu.c | 930 | ||||
-rw-r--r-- | libparted/arch/linux.c | 3413 | ||||
-rw-r--r-- | libparted/arch/linux.h | 44 |
4 files changed, 5036 insertions, 0 deletions
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 */ |