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