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 }