1 /*
   2  *
   3  * fsutils.c : filesystem utilities
   4  *
   5  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
   6  * Use is subject to license terms.
   7  *
   8  * Licensed under the Academic Free License version 2.1
   9  *
  10  */
  11 
  12 #ifdef HAVE_CONFIG_H
  13 #include <config.h>
  14 #endif
  15 
  16 #include <stdio.h>
  17 #include <sys/types.h>
  18 #include <sys/scsi/impl/uscsi.h>
  19 #include <string.h>
  20 #include <strings.h>
  21 #include <ctype.h>
  22 #include <unistd.h>
  23 #include <stdlib.h>
  24 #include <errno.h>
  25 #include <fcntl.h>
  26 #include <sys/dkio.h>
  27 #include <libintl.h>
  28 #include <sys/dktp/fdisk.h>
  29 #include <sys/fs/pc_label.h>
  30 
  31 #include <libhal.h>
  32 #include "fsutils.h"
  33 
  34 /*
  35  * Separates dos notation device spec into device and drive number
  36  */
  37 boolean_t
  38 dos_to_dev(char *path, char **devpath, int *num)
  39 {
  40         char *p;
  41 
  42         if ((p = strrchr(path, ':')) == NULL) {
  43                 return (B_FALSE);
  44         }
  45         if ((*num = atoi(p + 1)) == 0) {
  46                 return (B_FALSE);
  47         }
  48         p[0] = '\0';
  49         *devpath = strdup(path);
  50         p[0] = ':';
  51         return (*devpath != NULL);
  52 }
  53 
  54 char *
  55 get_slice_name(char *devlink)
  56 {
  57         char    *part, *slice, *disk;
  58         char    *s = NULL;
  59         char    *p;
  60 
  61         if ((p = strstr(devlink, "/lofi/")) != 0) {
  62                 return (p + sizeof ("/lofi/") - 1);
  63         }
  64 
  65         part = strrchr(devlink, 'p');
  66         slice = strrchr(devlink, 's');
  67         disk = strrchr(devlink, 'd');
  68 
  69         if ((part != NULL) && (part > slice) && (part > disk)) {
  70                 s = part;
  71         } else if ((slice != NULL) && (slice > disk)) {
  72                 s = slice;
  73         } else {
  74                 s = disk;
  75         }
  76         if ((s != NULL) && isdigit(s[1])) {
  77                 return (s);
  78         } else {
  79                 return ("");
  80         }
  81 }
  82 
  83 boolean_t
  84 is_dos_drive(uchar_t type)
  85 {
  86         return ((type == DOSOS12) || (type == DOSOS16) ||
  87             (type == DOSHUGE) || (type == FDISK_WINDOWS) ||
  88             (type == FDISK_EXT_WIN) || (type == FDISK_FAT95) ||
  89             (type == DIAGPART));
  90 }
  91 
  92 boolean_t
  93 is_dos_extended(uchar_t id)
  94 {
  95         return ((id == EXTDOS) || (id == FDISK_EXTLBA));
  96 }
  97 
  98 struct part_find_s {
  99         int     num;
 100         int     count;
 101         int     systid;
 102         int     r_systid;
 103         uint_t  r_relsect;
 104         uint_t  r_numsect;
 105 };
 106 
 107 enum { WALK_CONTINUE, WALK_TERMINATE };
 108 
 109 /*
 110  * Walk partition tables and invoke a callback for each.
 111  */
 112 static void
 113 walk_partitions(int fd, int startsec, uint_t secsz,
 114     int (*f)(void *, int, uint_t, uint_t), void *arg)
 115 {
 116         uint32_t buf[1024/4];
 117         int bufsize = 1024;
 118         struct mboot *mboot = (struct mboot *)&buf[0];
 119         struct ipart ipart[FD_NUMPART];
 120         uint_t sec = startsec;
 121         uint_t lastsec = sec + 1;
 122         uint_t relsect;
 123         int ext = 0;
 124         int systid;
 125         boolean_t valid;
 126         int i;
 127 
 128         while (sec != lastsec) {
 129                 if (pread(fd, buf, bufsize, (off_t)sec * secsz) != bufsize) {
 130                         break;
 131                 }
 132                 lastsec = sec;
 133                 if (ltohs(mboot->signature) != MBB_MAGIC) {
 134                         break;
 135                 }
 136                 bcopy(mboot->parts, ipart, FD_NUMPART * sizeof (struct ipart));
 137 
 138                 for (i = 0; i < FD_NUMPART; i++) {
 139                         systid = ipart[i].systid;
 140                         relsect = sec + ltohi(ipart[i].relsect);
 141                         if (systid == 0) {
 142                                 continue;
 143                         }
 144                         valid = B_TRUE;
 145                         if (is_dos_extended(systid) && (sec == lastsec)) {
 146                                 sec = startsec + ltohi(ipart[i].relsect);
 147                                 if (ext++ == 0) {
 148                                         relsect = startsec = sec;
 149                                 } else {
 150                                         valid = B_FALSE;
 151                                 }
 152                         }
 153                         if (valid && f(arg, ipart[i].systid, relsect,
 154                             ltohi(ipart[i].numsect)) == WALK_TERMINATE) {
 155                                 return;
 156                         }
 157                 }
 158         }
 159 }
 160 
 161 static int
 162 find_dos_drive_cb(void *arg, int systid, uint_t relsect, uint_t numsect)
 163 {
 164         struct part_find_s *p = arg;
 165 
 166         if (is_dos_drive(systid)) {
 167                 if (++p->count == p->num) {
 168                         p->r_relsect = relsect;
 169                         p->r_numsect = numsect;
 170                         p->r_systid = systid;
 171                         return (WALK_TERMINATE);
 172                 }
 173         }
 174 
 175         return (WALK_CONTINUE);
 176 }
 177 
 178 /*
 179  * Given a dos drive number, return its relative sector number,
 180  * number of sectors in partition and the system id.
 181  */
 182 boolean_t
 183 find_dos_drive(int fd, int num, uint_t secsz, off_t *offset)
 184 {
 185         struct part_find_s p = { 0, 0, 0, 0, 0, 0 };
 186 
 187         p.num = num;
 188 
 189         if (num > 0) {
 190                 walk_partitions(fd, 0, secsz, find_dos_drive_cb, &p);
 191                 if (p.count == num) {
 192                         *offset = (off_t)p.r_relsect * secsz;
 193                         return (B_TRUE);
 194                 }
 195         }
 196 
 197         return (B_FALSE);
 198 }
 199 
 200 static int
 201 get_num_dos_drives_cb(void *arg, int systid, uint_t relsect, uint_t numsect)
 202 {
 203         if (is_dos_drive(systid)) {
 204                 (*(int *)arg)++;
 205         }
 206         return (WALK_CONTINUE);
 207 }
 208 
 209 int
 210 get_num_dos_drives(int fd, uint_t secsz)
 211 {
 212         int count = 0;
 213 
 214         walk_partitions(fd, 0, secsz, get_num_dos_drives_cb, &count);
 215 
 216         return (count);
 217 }
 218 
 219 /*
 220  * Return true if all non-empty slices in vtoc have identical start/size and
 221  * are tagged backup/entire disk.
 222  */
 223 boolean_t
 224 vtoc_one_slice_entire_disk(struct extvtoc *vtoc)
 225 {
 226         int             i;
 227         struct extpartition *p;
 228         diskaddr_t      prev_start;
 229         diskaddr_t      prev_size;
 230 
 231         for (i = 0; i < vtoc->v_nparts; i++) {
 232                 p = &vtoc->v_part[i];
 233                 if (p->p_size == 0) {
 234                         continue;
 235                 }
 236                 if ((p->p_tag != V_BACKUP) && ((p->p_tag != V_UNASSIGNED))) {
 237                         return (B_FALSE);
 238                 }
 239                 if ((i > 0) &&
 240                     ((p->p_start != prev_start) || (p->p_size != prev_size))) {
 241                         return (B_FALSE);
 242                 }
 243                 prev_start = p->p_start;
 244                 prev_size = p->p_size;
 245         }
 246 
 247         return (B_TRUE);
 248 }