3414 lines
111 KiB
C
Executable File
3414 lines
111 KiB
C
Executable File
/* 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
|
|
};
|