/*
*
* fsutils.c : filesystem utilities
*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Licensed under the Academic Free License version 2.1
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <sys/types.h>
#include <sys/scsi/impl/uscsi.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/dkio.h>
#include <libintl.h>
#include <sys/dktp/fdisk.h>
#include <sys/fs/pc_label.h>
#include <libhal.h>
#include "fsutils.h"
/*
* Separates dos notation device spec into device and drive number
*/
boolean_t
dos_to_dev(char *path, char **devpath, int *num)
{
char *p;
if ((p = strrchr(path, ':')) == NULL) {
return (B_FALSE);
}
if ((*num = atoi(p + 1)) == 0) {
return (B_FALSE);
}
p[0] = '\0';
*devpath = strdup(path);
p[0] = ':';
return (*devpath != NULL);
}
char *
get_slice_name(char *devlink)
{
char *part, *slice, *disk;
char *s = NULL;
char *p;
if ((p = strstr(devlink, "/lofi/")) != 0) {
return (p + sizeof ("/lofi/") - 1);
}
part = strrchr(devlink, 'p');
slice = strrchr(devlink, 's');
disk = strrchr(devlink, 'd');
if ((part != NULL) && (part > slice) && (part > disk)) {
s = part;
} else if ((slice != NULL) && (slice > disk)) {
s = slice;
} else {
s = disk;
}
if ((s != NULL) && isdigit(s[1])) {
return (s);
} else {
return ("");
}
}
boolean_t
is_dos_drive(uchar_t type)
{
return ((type == DOSOS12) || (type == DOSOS16) ||
(type == DOSHUGE) || (type == FDISK_WINDOWS) ||
(type == FDISK_EXT_WIN) || (type == FDISK_FAT95) ||
(type == DIAGPART));
}
boolean_t
is_dos_extended(uchar_t id)
{
return ((id == EXTDOS) || (id == FDISK_EXTLBA));
}
struct part_find_s {
int num;
int count;
int systid;
int r_systid;
uint_t r_relsect;
uint_t r_numsect;
};
enum { WALK_CONTINUE, WALK_TERMINATE };
/*
* Walk partition tables and invoke a callback for each.
*/
static void
walk_partitions(int fd, int startsec, uint_t secsz,
int (*f)(void *, int, uint_t, uint_t), void *arg)
{
uint32_t buf[1024/4];
int bufsize = 1024;
struct mboot *mboot = (struct mboot *)&buf[0];
struct ipart ipart[FD_NUMPART];
uint_t sec = startsec;
uint_t lastsec = sec + 1;
uint_t relsect;
int ext = 0;
int systid;
boolean_t valid;
int i;
while (sec != lastsec) {
if (pread(fd, buf, bufsize, (off_t)sec * secsz) != bufsize) {
break;
}
lastsec = sec;
if (ltohs(mboot->signature) != MBB_MAGIC) {
break;
}
bcopy(mboot->parts, ipart, FD_NUMPART * sizeof (struct ipart));
for (i = 0; i < FD_NUMPART; i++) {
systid = ipart[i].systid;
relsect = sec + ltohi(ipart[i].relsect);
if (systid == 0) {
continue;
}
valid = B_TRUE;
if (is_dos_extended(systid) && (sec == lastsec)) {
sec = startsec + ltohi(ipart[i].relsect);
if (ext++ == 0) {
relsect = startsec = sec;
} else {
valid = B_FALSE;
}
}
if (valid && f(arg, ipart[i].systid, relsect,
ltohi(ipart[i].numsect)) == WALK_TERMINATE) {
return;
}
}
}
}
static int
find_dos_drive_cb(void *arg, int systid, uint_t relsect, uint_t numsect)
{
struct part_find_s *p = arg;
if (is_dos_drive(systid)) {
if (++p->count == p->num) {
p->r_relsect = relsect;
p->r_numsect = numsect;
p->r_systid = systid;
return (WALK_TERMINATE);
}
}
return (WALK_CONTINUE);
}
/*
* Given a dos drive number, return its relative sector number,
* number of sectors in partition and the system id.
*/
boolean_t
find_dos_drive(int fd, int num, uint_t secsz, off_t *offset)
{
struct part_find_s p = { 0, 0, 0, 0, 0, 0 };
p.num = num;
if (num > 0) {
walk_partitions(fd, 0, secsz, find_dos_drive_cb, &p);
if (p.count == num) {
*offset = (off_t)p.r_relsect * secsz;
return (B_TRUE);
}
}
return (B_FALSE);
}
static int
get_num_dos_drives_cb(void *arg, int systid, uint_t relsect, uint_t numsect)
{
if (is_dos_drive(systid)) {
(*(int *)arg)++;
}
return (WALK_CONTINUE);
}
int
get_num_dos_drives(int fd, uint_t secsz)
{
int count = 0;
walk_partitions(fd, 0, secsz, get_num_dos_drives_cb, &count);
return (count);
}
/*
* Return true if all non-empty slices in vtoc have identical start/size and
* are tagged backup/entire disk.
*/
boolean_t
vtoc_one_slice_entire_disk(struct extvtoc *vtoc)
{
int i;
struct extpartition *p;
diskaddr_t prev_start;
diskaddr_t prev_size;
for (i = 0; i < vtoc->v_nparts; i++) {
p = &vtoc->v_part[i];
if (p->p_size == 0) {
continue;
}
if ((p->p_tag != V_BACKUP) && ((p->p_tag != V_UNASSIGNED))) {
return (B_FALSE);
}
if ((i > 0) &&
((p->p_start != prev_start) || (p->p_size != prev_size))) {
return (B_FALSE);
}
prev_start = p->p_start;
prev_size = p->p_size;
}
return (B_TRUE);
}