xref: /titanic_41/usr/src/lib/lvm/libpreen/common/mdpreen.c (revision 6185db853e024a486ff8837e6784dd290d866112)
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
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
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  * If the name contains a diskset name, it is parsed out and returned.
128  * The dev_path can be either a md pathname /dev/md/rdsk/d0 or a path
129  * name that contains a diskset /dev/md/red/rdsk/d0.
130  */
131 
132 static char *
133 parse_path(char *dev_path)
134 {
135 	char *cpdev;
136 	char *cp, *cpp;
137 	char *setname;
138 	size_t size;
139 
140 	/*
141 	 * paths are /dev/md/rdsk/dx or /dev/md/<setname>/rdsk/dx
142 	 * cp points to /rdsk/dx. Scan back to the previous slash.
143 	 * If this matches "dev", then path is a local set.
144 	 *
145 	 * The /rdsk/d pattern in strstr is used so that users with
146 	 * a twisted mind can create a diskset called "rdsk" and
147 	 * would still want everything to work!!
148 	 */
149 	cp = strstr(dev_path, "/rdsk/d");
150 
151 	for (cpdev = cp - 1; *cpdev != '/'; cpdev--);
152 	cpdev = cpdev - 3; /* backspace 3 char */
153 	if (strncmp(cpdev, "dev", strlen("dev")) == 0)
154 		return (Strdup(MD_LOCAL_NAME));
155 
156 	/*
157 	 * extract the setname from the path
158 	 */
159 	cpp = cp;
160 	for (cp--; *cp != '/'; cp--);
161 	size = (size_t)(cpp - cp);
162 	setname = (char *)Malloc(size);
163 	(void) strlcpy(setname, (const char *)(cp + 1), size);
164 
165 	return (setname);
166 }
167 
168 /*
169  * This routine is called from preenlib the first time. It is then
170  * recursively called through preen_subdev.
171  *
172  * The argument passed in (uname) starts with the special device from
173  * /etc/vfstab. Recursive calls pass in the underlying physical device
174  * names.
175  */
176 void
177 preen_build_devs(
178 	char		*uname,		/* name of metadevice */
179 	struct dk_cinfo	*dkiop,		/* associated controller info */
180 	void		*dp		/* magic info */
181 )
182 {
183 	char		*setname = NULL;
184 	mdsetname_t	*sp;
185 	mdname_t	*namep;		/* metadevice name */
186 	mdnamelist_t	*nlp = NULL;	/* list of real devices */
187 	mdnamelist_t	*p;
188 	devid_nmlist_t	*nm_list = NULL;
189 	md_error_t	status = mdnullerror;
190 	md_error_t	*ep = &status;
191 	int		ep_valid = 0;	/* does ep contain a real error */
192 	struct stat	statb;
193 	static int	md_major = -1;
194 	side_t		sideno;
195 
196 	/*
197 	 * The rest of the code in this library can't work within a
198 	 * non-global zone so we just return the top level metadevice back
199 	 * to be fscked.
200 	 */
201 	if (getzoneid() != GLOBAL_ZONEID) {
202 		preen_addunit(dp, dkiop->dki_dname, NULL, NULL,
203 		    dkiop->dki_unit);
204 		return;
205 	}
206 
207 	if (stat(uname, &statb) != 0)
208 		return;
209 
210 	if (md_major == -1 &&
211 		get_major_from_n2m(MD_MODULE, &md_major) != 0)
212 		return;
213 
214 	/*
215 	 * If the path passed in is not a metadevice, then add that
216 	 * device to the list (preen_addunit) since it has to be a
217 	 * physical device.
218 	 */
219 
220 	if (major(statb.st_rdev) != md_major) {
221 		preen_addunit(dp, dkiop->dki_dname, NULL, NULL,
222 		    dkiop->dki_unit);
223 		return;
224 	}
225 	/*
226 	 * Bind to the cluster library
227 	 */
228 
229 	if (sdssc_bind_library() == SDSSC_ERROR)
230 		return;
231 
232 	if (md_init_daemon("fsck", ep) != 0) {
233 		ep_valid = 1;
234 		goto out;
235 	}
236 
237 	/*
238 	 * parse the path name to get the diskset name.
239 	 */
240 
241 	setname = parse_path(uname);
242 	if ((sp = metasetname(setname, ep)) == NULL) {
243 		ep_valid = 1;
244 		goto out;
245 	}
246 
247 	/* check for ownership */
248 	if (meta_check_ownership(sp, ep) != 0) {
249 		/*
250 		 * Don't own the set but we are here implies
251 		 * that this is a clustered proxy device. Simply add
252 		 * the unit.
253 		 */
254 		preen_addunit(dp, dkiop->dki_dname, NULL, NULL,
255 		    dkiop->dki_unit);
256 		ep_valid = 1;
257 		goto out;
258 	}
259 
260 	/*
261 	 * get list of underlying physical devices.
262 	 */
263 	if ((namep = metaname(&sp, uname, UNKNOWN, ep)) == NULL) {
264 		ep_valid = 1;
265 		goto out;
266 	}
267 
268 	if (namep->dev == NODEV64) {
269 		goto out;
270 	}
271 
272 	if (meta_getdevs(sp, namep, &nlp, ep) != 0) {
273 		ep_valid = 1;
274 		goto out;
275 	}
276 
277 	if ((sideno = getmyside(sp, ep)) == MD_SIDEWILD) {
278 		ep_valid = 1;
279 		goto out;
280 	}
281 
282 	/* gather and add the underlying devs */
283 	for (p = nlp; (p != NULL); p = p->next) {
284 		mdname_t	*devnp = p->namep;
285 		int		fd;
286 		struct dk_cinfo	cinfo;
287 		ddi_devid_t	md_did;
288 		char		*devname;
289 		char		*minor_name = NULL;
290 		char		mname[MAXPATHLEN];
291 
292 		/*
293 		 * we don't want to use the rname anymore because
294 		 * that may have changed. Use the device id information
295 		 * to find the correct ctd name and open based on that.
296 		 * If there isn't a devid or we have a did device, then
297 		 * use the rname. In clustering, it's corrected for us.
298 		 * If no devid it's at least worth a try.
299 		 */
300 		if (((md_did = meta_getdidbykey(sp->setno, sideno,
301 		    devnp->key, ep)) == NULL) || ((minor_name =
302 		    meta_getdidminorbykey(sp->setno, sideno,
303 		    devnp->key, ep)) == NULL)) {
304 			devname = devnp->rname;
305 			if (md_did)
306 				Free(md_did);
307 		} else {
308 			if (strstr(minor_name, ",raw") == NULL) {
309 				(void) snprintf(mname, MAXPATHLEN, "%s,raw",
310 				    minor_name);
311 			} else {
312 				(void) snprintf(mname, MAXPATHLEN, "%s",
313 				    minor_name);
314 			}
315 
316 			/*
317 			 * We need to make sure we call this with a specific
318 			 * mname (raw mname) so that we get the exact slice
319 			 * with the given device id. Otherwise we could try
320 			 * to open a slice that doesn't really exist.
321 			 */
322 			if (meta_deviceid_to_nmlist("/dev", md_did,
323 			    mname, &nm_list) != 0) {
324 				(void) mdsyserror(ep, errno, devnp->rname);
325 				ep_valid = 1;
326 				Free(md_did);
327 				Free(minor_name);
328 				goto out;
329 			}
330 			devname = Strdup(nm_list->devname);
331 			Free(md_did);
332 			Free(minor_name);
333 			devid_free_nmlist(nm_list);
334 		}
335 		/* get device name and (real) cinfo */
336 		if ((fd = open(devname, O_RDONLY, 0)) < 0) {
337 			(void) mdsyserror(ep, errno, devname);
338 			ep_valid = 1;
339 			/*
340 			 * We need to scan all sub devices even if some fail
341 			 * since exit here would end up in not finishing fsck
342 			 * on all devices and prevent us from going into
343 			 * multiuser mode.
344 			 */
345 			continue;
346 		}
347 
348 		if (ioctl(fd, DKIOCINFO, &cinfo) != 0) {
349 			(void) mdsyserror(ep, errno, devname);
350 			(void) close(fd);
351 			ep_valid = 1;
352 			/* Continue here too. See comment from before */
353 			continue;
354 		}
355 		(void) close(fd);	/* sd/ssd bug */
356 
357 		/*
358 		 * preen_subdev fails when the device name has been
359 		 * resolved to the physical layer. Hence it is added
360 		 * to preen_addunit.
361 		 */
362 		if (preen_subdev(devname, &cinfo, dp) != 0) {
363 			preen_addunit(dp, cinfo.dki_dname, NULL, NULL,
364 			    cinfo.dki_unit);
365 		}
366 	}
367 
368 	/* cleanup, if we fail, just add this composite device to the list */
369 out:
370 	if (setname != NULL)
371 		Free(setname);
372 	if (ep_valid != 0) {
373 		mde_perror(&status, "");
374 		mdclrerror(&status);
375 	}
376 	metafreenamelist(nlp);
377 }
378