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