/*
* Copyright 2018 The NetBSD Foundation, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* We should actually try to read the label inside the start/len
* boundary, but for simplicity just rely on the kernel and
* instead verify a FS_UNUSED partition at RAW_PART-1 (if
* RAW_PART > 'c') is within the given limits.
*/
if (ioctl(fd, DIOCGDINFO, &parts->l) < 0) {
free(parts);
close(fd);
return NULL;
}
#if RAW_PART == 3
if (parts->l.d_partitions[RAW_PART-1].p_fstype == FS_UNUSED) {
daddr_t dlstart = parts->l.d_partitions[RAW_PART-1].p_offset;
daddr_t dlend = start +
parts->l.d_partitions[RAW_PART-1].p_size;
if (dlstart < start || dlend > (start+len)) {
/*
* Kernel assumes different outer partition
* (probably not yet written back to disk)
* so this label is invalid.
*/
free(parts);
close(fd);
return NULL;
}
}
#endif
for (int part = 0; part < parts->l.d_npartitions; part++) {
if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
&& parts->l.d_partitions[part].p_size == 0)
continue;
parts->dp.num_part++;
if (parts->l.d_partitions[part].p_fstype == FS_UNUSED)
continue;
/*
* Verify we really have a disklabel on the target disk.
*/
if (run_program(RUN_SILENT | RUN_ERROR_OK,
"disklabel -r %s", disk) == 0) {
have_own_label = true;
}
#ifdef DISKLABEL_NO_ONDISK_VERIFY
else {
/*
* disklabel(8) with -r checks a native disklabel at
* LABELOFFSET sector, but several ports don't have
* a native label and use emulated one translated from
* port specific MD disk partition information.
* Unfortunately, there is no MI way to check whether
* the disk has a native BSD disklabel by readdisklabel(9)
* via DIOCGDINFO. So check if returned label looks
* defaults set by readdisklabel(9) per MD way.
*/
have_own_label = !md_disklabel_is_default(&parts->l);
}
#endif
if (!have_own_label) {
bool found_real_part = false;
if (parts->l.d_npartitions <= RAW_PART ||
parts->l.d_partitions[RAW_PART].p_size == 0)
goto no_valid_label;
/*
* Check if kernel translation gave us "something" besides
* the raw or the whole-disk partition.
* If not: report missing disklabel.
*/
for (int part = 0; part < parts->l.d_npartitions; part++) {
if (parts->l.d_partitions[part].p_fstype == FS_UNUSED)
continue;
if (/* part == 0 && */ /* PR kern/54882 */
parts->l.d_partitions[part].p_offset ==
parts->l.d_partitions[RAW_PART].p_offset &&
parts->l.d_partitions[part].p_size ==
parts->l.d_partitions[RAW_PART].p_size)
continue;
if (part == RAW_PART)
continue;
found_real_part = true;
break;
}
if (!found_real_part) {
/* no partition there yet */
no_valid_label:
free(parts);
return NULL;
}
}
return &parts->dp;
}
/*
* Escape a string for usage as a tag name in a capfile(5),
* we really know there is enough space in the destination buffer...
*/
static void
escape_capfile(char *dest, const char *src, size_t len)
{
while (*src && len > 0) {
if (*src == ':')
*dest++ = ' ';
else
*dest++ = *src;
src++;
len--;
}
*dest = 0;
}
/* make sure we have a 0 terminated packname */
strlcpy(packname, parts->l.d_packname, sizeof packname);
if (packname[0] == 0)
strcpy(packname, "fictious");
/* fill typename with disk name prefix, if not already set */
if (strlen(parts->l.d_typename) == 0) {
for (n = 0, d = parts->l.d_typename, s = disk;
*s && n < sizeof(parts->l.d_typename); d++, s++, n++) {
if (isdigit((unsigned char)*s))
break;
*d = *s;
}
}
/* we need a valid disk type name, so enforce an arbitrary if
* above did not yield a usable one */
if (strlen(parts->l.d_typename) == 0)
strncpy(parts->l.d_typename, "SCSI",
sizeof(parts->l.d_typename));
escape_capfile(disktype, parts->l.d_typename,
sizeof(parts->l.d_typename));
sprintf(fname, "/tmp/disklabel.%u", getpid());
f = fopen(fname, "w");
if (f == NULL)
return false;
if (i < parts->l.d_npartitions - 1)
scripting_fprintf(f, "\\\n");
else
scripting_fprintf(f, "\n");
}
scripting_fprintf(NULL, "EOF\n");
fclose(f);
/*
* Label a disk using an MD-specific string DISKLABEL_CMD for
* to invoke disklabel.
* if MD code does not define DISKLABEL_CMD, this is a no-op.
*
* i386 port uses "/sbin/disklabel -w -r", just like i386
* miniroot scripts, though this may leave a bogus incore label.
*
* Sun ports should use DISKLABEL_CMD "/sbin/disklabel -w"
* to get incore to ondisk inode translation for the Sun proms.
*/
#ifdef DISKLABEL_CMD
/* disklabel the disk */
rv = run_program(0, "%s -f %s %s '%s' '%s'",
DISKLABEL_CMD, fname, disk, disktype, packname);
#endif
for (int part = 0; part < parts->l.d_npartitions; part++) {
if (parts->l.d_partitions[part].p_fstype == FS_UNUSED
&& parts->l.d_partitions[part].p_size == 0)
continue;
if (part == RAW_PART)
continue;
daddr_t start = parts->l.d_partitions[part].p_offset;
daddr_t end = start + parts->l.d_partitions[part].p_size;
if (dl_types[0].description == NULL)
dl_init_types();
switch (pt) {
case PT_root: nt = FS_BSDFFS; break;
case PT_swap: nt = FS_SWAP; break;
case PT_FAT:
case PT_EFI_SYSTEM:
nt = FS_MSDOS; break;
case PT_EXT2: nt = FS_EX2FS; break;
case PT_SYSVBFS:
nt = FS_SYSVBFS; break;
default: nt = FS_UNUSED; break;
}
if (start < parts->dp.disk_start)
start = parts->dp.disk_start;
if (min_space_size < 1)
min_space_size = 1;
if (align > 1 && (start % align) != 0)
start = max(roundup(start, align), align);
end_of_disk = parts->dp.disk_start + parts->dp.disk_size;
from = start;
while (from < end_of_disk && cnt < max_num_result) {
again:
size = parts->dp.disk_start + parts->dp.disk_size - from;
start = from;
for (i = 0; i < parts->l.d_npartitions; i++) {
if (i == RAW_PART)
continue;
if (parts->l.d_partitions[i].p_fstype == FS_UNUSED)
continue;
if (parts->l.d_partitions[i].p_size == 0)
continue;
s = parts->l.d_partitions[i].p_offset;
e = parts->l.d_partitions[i].p_size + s;
if (s == ignore)
continue;
if (e < from)
continue;
if (s <= from && e > from) {
if (e - 1 >= end_of_disk)
return cnt;
from = e + 1;
if (align > 1) {
from = max(roundup(from, align), align);
if (from >= end_of_disk) {
size = 0;
break;
}
}
goto again;
}
if (s > from && s - from < size) {
size = s - from;
}
}
if (size >= min_space_size) {
result->start = start;
result->size = size;
result++;
cnt++;
}
from += size + 1;
if (align > 1)
from = max(roundup(from, align), align);
}
for (id = part_index = 0; part_index < parts->l.d_npartitions;
part_index++) {
if (parts->l.d_partitions[part_index].p_fstype == FS_UNUSED &&
parts->l.d_partitions[part_index].p_size == 0)
continue;
if (id == ptn)
break;
id++;
if (id > ptn)
return false;
}
if (part != 0)
*part = part_index;
pname = 'a'+ part_index;
switch (which_name) {
case parent_device_only:
strlcpy(devname, arg->disk, max_devname_len);
return true;
case logical_name:
case plain_name:
if (with_path)
snprintf(devname, max_devname_len, _PATH_DEV "%s%c",
arg->disk, pname);
else
snprintf(devname, max_devname_len, "%s%c",
arg->disk, pname);
return true;
case raw_dev_name:
if (with_path)
snprintf(devname, max_devname_len, _PATH_DEV "r%s%c",
arg->disk, pname);
else
snprintf(devname, max_devname_len, "r%s%c",
arg->disk, pname);
return true;
}
return false;
}
/*
* If the requested partition file system type internally skips
* the disk label sector, we can allow it to start at the beginning
* of the disk. In most cases though we have to move the partition
* to start past the label sector.
*/
static bool
need_to_skip_past_label(const struct disk_part_info *info)
{
switch (info->fs_type) {
case FS_BSDFFS:
case FS_RAID:
return false;
}
return true;
}
static part_id
disklabel_add_partition(struct disk_partitions *arg,
const struct disk_part_info *info, const char **err_msg)
{
struct disklabel_disk_partitions *parts =
(struct disklabel_disk_partitions*)arg;
int i, part = -1;
part_id new_id;
struct disk_part_free_space space;
struct disk_part_info data = *info;
if (dl_maxpart == 0)
dl_maxpart = getmaxpartitions();
for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) {
if (parts->l.d_partitions[i].p_size > 0)
new_id++;
if (data.nat_type->generic_ptype != PT_root &&
data.nat_type->generic_ptype != PT_swap && i < RAW_PART)
continue;
if (i == 0 && data.nat_type->generic_ptype != PT_root)
continue;
if (i == 1 && data.nat_type->generic_ptype != PT_swap)
continue;
if (i == RAW_PART)
continue;
#if RAW_PART == 3
if (i == RAW_PART-1 && parts->dp.parent != NULL)
continue;
#endif
if (parts->l.d_partitions[i].p_size > 0)
continue;
#ifdef MD_DISKLABEL_PART_INDEX_CHECK
if (!MD_DISKLABEL_PART_INDEX_CHECK(&parts->l, i, info))
continue;
#endif
part = i;
break;
}
if (part < 0) {
if (parts->l.d_npartitions >= dl_maxpart) {
if (err_msg)
*err_msg =
msg_string(MSG_err_too_many_partitions);
return NO_PART;
}
static part_id
disklabel_add_outer_partition(struct disk_partitions *arg,
const struct disk_part_info *info, const char **err_msg)
{
struct disklabel_disk_partitions *parts =
(struct disklabel_disk_partitions*)arg;
int i, part = -1;
part_id new_id;
if (dl_maxpart == 0)
dl_maxpart = getmaxpartitions();
for (new_id = 0, i = 0; i < parts->l.d_npartitions; i++) {
if (parts->l.d_partitions[i].p_size > 0)
new_id++;
if (info->nat_type->generic_ptype != PT_root &&
info->nat_type->generic_ptype != PT_swap && i < RAW_PART)
continue;
if (i == 0 && info->nat_type->generic_ptype != PT_root)
continue;
if (i == 1 && info->nat_type->generic_ptype != PT_swap)
continue;
if (i == RAW_PART)
continue;
#if RAW_PART == 3
if (i == RAW_PART-1 && parts->dp.parent != NULL)
continue;
#endif
if (parts->l.d_partitions[i].p_size > 0)
continue;
part = i;
break;
}
if (part < 0) {
if (parts->l.d_npartitions >= dl_maxpart) {
if (err_msg)
*err_msg =
msg_string(MSG_err_too_many_partitions);
return NO_PART;
}