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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*LINTLIBRARY*/
28 
29 #include <sys/types.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <sys/param.h>
36 #include <sys/stat.h>
37 #include <sys/sysmacros.h>
38 #include <sys/vfstab.h>
39 #include <sys/lofi.h>
40 #include <sys/ramdisk.h>
41 #include <sys/fssnap_if.h>
42 #include "libadm.h"
43 
44 /*
45  * Globals:
46  *	getfullrawname - returns a fully-qualified raw device name
47  *	getfullblkname - returns a fully-qualified block device name
48  *
49  * These two routines take a device pathname and return corresponding
50  * the raw or block device name.
51  *
52  * First the device name is fully qualified:
53  * 	If the device name does not start with a '/' or starts with
54  *	'./' then the current working directory is added to the beginning
55  *	of the pathname.
56  *
57  *	If the device name starts with a '../' then all but the last
58  *	sub-directory of the current working directory is added to the
59  *	the beginning of the pathname.
60  *
61  * Second if the fully-qualified device name given is the raw/block
62  * device that is being asked for then the fully-qualified device name is
63  * returned.
64  *
65  * Third if an entry is found in /etc/vfstab which matches the given name
66  * then the corresponding raw/block device is returned.  This allows
67  * non-standard names to be converted (.i.e., block device "/dev/joe" can
68  * be converted to raw device "/dev/fred", via this mechanism).
69  *
70  * Last standard names are converted.  Standard names are those
71  * with a '/dsk/' for block or '/rdsk/' for raw sub-directory components
72  * in the device name. Or, the filename component has an 'r' for raw or
73  * no 'r' for block (e.g., rsd0a <=> sd0a).
74  *
75  * Caveat:
76  * It is assumed that the block and raw devices have the
77  * same device number, and this is used to verify the conversion
78  * happened corretly.  If this happens not to be true, due to mapping
79  * of minor numbers or sometheing, then entries can be put in the
80  * the '/etc/vfstab' file to over-ride this checking.
81  *
82  *
83  * Return Values:
84  * 	raw/block device name	- (depending on which routine is used)
85  *	null string		- When the conversion failed
86  *	null pointer		- malloc problems
87  *
88  * It is up to the user of these routines to free the memory, of
89  * the device name or null string returned by these library routines,
90  * when appropriate by the application.
91  */
92 #define	GET_BLK	0
93 #define	GET_RAW	1
94 
95 static int test_if_blk(char *, dev_t);
96 static int test_if_raw(char *, dev_t);
97 static char *getblkcomplete(char *, struct stat64 *);
98 static char *getrawcomplete(char *, struct stat64 *);
99 
100 /*
101  * getfullname() - Builds a fully qualified pathname.
102  *		   This handles . and .. as well.
103  *		   NOTE: This is different from realpath(3C) because
104  *			 it does not follow links.
105  */
106 static char *
getfullname(char * path)107 getfullname(char *path)
108 {
109 	char	cwd[MAXPATHLEN];
110 	char	*c;
111 	char	*wa;
112 	size_t	len;
113 
114 	if (*path == '/')
115 		return (strdup(path));
116 
117 	if (getcwd(cwd, sizeof (cwd)) == NULL)
118 		return (strdup(""));
119 
120 	/* handle . and .. */
121 	if (strncmp(path, "./", 2) == 0) {
122 		/* strip the ./ from the given path */
123 		path += 2;
124 	} else if (strncmp(path, "../", 3) == 0) {
125 		/* strip the last directory component from cwd */
126 		c = strrchr(cwd, '/');
127 		*c = '\0';
128 
129 		/* strip the ../ from the given path */
130 		path += 3;
131 	}
132 
133 	/*
134 	 * Adding 2 takes care of slash and null terminator.
135 	 */
136 	len = strlen(cwd) + strlen(path) + 2;
137 	if ((wa = malloc(len)) == NULL)
138 		return (NULL);
139 
140 	(void) strcpy(wa, cwd);
141 	(void) strcat(wa, "/");
142 	(void) strcat(wa, path);
143 
144 	return (wa);
145 }
146 
147 /*
148  * test the path/fname to see if is blk special
149  */
150 static int
test_if_blk(char * new_path,dev_t raw_dev)151 test_if_blk(char *new_path, dev_t raw_dev)
152 {
153 	struct stat64	buf;
154 
155 	/* check if we got a char special file */
156 	if (stat64(new_path, &buf) != 0)
157 		return (0);
158 
159 	if (!S_ISBLK(buf.st_mode))
160 		return (0);
161 
162 	if (raw_dev != buf.st_rdev)
163 		return (0);
164 
165 	return (1);
166 }
167 
168 /*
169  * test the path/fname to see if is char special
170  */
171 static int
test_if_raw(char * new_path,dev_t blk_dev)172 test_if_raw(char *new_path, dev_t blk_dev)
173 {
174 	struct stat64	buf;
175 
176 	/* check if we got a char special file */
177 	if (stat64(new_path, &buf) != 0)
178 		return (0);
179 
180 	if (!S_ISCHR(buf.st_mode))
181 		return (0);
182 
183 	if (blk_dev != buf.st_rdev)
184 		return (0);
185 
186 	return (1);
187 }
188 
189 /*
190  * complete getblkrawname() for blk->raw to handle volmgt devices
191  */
192 
193 static char *
getblkcomplete(char * cp,struct stat64 * dat)194 getblkcomplete(char *cp, struct stat64 *dat)
195 {
196 	char 		*dp;
197 	char		*new_path;
198 	char		c;
199 
200 	/* ok, so we either have a bad device or a floppy */
201 
202 	/* try the rfd# form */
203 	if ((dp = strstr(cp, "/rfd")) != NULL) {
204 		if ((new_path = malloc(strlen(cp))) == NULL)
205 			return (NULL);
206 
207 		c = *++dp;			/* save the 'r' */
208 		*dp = '\0';			/* replace it with a null */
209 		(void) strcpy(new_path, cp);	/* save first part of it */
210 		*dp++ = c;			/* give the 'r' back */
211 		(void) strcat(new_path, dp);	/* copy, skipping the 'r' */
212 
213 		if (test_if_blk(new_path, dat->st_rdev))
214 			return (new_path);
215 
216 		free(new_path);
217 		return (strdup(""));
218 	}
219 
220 	/* try the rdiskette form */
221 	if ((dp = strstr(cp, "/rdiskette")) != NULL) {
222 		if ((new_path = malloc(strlen(cp))) == NULL)
223 			return (NULL);
224 
225 		c = *++dp;			/* save the 'r' */
226 		*dp = '\0';			/* replace it with a null */
227 		(void) strcpy(new_path, cp);	/* save first part of it */
228 		*dp++ = c;			/* give the 'r' back */
229 		(void) strcat(new_path, dp);	/* copy, skipping the 'r' */
230 
231 		if (test_if_blk(new_path, dat->st_rdev))
232 			return (new_path);
233 
234 		free(new_path);
235 		return (strdup(""));
236 	}
237 
238 	/* no match found */
239 	return (strdup(""));
240 }
241 
242 /*
243  * complete getfullrawname() for raw->blk to handle volmgt devices
244  */
245 
246 static char *
getrawcomplete(char * cp,struct stat64 * dat)247 getrawcomplete(char *cp, struct stat64 *dat)
248 {
249 	char 		*dp;
250 	char		*new_path;
251 	char		c;
252 
253 	/* ok, so we either have a bad device or a floppy */
254 
255 	/* try the fd# form */
256 	if ((dp = strstr(cp, "/fd")) != NULL) {
257 		/* malloc path for new_path to hold raw */
258 		if ((new_path = malloc(strlen(cp)+2)) == NULL)
259 			return (NULL);
260 
261 		c = *++dp;			/* save the 'f' */
262 		*dp = '\0';			/* replace it with a null */
263 		(void) strcpy(new_path, cp);	/* save first part of it */
264 		*dp = c;			/* put the 'f' back */
265 		(void) strcat(new_path, "r");	/* insert an 'r' */
266 		(void) strcat(new_path, dp);	/* copy the rest */
267 
268 		if (test_if_raw(new_path, dat->st_rdev))
269 			return (new_path);
270 
271 		free(new_path);
272 	}
273 
274 	/* try the diskette form */
275 	if ((dp = strstr(cp, "/diskette")) != NULL) {
276 		/* malloc path for new_path to hold raw */
277 		if ((new_path = malloc(strlen(cp)+2)) == NULL)
278 			return (NULL);
279 
280 		c = *++dp;			/* save at 'd' */
281 		*dp = '\0';			/* replace it with a null */
282 		(void) strcpy(new_path, cp);	/* save first part */
283 		*dp = c;			/* put the 'd' back */
284 		(void) strcat(new_path, "r");	/* insert an 'r' */
285 		(void) strcat(new_path, dp);	/* copy the rest */
286 
287 		if (test_if_raw(new_path, dat->st_rdev))
288 			return (new_path);
289 
290 		free(new_path);
291 		return (strdup(""));
292 	}
293 
294 	/* failed to build raw name, return null string */
295 	return (strdup(""));
296 
297 
298 
299 }
300 
301 static char *
getvfsspecial(char * path,int raw_special)302 getvfsspecial(char *path, int raw_special)
303 {
304 	FILE		*fp;
305 	struct vfstab	vp;
306 	struct vfstab	ref_vp;
307 
308 	if ((fp = fopen("/etc/vfstab", "r")) == NULL)
309 		return (NULL);
310 
311 	(void) memset(&ref_vp, 0, sizeof (struct vfstab));
312 
313 	if (raw_special)
314 		ref_vp.vfs_special = path;
315 	else
316 		ref_vp.vfs_fsckdev = path;
317 
318 	if (getvfsany(fp, &vp, &ref_vp)) {
319 		(void) fclose(fp);
320 		return (NULL);
321 	}
322 
323 	(void) fclose(fp);
324 
325 	if (raw_special)
326 		return (vp.vfs_fsckdev);
327 
328 	return (vp.vfs_special);
329 }
330 
331 /*
332  * change the device name to a block device name
333  */
334 char *
getfullblkname(char * cp)335 getfullblkname(char *cp)
336 {
337 	struct stat64	buf;
338 	char		*dp;
339 	char		*new_path;
340 	dev_t		raw_dev;
341 
342 	if (cp == NULL)
343 		return (strdup(""));
344 
345 	/*
346 	 * Create a fully qualified name.
347 	 */
348 	if ((cp = getfullname(cp)) == NULL)
349 		return (NULL);
350 
351 	if (*cp == '\0')
352 		return (cp);
353 
354 	if (stat64(cp, &buf) != 0) {
355 		free(cp);
356 		return (strdup(""));
357 	}
358 
359 	if (S_ISBLK(buf.st_mode))
360 		return (cp);
361 
362 	if (!S_ISCHR(buf.st_mode)) {
363 		free(cp);
364 		return (strdup(""));
365 	}
366 
367 	if ((dp = getvfsspecial(cp, GET_BLK)) != NULL) {
368 		free(cp);
369 		return (strdup(dp));
370 	}
371 
372 	raw_dev = buf.st_rdev;
373 
374 	/*
375 	 * We have a raw device name, go find the block name.
376 	 */
377 	if ((dp = strstr(cp, "/rdsk/")) == NULL &&
378 	    (dp = strstr(cp, "/" LOFI_CHAR_NAME "/")) == NULL &&
379 	    (dp = strstr(cp, "/" RD_CHAR_NAME "/")) == NULL &&
380 	    (dp = strstr(cp, "/" SNAP_CHAR_NAME "/")) == NULL &&
381 	    (dp = strrchr(cp, '/')) == NULL) {
382 		/* this is not really possible */
383 		free(cp);
384 		return (strdup(""));
385 	}
386 	dp++;
387 	if (*dp != 'r') {
388 		dp = getblkcomplete(cp, &buf);
389 		free(cp);
390 		return (dp);
391 	}
392 	if ((new_path = malloc(strlen(cp))) == NULL) {
393 		free(cp);
394 		return (NULL);
395 	}
396 	(void) strncpy(new_path, cp, dp - cp);
397 
398 	/* fill in the rest of the unraw name */
399 	(void) strcpy(new_path + (dp - cp), dp + 1);
400 
401 	if (test_if_blk(new_path, raw_dev)) {
402 		free(cp);
403 		/* block name was found, return it here */
404 		return (new_path);
405 	}
406 	free(new_path);
407 
408 	dp = getblkcomplete(cp, &buf);
409 	free(cp);
410 	return (dp);
411 }
412 
413 /*
414  * change the device name to a raw devname
415  */
416 char *
getfullrawname(char * cp)417 getfullrawname(char *cp)
418 {
419 	struct stat64	buf;
420 	char		*dp;
421 	char		*new_path;
422 	dev_t		blk_dev;
423 
424 	if (cp == NULL)
425 		return (strdup(""));
426 
427 	/*
428 	 * Create a fully qualified name.
429 	 */
430 	if ((cp = getfullname(cp)) == NULL)
431 		return (NULL);
432 
433 	if (*cp == '\0')
434 		return (cp);
435 
436 	if (stat64(cp, &buf) != 0) {
437 		free(cp);
438 		return (strdup(""));
439 	}
440 
441 	if (S_ISCHR(buf.st_mode))
442 		return (cp);
443 
444 	if (!S_ISBLK(buf.st_mode)) {
445 		free(cp);
446 		return (strdup(""));
447 	}
448 
449 	blk_dev = buf.st_rdev;
450 
451 	if ((dp = getvfsspecial(cp, GET_RAW)) != NULL) {
452 		free(cp);
453 		return (strdup(dp));
454 	}
455 
456 	/*
457 	 * We have a block device name, go find the raw name.
458 	 */
459 	if ((dp = strstr(cp, "/dsk/")) == NULL &&
460 	    (dp = strstr(cp, "/" LOFI_BLOCK_NAME "/")) == NULL &&
461 	    (dp = strstr(cp, "/" RD_BLOCK_NAME "/")) == NULL &&
462 	    (dp = strstr(cp, "/" SNAP_BLOCK_NAME "/")) == NULL &&
463 	    (dp = strrchr(cp, '/')) == NULL) {
464 		/* this is not really possible */
465 		free(cp);
466 		return (strdup(""));
467 	}
468 	dp++;
469 
470 	if ((new_path = malloc(strlen(cp)+2)) == NULL) {
471 		free(cp);
472 		return (NULL);
473 	}
474 	(void) strncpy(new_path, cp, dp - cp);
475 	/* fill in the rest of the raw name */
476 	new_path[dp - cp] = 'r';
477 	(void) strcpy(new_path + (dp - cp) + 1, dp);
478 
479 	if (test_if_raw(new_path, blk_dev)) {
480 		free(cp);
481 		return (new_path);
482 	}
483 	free(new_path);
484 
485 	dp = getrawcomplete(cp, &buf);
486 	free(cp);
487 	return (dp);
488 }
489