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
61extern	void	preen_addunit(void *cookie, char *dname, int (*cf)(),
62		    void *datap, uint_t unit);
63extern	int	preen_subdev(char *name, struct dk_cinfo *dkiop, void *dp);
64
65static	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 */
73static int
74is_blank(char *line)
75{
76	for (/* nothing */; *line != '\0'; line++)
77		if (!isspace(*line))
78			return (0);
79	return (1);
80}
81
82static int
83get_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 */
134void
135preen_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 */
328out:
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