// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 1991-1998 Linus Torvalds * Re-organised Feb 1998 Russell King * Copyright (C) 2020 Christoph Hellwig
*/ #include <linux/fs.h> #include <linux/major.h> #include <linux/slab.h> #include <linux/ctype.h> #include <linux/vmalloc.h> #include <linux/raid/detect.h> #include"check.h"
staticint (*const check_part[])(struct parsed_partitions *) = { /* * Probe partition formats with tables at disk address 0 * that also have an ADFS boot block at 0xdc0.
*/ #ifdef CONFIG_ACORN_PARTITION_ICS
adfspart_check_ICS, #endif #ifdef CONFIG_ACORN_PARTITION_POWERTEC
adfspart_check_POWERTEC, #endif #ifdef CONFIG_ACORN_PARTITION_EESOX
adfspart_check_EESOX, #endif
/* * Now move on to formats that only have partition info at * disk address 0xdc0. Since these may also have stale * PC/BIOS partition tables, they need to come before * the msdos entry.
*/ #ifdef CONFIG_ACORN_PARTITION_CUMANA
adfspart_check_CUMANA, #endif #ifdef CONFIG_ACORN_PARTITION_ADFS
adfspart_check_ADFS, #endif
#ifdef CONFIG_CMDLINE_PARTITION
cmdline_partition, #endif #ifdef CONFIG_OF_PARTITION
of_partition, /* cmdline have priority to OF */ #endif #ifdef CONFIG_EFI_PARTITION
efi_partition, /* this must come before msdos */ #endif #ifdef CONFIG_SGI_PARTITION
sgi_partition, #endif #ifdef CONFIG_LDM_PARTITION
ldm_partition, /* this must come before msdos */ #endif #ifdef CONFIG_MSDOS_PARTITION
msdos_partition, #endif #ifdef CONFIG_OSF_PARTITION
osf_partition, #endif #ifdef CONFIG_SUN_PARTITION
sun_partition, #endif #ifdef CONFIG_AMIGA_PARTITION
amiga_partition, #endif #ifdef CONFIG_ATARI_PARTITION
atari_partition, #endif #ifdef CONFIG_MAC_PARTITION
mac_partition, #endif #ifdef CONFIG_ULTRIX_PARTITION
ultrix_partition, #endif #ifdef CONFIG_IBM_PARTITION
ibm_partition, #endif #ifdef CONFIG_KARMA_PARTITION
karma_partition, #endif #ifdef CONFIG_SYSV68_PARTITION
sysv68_partition, #endif
NULL
};
staticstruct parsed_partitions *allocate_partitions(struct gendisk *hd)
{ struct parsed_partitions *state; int nr = DISK_MAX_PARTS;
state = kzalloc(sizeof(*state), GFP_KERNEL); if (!state) return NULL;
i = res = err = 0; while (!res && check_part[i]) {
memset(state->parts, 0, state->limit * sizeof(state->parts[0]));
res = check_part[i++](state); if (res < 0) { /* * We have hit an I/O error which we don't report now. * But record it, and let the others do their job.
*/
err = res;
res = 0;
}
} if (res > 0) {
printk(KERN_INFO "%s", state->pp_buf);
free_page((unsignedlong)state->pp_buf); return state;
} if (state->access_beyond_eod)
err = -ENOSPC; /* * The partition is unrecognized. So report I/O errors if there were any
*/ if (err)
res = err; if (res) {
strlcat(state->pp_buf, " unable to read partition table\n", PAGE_SIZE);
printk(KERN_INFO "%s", state->pp_buf);
}
/* * Must be called either with open_mutex held, before a disk can be opened or * after all disk users are gone.
*/ staticstruct block_device *add_partition(struct gendisk *disk, int partno,
sector_t start, sector_t len, int flags, struct partition_meta_info *info)
{
dev_t devt = MKDEV(0, 0); struct device *ddev = disk_to_dev(disk); struct device *pdev; struct block_device *bdev; constchar *dname; int err;
lockdep_assert_held(&disk->open_mutex);
if (partno >= DISK_MAX_PARTS) return ERR_PTR(-EINVAL);
/* * Partitions are not supported on zoned block devices that are used as * such.
*/ if (bdev_is_zoned(disk->part0)) {
pr_warn("%s: partitions not supported on host managed zoned block device\n",
disk->disk_name); return ERR_PTR(-ENXIO);
}
if (xa_load(&disk->part_tbl, partno)) return ERR_PTR(-EBUSY);
/* ensure we always have a reference to the whole disk */
get_device(disk_to_dev(disk));
int bdev_add_partition(struct gendisk *disk, int partno, sector_t start,
sector_t length)
{ struct block_device *part; int ret;
mutex_lock(&disk->open_mutex); if (!disk_live(disk)) {
ret = -ENXIO; goto out;
}
if (disk->flags & GENHD_FL_NO_PART) {
ret = -EINVAL; goto out;
}
if (partition_overlaps(disk, start, length, -1)) {
ret = -EBUSY; goto out;
}
part = add_partition(disk, partno, start, length,
ADDPART_FLAG_NONE, NULL);
ret = PTR_ERR_OR_ZERO(part);
out:
mutex_unlock(&disk->open_mutex); return ret;
}
int bdev_del_partition(struct gendisk *disk, int partno)
{ struct block_device *part = NULL; int ret = -ENXIO;
mutex_lock(&disk->open_mutex);
part = xa_load(&disk->part_tbl, partno); if (!part) goto out_unlock;
ret = -EBUSY; if (atomic_read(&part->bd_openers)) goto out_unlock;
/* * We verified that @part->bd_openers is zero above and so * @part->bd_holder{_ops} can't be set. And since we hold * @disk->open_mutex the device can't be claimed by anyone. * * So no need to call @part->bd_holder_ops->mark_dead() here. * Just delete the partition and invalidate it.
*/
if (disk_unlock_native_capacity(disk)) returnfalse;
/* * We can not ignore partitions of broken tables created by for * example camera firmware, but we limit them to the end of the * disk to avoid creating invalid block devices.
*/
size = get_capacity(disk) - from;
}
part = add_partition(disk, p, from, size, state->parts[p].flags,
&state->parts[p].info); if (IS_ERR(part)) { if (PTR_ERR(part) != -ENXIO) {
printk(KERN_ERR " %s: p%d could not be added: %pe\n",
disk->disk_name, p, part);
} returntrue;
}
if (IS_BUILTIN(CONFIG_BLK_DEV_MD) &&
(state->parts[p].flags & ADDPART_FLAG_RAID))
md_autodetect_dev(part->bd_dev);
returntrue;
}
staticint blk_add_partitions(struct gendisk *disk)
{ struct parsed_partitions *state; int ret = -EAGAIN, p;
if (!disk_has_partscan(disk)) return 0;
state = check_partition(disk); if (!state) return 0; if (IS_ERR(state)) { /* * I/O error reading the partition table. If we tried to read * beyond EOD, retry after unlocking the native capacity.
*/ if (PTR_ERR(state) == -ENOSPC) {
printk(KERN_WARNING "%s: partition table beyond EOD, ",
disk->disk_name); if (disk_unlock_native_capacity(disk)) return -EAGAIN;
} return -EIO;
}
/* * Partitions are not supported on host managed zoned block devices.
*/ if (bdev_is_zoned(disk->part0)) {
pr_warn("%s: ignoring partition table on host managed zoned block device\n",
disk->disk_name);
ret = 0; goto out_free_state;
}
/* * If we read beyond EOD, try unlocking native capacity even if the * partition table was successfully read as we could be missing some * partitions.
*/ if (state->access_beyond_eod) {
printk(KERN_WARNING "%s: partition table partially beyond EOD, ",
disk->disk_name); if (disk_unlock_native_capacity(disk)) goto out_free_state;
}
/* tell userspace that the media / partition table may have changed */
kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE);
for (p = 1; p < state->limit; p++) if (!blk_add_partition(disk, state, p)) goto out_free_state;
ret = 0;
out_free_state:
free_partitions(state); return ret;
}
int bdev_disk_changed(struct gendisk *disk, bool invalidate)
{ struct block_device *part; unsignedlong idx; int ret = 0;
lockdep_assert_held(&disk->open_mutex);
if (!disk_live(disk)) return -ENXIO;
rescan: if (disk->open_partitions) return -EBUSY;
sync_blockdev(disk->part0);
invalidate_bdev(disk->part0);
xa_for_each_start(&disk->part_tbl, idx, part, 1) { /* * Remove the block device from the inode hash, so that * it cannot be looked up any more even when openers * still hold references.
*/
bdev_unhash(part);
/* * If @disk->open_partitions isn't elevated but there's * still an active holder of that block device things * are broken.
*/
WARN_ON_ONCE(atomic_read(&part->bd_openers));
invalidate_bdev(part);
drop_partition(part);
}
clear_bit(GD_NEED_PART_SCAN, &disk->state);
/* * Historically we only set the capacity to zero for devices that * support partitions (independ of actually having partitions created). * Doing that is rather inconsistent, but changing it broke legacy * udisks polling for legacy ide-cdrom devices. Use the crude check * below to get the sane behavior for most device while not breaking * userspace for this particular setup.
*/ if (invalidate) { if (!(disk->flags & GENHD_FL_NO_PART) ||
!(disk->flags & GENHD_FL_REMOVABLE))
set_capacity(disk, 0);
}
if (get_capacity(disk)) {
ret = blk_add_partitions(disk); if (ret == -EAGAIN) goto rescan;
} elseif (invalidate) { /* * Tell userspace that the media / partition table may have * changed.
*/
kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE);
}
return ret;
} /* * Only exported for loop and dasd for historic reasons. Don't use in new * code!
*/
EXPORT_SYMBOL_GPL(bdev_disk_changed);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.