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 }