1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 /*
29 * preenlib interface for SVM.
30 *
31 * On startup fsck attempts to check filesystems in parallel. However
32 * running mutiple fscks on the same disk at the same time
33 * significantly degrades the performance. fsck code avoids such
34 * behavior. To analyse such patterns it needs the physical disk
35 * instance. preen_build_devs provides that information for
36 * filesystems that are on top of metadevices.
37 */
38
39 #include <ctype.h>
40 #include <meta.h>
41 #include <limits.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <zone.h>
45
46 #include <sdssc.h>
47
48 #define MAX_N2M_ALIAS_LINE (2*FILENAME_MAX + 1)
49 #define NAME_TO_MAJOR "/etc/name_to_major"
50 #define MD_MODULE "md"
51
52 /*
53 * Macros to produce a quoted string containing the value of a
54 * preprocessor macro. For example, if SIZE is defined to be 256,
55 * VAL2STR(SIZE) is "256". This is used to construct format
56 * strings for scanf-family functions below.
57 */
58 #define QUOTE(x) #x
59 #define VAL2STR(x) QUOTE(x)
60
61 extern void preen_addunit(void *cookie, char *dname, int (*cf)(),
62 void *datap, uint_t unit);
63 extern int preen_subdev(char *name, struct dk_cinfo *dkiop, void *dp);
64
65 static int is_blank(char *);
66
67 /*
68 * is_blank() returns 1 (true) if a line specified is composed of
69 * whitespace characters only. otherwise, it returns 0 (false).
70 *
71 * Note. the argument (line) must be null-terminated.
72 */
73 static int
is_blank(char * line)74 is_blank(char *line)
75 {
76 for (/* nothing */; *line != '\0'; line++)
77 if (!isspace(*line))
78 return (0);
79 return (1);
80 }
81
82 static int
get_major_from_n2m(char * modname,int * major)83 get_major_from_n2m(char *modname, int *major)
84 {
85 FILE *fp;
86 char drv[FILENAME_MAX + 1];
87 int entry;
88 int found = 0;
89 char line[MAX_N2M_ALIAS_LINE], *cp;
90 int status = 0;
91
92 if ((fp = fopen(NAME_TO_MAJOR, "r")) == NULL) {
93 return (-1);
94 }
95
96 while ((fgets(line, sizeof (line), fp) != NULL) &&
97 status == 0) {
98 /* cut off comments starting with '#' */
99 if ((cp = strchr(line, '#')) != NULL)
100 *cp = '\0';
101 /* ignore comment or blank lines */
102 if (is_blank(line))
103 continue;
104 /* sanity-check */
105 if (sscanf(line, "%" VAL2STR(FILENAME_MAX) "s %d",
106 drv, &entry) != 2) {
107 status = -1;
108 }
109 if (strcmp(drv, modname) == 0) {
110 *major = entry;
111 found = 1;
112 break;
113 }
114 }
115
116 /*
117 * if no match is found return -1
118 */
119 if (found == 0)
120 status = -1;
121
122 (void) fclose(fp);
123 return (status);
124 }
125
126 /*
127 * This routine is called from preenlib the first time. It is then
128 * recursively called through preen_subdev.
129 *
130 * The argument passed in (uname) starts with the special device from
131 * /etc/vfstab. Recursive calls pass in the underlying physical device
132 * names.
133 */
134 void
preen_build_devs(char * uname,struct dk_cinfo * dkiop,void * dp)135 preen_build_devs(
136 char *uname, /* name of metadevice */
137 struct dk_cinfo *dkiop, /* associated controller info */
138 void *dp /* magic info */
139 )
140 {
141 char *setname = NULL;
142 char *tname = NULL;
143 mdsetname_t *sp;
144 mdname_t *namep; /* metadevice name */
145 mdnamelist_t *nlp = NULL; /* list of real devices */
146 mdnamelist_t *p;
147 devid_nmlist_t *nm_list = NULL;
148 md_error_t status = mdnullerror;
149 md_error_t *ep = &status;
150 int ep_valid = 0; /* does ep contain a real error */
151 struct stat statb;
152 static int md_major = -1;
153 side_t sideno;
154
155 /*
156 * The rest of the code in this library can't work within a
157 * non-global zone so we just return the top level metadevice back
158 * to be fscked.
159 */
160 if (getzoneid() != GLOBAL_ZONEID) {
161 preen_addunit(dp, dkiop->dki_dname, NULL, NULL,
162 dkiop->dki_unit);
163 return;
164 }
165
166 if (stat(uname, &statb) != 0)
167 return;
168
169 if (md_major == -1 &&
170 get_major_from_n2m(MD_MODULE, &md_major) != 0)
171 return;
172
173 /*
174 * If the path passed in is not a metadevice, then add that
175 * device to the list (preen_addunit) since it has to be a
176 * physical device.
177 */
178
179 if (major(statb.st_rdev) != md_major) {
180 preen_addunit(dp, dkiop->dki_dname, NULL, NULL,
181 dkiop->dki_unit);
182 return;
183 }
184 /*
185 * Bind to the cluster library
186 */
187
188 if (sdssc_bind_library() == SDSSC_ERROR)
189 return;
190
191 if (md_init_daemon("fsck", ep) != 0) {
192 ep_valid = 1;
193 goto out;
194 }
195
196 /*
197 * parse the path name to get the diskset name.
198 */
199 parse_device(NULL, uname, &tname, &setname);
200 Free(tname);
201 if ((sp = metasetname(setname, ep)) == NULL) {
202 ep_valid = 1;
203 goto out;
204 }
205
206 /* check for ownership */
207 if (meta_check_ownership(sp, ep) != 0) {
208 /*
209 * Don't own the set but we are here implies
210 * that this is a clustered proxy device. Simply add
211 * the unit.
212 */
213 preen_addunit(dp, dkiop->dki_dname, NULL, NULL,
214 dkiop->dki_unit);
215 ep_valid = 1;
216 goto out;
217 }
218
219 /*
220 * get list of underlying physical devices.
221 */
222 if ((namep = metaname(&sp, uname, UNKNOWN, ep)) == NULL) {
223 ep_valid = 1;
224 goto out;
225 }
226
227 if (namep->dev == NODEV64) {
228 goto out;
229 }
230
231 if (meta_getdevs(sp, namep, &nlp, ep) != 0) {
232 ep_valid = 1;
233 goto out;
234 }
235
236 if ((sideno = getmyside(sp, ep)) == MD_SIDEWILD) {
237 ep_valid = 1;
238 goto out;
239 }
240
241 /* gather and add the underlying devs */
242 for (p = nlp; (p != NULL); p = p->next) {
243 mdname_t *devnp = p->namep;
244 int fd;
245 struct dk_cinfo cinfo;
246 ddi_devid_t md_did;
247 char *devname;
248 char *minor_name = NULL;
249 char mname[MAXPATHLEN];
250
251 /*
252 * we don't want to use the rname anymore because
253 * that may have changed. Use the device id information
254 * to find the correct ctd name and open based on that.
255 * If there isn't a devid or we have a did device, then
256 * use the rname. In clustering, it's corrected for us.
257 * If no devid it's at least worth a try.
258 */
259 if (((md_did = meta_getdidbykey(sp->setno, sideno,
260 devnp->key, ep)) == NULL) || ((minor_name =
261 meta_getdidminorbykey(sp->setno, sideno,
262 devnp->key, ep)) == NULL)) {
263 devname = devnp->rname;
264 if (md_did)
265 Free(md_did);
266 } else {
267 if (strstr(minor_name, ",raw") == NULL) {
268 (void) snprintf(mname, MAXPATHLEN, "%s,raw",
269 minor_name);
270 } else {
271 (void) snprintf(mname, MAXPATHLEN, "%s",
272 minor_name);
273 }
274
275 /*
276 * We need to make sure we call this with a specific
277 * mname (raw mname) so that we get the exact slice
278 * with the given device id. Otherwise we could try
279 * to open a slice that doesn't really exist.
280 */
281 if (meta_deviceid_to_nmlist("/dev", md_did,
282 mname, &nm_list) != 0) {
283 (void) mdsyserror(ep, errno, devnp->rname);
284 ep_valid = 1;
285 Free(md_did);
286 Free(minor_name);
287 goto out;
288 }
289 devname = Strdup(nm_list->devname);
290 Free(md_did);
291 Free(minor_name);
292 devid_free_nmlist(nm_list);
293 }
294 /* get device name and (real) cinfo */
295 if ((fd = open(devname, O_RDONLY, 0)) < 0) {
296 (void) mdsyserror(ep, errno, devname);
297 ep_valid = 1;
298 /*
299 * We need to scan all sub devices even if some fail
300 * since exit here would end up in not finishing fsck
301 * on all devices and prevent us from going into
302 * multiuser mode.
303 */
304 continue;
305 }
306
307 if (ioctl(fd, DKIOCINFO, &cinfo) != 0) {
308 (void) mdsyserror(ep, errno, devname);
309 (void) close(fd);
310 ep_valid = 1;
311 /* Continue here too. See comment from before */
312 continue;
313 }
314 (void) close(fd); /* sd/ssd bug */
315
316 /*
317 * preen_subdev fails when the device name has been
318 * resolved to the physical layer. Hence it is added
319 * to preen_addunit.
320 */
321 if (preen_subdev(devname, &cinfo, dp) != 0) {
322 preen_addunit(dp, cinfo.dki_dname, NULL, NULL,
323 cinfo.dki_unit);
324 }
325 }
326
327 /* cleanup, if we fail, just add this composite device to the list */
328 out:
329 if (setname != NULL)
330 Free(setname);
331 if (ep_valid != 0) {
332 mde_perror(&status, "");
333 mdclrerror(&status);
334 }
335 metafreenamelist(nlp);
336 }
337