xref: /illumos-gate/usr/src/cmd/lofiadm/main.c (revision 7800901e60d340b6af88e94a2149805dcfcaaf56)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * lofiadm - administer lofi(7d). Very simple, add and remove file<->device
28  * associations, and display status. All the ioctls are private between
29  * lofi and lofiadm, and so are very simple - device information is
30  * communicated via a minor number.
31  */
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/lofi.h>
38 #include <sys/stat.h>
39 #include <stdio.h>
40 #include <fcntl.h>
41 #include <locale.h>
42 #include <string.h>
43 #include <errno.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <stropts.h>
47 #include <libdevinfo.h>
48 #include "utils.h"
49 
50 static const char USAGE[] =
51 	"Usage: %s -a file [ device ]\n"
52 	"       %s -d file | device \n"
53 	"       %s [ device | file ]\n";
54 
55 static const char *pname;
56 static int	addflag = 0;
57 static int	deleteflag = 0;
58 static int	errflag = 0;
59 
60 #define	FORMAT "%-20s     %s\n"
61 
62 /*
63  * Print the list of all the mappings. Including a header.
64  */
65 static void
66 print_mappings(int fd)
67 {
68 	struct lofi_ioctl li;
69 	int	minor;
70 	int	maxminor;
71 	char	path[MAXPATHLEN + 1];
72 
73 	li.li_minor = 0;
74 	if (ioctl(fd, LOFI_GET_MAXMINOR, &li) == -1) {
75 		perror("ioctl");
76 		exit(E_ERROR);
77 	}
78 
79 	maxminor = li.li_minor;
80 
81 	(void) printf(FORMAT, "Block Device", "File");
82 	for (minor = 1; minor <= maxminor; minor++) {
83 		li.li_minor = minor;
84 		if (ioctl(fd, LOFI_GET_FILENAME, &li) == -1) {
85 			if (errno == ENXIO)
86 				continue;
87 			perror("ioctl");
88 			break;
89 		}
90 		(void) snprintf(path, sizeof (path), "/dev/%s/%d",
91 		    LOFI_BLOCK_NAME, minor);
92 		(void) printf(FORMAT, path, li.li_filename);
93 	}
94 }
95 
96 static void
97 usage(void)
98 {
99 	(void) fprintf(stderr, gettext(USAGE), pname, pname, pname);
100 	exit(E_USAGE);
101 }
102 
103 /*
104  * Translate a lofi device name to a minor number. We might be asked
105  * to do this when there is no association (such as when the user specifies
106  * a particular device), so we can only look at the string.
107  */
108 static int
109 name_to_minor(const char *devicename)
110 {
111 	int	minor;
112 
113 	if (sscanf(devicename, "/dev/" LOFI_BLOCK_NAME "/%d", &minor) == 1) {
114 		return (minor);
115 	}
116 	if (sscanf(devicename, "/dev/" LOFI_CHAR_NAME "/%d", &minor) == 1) {
117 		return (minor);
118 	}
119 	return (0);
120 }
121 
122 /*
123  * This might be the first time we've used this minor number. If so,
124  * it might also be that the /dev links are in the process of being created
125  * by devfsadmd (or that they'll be created "soon"). We cannot return
126  * until they're there or the invoker of lofiadm might try to use them
127  * and not find them. This can happen if a shell script is running on
128  * an MP.
129  */
130 static int sleeptime = 2;	/* number of seconds to sleep between stat's */
131 static int maxsleep = 120;	/* maximum number of seconds to sleep */
132 
133 static void
134 wait_until_dev_complete(int minor)
135 {
136 	struct stat64 buf;
137 	int	cursleep;
138 	char	blkpath[MAXPATHLEN + 1];
139 	char	charpath[MAXPATHLEN + 1];
140 	di_devlink_handle_t hdl;
141 
142 
143 	(void) snprintf(blkpath, sizeof (blkpath), "/dev/%s/%d",
144 	    LOFI_BLOCK_NAME, minor);
145 	(void) snprintf(charpath, sizeof (charpath), "/dev/%s/%d",
146 	    LOFI_CHAR_NAME, minor);
147 
148 	/* Check if links already present */
149 	if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0)
150 		return;
151 
152 	/* First use di_devlink_init() */
153 	if (hdl = di_devlink_init("lofi", DI_MAKE_LINK)) {
154 		(void) di_devlink_fini(&hdl);
155 		goto out;
156 	}
157 
158 	/*
159 	 * Under normal conditions, di_devlink_init(DI_MAKE_LINK) above will
160 	 * only fail if the caller is non-root. In that case, wait for
161 	 * link creation via sysevents.
162 	 */
163 	cursleep = 0;
164 	while (cursleep < maxsleep) {
165 		if ((stat64(blkpath, &buf) == -1) ||
166 		    (stat64(charpath, &buf) == -1)) {
167 			(void) sleep(sleeptime);
168 			cursleep += sleeptime;
169 			continue;
170 		}
171 		return;
172 	}
173 
174 	/* one last try */
175 
176 out:
177 	if (stat64(blkpath, &buf) == -1) {
178 		die(gettext("%s was not created"), blkpath);
179 	}
180 	if (stat64(charpath, &buf) == -1) {
181 		die(gettext("%s was not created"), charpath);
182 	}
183 }
184 
185 /*
186  * Add a device association. If devicename is NULL, let the driver
187  * pick a device.
188  */
189 static void
190 add_mapping(int lfd, const char *devicename, const char *filename)
191 {
192 	struct lofi_ioctl li;
193 	int	minor;
194 
195 	if (devicename == NULL) {
196 		/* pick one */
197 		li.li_minor = 0;
198 		(void) strcpy(li.li_filename, filename);
199 		minor = ioctl(lfd, LOFI_MAP_FILE, &li);
200 		if (minor == -1) {
201 			die(gettext("could not map file %s"), filename);
202 		}
203 		wait_until_dev_complete(minor);
204 		/* print one picked */
205 		(void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, minor);
206 		return;
207 	}
208 	/* use device we were given */
209 	minor = name_to_minor(devicename);
210 	if (minor == 0) {
211 		die(gettext("malformed device name %s\n"), devicename);
212 	}
213 	(void) strcpy(li.li_filename, filename);
214 	li.li_minor = minor;
215 	if (ioctl(lfd, LOFI_MAP_FILE_MINOR, &li) == -1) {
216 		die(gettext("could not map file %s to %s"), filename,
217 		    devicename);
218 	}
219 	wait_until_dev_complete(minor);
220 }
221 
222 /*
223  * Remove an association. Delete by device name if non-NULL, or by
224  * filename otherwise.
225  */
226 static void
227 delete_mapping(int lfd, const char *devicename, const char *filename,
228     boolean_t force)
229 {
230 	struct lofi_ioctl li;
231 
232 	li.li_force = force;
233 	if (devicename == NULL) {
234 		/* delete by filename */
235 		(void) strcpy(li.li_filename, filename);
236 		li.li_minor = 0;
237 		if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) {
238 			die(gettext("could not unmap file %s"), filename);
239 		}
240 		return;
241 	}
242 	/* delete by device */
243 
244 	li.li_minor = name_to_minor(devicename);
245 	if (li.li_minor == 0) {
246 		die(gettext("malformed device name %s\n"), devicename);
247 	}
248 	if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) {
249 		die(gettext("could not unmap device %s"), devicename);
250 	}
251 }
252 
253 static void
254 print_one_mapping(int lfd, const char *devicename, const char *filename)
255 {
256 	struct lofi_ioctl li;
257 
258 	if (devicename == NULL) {
259 		/* given filename, print devicename */
260 		li.li_minor = 0;
261 		(void) strcpy(li.li_filename, filename);
262 		if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) {
263 			die(gettext("could not find device for %s"), filename);
264 		}
265 		(void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, li.li_minor);
266 		return;
267 	}
268 
269 	/* given devicename, print filename */
270 	li.li_minor = name_to_minor(devicename);
271 	if (li.li_minor == 0) {
272 		die(gettext("malformed device name %s\n"), devicename);
273 	}
274 	if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) {
275 		die(gettext("could not find filename for %s"), devicename);
276 	}
277 	(void) printf("%s\n", li.li_filename);
278 }
279 
280 int
281 main(int argc, char *argv[])
282 {
283 	int	lfd;
284 	int	c;
285 	int	error;
286 	struct stat64 buf;
287 	const char *devicename = NULL;
288 	const char *filename = NULL;
289 	int	openflag;
290 	int	minor;
291 	int	fd = -1;
292 	static char *lofictl = "/dev/" LOFI_CTL_NAME;
293 	boolean_t force = B_FALSE;
294 
295 	pname = getpname(argv[0]);
296 
297 	(void) setlocale(LC_ALL, "");
298 	(void) textdomain(TEXT_DOMAIN);
299 
300 	while ((c = getopt(argc, argv, "a:d:f")) != EOF) {
301 		switch (c) {
302 		case 'a':
303 			addflag = 1;
304 			filename = optarg;
305 			fd = open64(filename, O_RDONLY);
306 			if (fd == -1) {
307 				die(gettext("open: %s"), filename);
308 			}
309 			error = fstat64(fd, &buf);
310 			if (error == -1) {
311 				die(gettext("fstat: %s"), filename);
312 			} else if (!S_ISLOFIABLE(buf.st_mode)) {
313 				die(gettext("%s is not a regular file, "
314 				    "block, or character device\n"),
315 				    filename);
316 			} else if ((buf.st_size % DEV_BSIZE) != 0) {
317 				die(gettext("size of %s is not a multiple "
318 				    "of %d\n"),
319 				    filename, DEV_BSIZE);
320 			}
321 			(void) close(fd);
322 			minor = name_to_minor(filename);
323 			if (minor != 0) {
324 				die(gettext("cannot use " LOFI_DRIVER_NAME
325 				    " on itself\n"), devicename);
326 			}
327 			if (((argc - optind) > 0) && (*argv[optind] != '-')) {
328 				/* optional device */
329 				devicename = argv[optind];
330 				optind++;
331 			}
332 			break;
333 		case 'd':
334 			deleteflag = 1;
335 
336 			minor = name_to_minor(optarg);
337 			if (minor != 0)
338 				devicename = optarg;
339 			else
340 				filename = optarg;
341 			break;
342 		case 'f':
343 			force = B_TRUE;
344 			break;
345 		case '?':
346 		default:
347 			errflag = 1;
348 			break;
349 		}
350 	}
351 	if (errflag || (addflag && deleteflag))
352 		usage();
353 
354 	switch (argc - optind) {
355 	case 0: /* no more args */
356 		break;
357 	case 1: /* one arg without options means print the association */
358 		if (addflag || deleteflag)
359 			usage();
360 		minor = name_to_minor(argv[optind]);
361 		if (minor != 0)
362 			devicename = argv[optind];
363 		else
364 			filename = argv[optind];
365 		break;
366 	default:
367 		usage();
368 		break;
369 	}
370 
371 	if (filename && !valid_abspath(filename))
372 		exit(E_ERROR);
373 
374 	/*
375 	 * Here, we know the arguments are correct, the filename is an
376 	 * absolute path, it exists and is a regular file. We don't yet
377 	 * know that the device name is ok or not.
378 	 */
379 	/*
380 	 * Now to the real work.
381 	 */
382 	openflag = O_EXCL;
383 	if (addflag || deleteflag)
384 		openflag |= O_RDWR;
385 	else
386 		openflag |= O_RDONLY;
387 	lfd = open(lofictl, openflag);
388 	if (lfd == -1) {
389 		if ((errno == EPERM) || (errno == EACCES)) {
390 			die("you do not have permission to perform "
391 			    "that operation.\n");
392 		} else {
393 			die("%s", lofictl);
394 		}
395 		/*NOTREACHED*/
396 	}
397 	if (addflag)
398 		add_mapping(lfd, devicename, filename);
399 	else if (deleteflag)
400 		delete_mapping(lfd, devicename, filename, force);
401 	else if (filename || devicename)
402 		print_one_mapping(lfd, devicename, filename);
403 	else
404 		print_mappings(lfd);
405 	(void) close(lfd);
406 	return (E_SUCCESS);
407 }
408