xref: /illumos-gate/usr/src/cmd/hal/utils/fsutils.c (revision 1e8f609027e7c6f96f887b174660e6ca42d775fc)
1 /***************************************************************************
2  *
3  * fsutils.c : filesystem utilities
4  *
5  * Copyright 2006 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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
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  */
39 boolean_t
40 dos_to_dev(char *path, char **devpath, int *num)
41 {
42 	char *p;
43 
44 	if ((p = strrchr(path, ':')) == NULL) {
45 		return (B_FALSE);
46 	}
47 	if ((*num = atoi(p + 1)) == 0) {
48 		return (B_FALSE);
49 	}
50 	p[0] = '\0';
51 	*devpath = strdup(path);
52 	p[0] = ':';
53 	return (*devpath != NULL);
54 }
55 
56 char *
57 get_slice_name (char *devlink)
58 {
59 	char	*part, *slice, *disk;
60 	char	*s = NULL;
61 	char	*p;
62 
63 	if ((p = strstr(devlink, "/lofi/")) != 0) {
64 		return (p + sizeof ("/lofi/") - 1);
65 	}
66 
67 	part = strrchr(devlink, 'p');
68 	slice = strrchr(devlink, 's');
69 	disk = strrchr(devlink, 'd');
70 
71 	if ((part != NULL) && (part > slice) && (part > disk)) {
72 		s = part;
73 	} else if ((slice != NULL) && (slice > disk)) {
74 		s = slice;
75 	} else {
76 		s = disk;
77 	}
78 	if ((s != NULL) && isdigit(s[1])) {
79 		return (s);
80 	} else {
81 		return ("");
82 	}
83 }
84 
85 boolean_t
86 is_dos_drive(uchar_t type)
87 {
88 	return ((type == 1) || (type == 4) || (type == 5) || (type == 6) ||
89 	    ((type >= 8) && (type <= 0xf)));
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 	int	r_relsect;
104 	int	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, int (*f)(void *, int, int, int),
114     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 	int sec = startsec;
121 	int lastsec = sec + 1;
122 	int 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 * 512) != 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, int relsect, int 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, int *relsect, int *numsect, int *systid)
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, find_dos_drive_cb, &p);
191 		if (p.count == num) {
192 			*relsect = p.r_relsect;
193 			*numsect = p.r_numsect;
194 			*systid = p.r_systid;
195 			return (B_TRUE);
196 		}
197 	}
198 
199 	return (B_FALSE);
200 }
201 
202 static int
203 get_num_dos_drives_cb(void *arg, int systid, int relsect, int numsect)
204 {
205 	if (is_dos_drive(systid)) {
206 		(*(int *)arg)++;
207 	}
208 	return (WALK_CONTINUE);
209 }
210 
211 int
212 get_num_dos_drives(int fd)
213 {
214 	int count = 0;
215 
216 	walk_partitions(fd, 0, get_num_dos_drives_cb, &count);
217 
218 	return (count);
219 }
220 
221 /*
222  * Return true if all non-empty slices in vtoc have identical start/size and
223  * are tagged backup/entire disk.
224  */
225 boolean_t
226 vtoc_one_slice_entire_disk(struct vtoc *vtoc)
227 {
228 	int		i;
229 	struct partition *p;
230 	daddr_t		prev_start;
231 	long		prev_size;
232 
233 	for (i = 0; i < vtoc->v_nparts; i++) {
234 		p = &vtoc->v_part[i];
235 		if (p->p_size == 0) {
236 			continue;
237 		}
238 		if ((p->p_tag != V_BACKUP) && ((p->p_tag != V_UNASSIGNED))) {
239 			return (B_FALSE);
240 		}
241 		if ((i > 0) &&
242 		    ((p->p_start != prev_start) || (p->p_size != prev_size))) {
243 			return (B_FALSE);
244 		}
245 		prev_start = p->p_start;
246 		prev_size = p->p_size;
247 	}
248 
249 	return (B_TRUE);
250 }
251