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 2008 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 "@(#)main.c 1.9 08/05/07 SMI"
34
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/lofi.h>
38 #include <sys/stat.h>
39 #include <netinet/in.h>
40 #include <stdio.h>
41 #include <fcntl.h>
42 #include <locale.h>
43 #include <string.h>
44 #include <strings.h>
45 #include <errno.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <stropts.h>
49 #include <libdevinfo.h>
50 #include <libgen.h>
51 #include <ctype.h>
52 #include <dlfcn.h>
53 #include "utils.h"
54
55 static const char USAGE[] =
56 "Usage: %s -a file [ device ]\n"
57 " %s -d file | device \n"
58 " %s -C [algorithm] [-s segment_size] file \n"
59 " %s -U file \n"
60 " %s [ device | file ]\n";
61
62 static const char *pname;
63 static int addflag = 0;
64 static int deleteflag = 0;
65 static int errflag = 0;
66 static int compressflag = 0;
67 static int uncompressflag = 0;
68
69 static int gzip_compress(void *src, size_t srclen, void *dst,
70 size_t *destlen, int level);
71
72 lofi_compress_info_t lofi_compress_table[LOFI_COMPRESS_FUNCTIONS] = {
73 {NULL, gzip_compress, 6, "gzip"}, /* default */
74 {NULL, gzip_compress, 6, "gzip-6"},
75 {NULL, gzip_compress, 9, "gzip-9"}
76 };
77
78 #define FORMAT "%-20s %-30s %s\n"
79 #define NONE "-"
80 #define COMPRESS "Compressed"
81 #define COMPRESS_ALGORITHM "gzip"
82 #define COMPRESS_THRESHOLD 2048
83 #define SEGSIZE 131072
84 #define BLOCK_SIZE 512
85 #define KILOBYTE 1024
86 #define MEGABYTE (KILOBYTE * KILOBYTE)
87 #define GIGABYTE (KILOBYTE * MEGABYTE)
88 #define LIBZ "libz.so"
89
90 static int (*compress2p)(void *, ulong_t *, void *, size_t, int) = NULL;
91
92 static int gzip_compress(void *src, size_t srclen, void *dst,
93 size_t *dstlen, int level)
94 {
95 void *libz_hdl = NULL;
96
97 /*
98 * The first time we are called, attempt to dlopen()
99 * libz.so and get a pointer to the compress2() function
100 */
101 if (compress2p == NULL) {
102 if ((libz_hdl = openlib(LIBZ)) == NULL)
103 die(gettext("could not find %s. "
104 "gzip compression unavailable\n"), LIBZ);
105
106 if ((compress2p =
107 (int (*)(void *, ulong_t *, void *, size_t, int))
108 dlsym(libz_hdl, "compress2")) == NULL) {
109 closelib();
110 die(gettext("could not find the correct %s. "
111 "gzip compression unavailable\n"), LIBZ);
112 }
113 }
114
115 if ((*compress2p)(dst, (ulong_t *)dstlen, src, srclen, level) != 0)
116 return (-1);
117 return (0);
118 }
119
120 /*
121 * Print the list of all the mappings. Including a header.
122 */
123 static void
124 print_mappings(int fd)
125 {
126 struct lofi_ioctl li;
127 int minor;
128 int maxminor;
129 char path[MAXPATHLEN];
130 char options[MAXPATHLEN];
131
132 li.li_minor = 0;
133 if (ioctl(fd, LOFI_GET_MAXMINOR, &li) == -1) {
134 perror("ioctl");
135 exit(E_ERROR);
136 }
137
138 maxminor = li.li_minor;
139
140 (void) printf(FORMAT, "Block Device", "File", "Options");
141 for (minor = 1; minor <= maxminor; minor++) {
142 li.li_minor = minor;
143 if (ioctl(fd, LOFI_GET_FILENAME, &li) == -1) {
144 if (errno == ENXIO)
145 continue;
146 perror("ioctl");
147 break;
148 }
149 (void) snprintf(path, sizeof (path), "/dev/%s/%d",
150 LOFI_BLOCK_NAME, minor);
151 if (li.li_algorithm[0] == '\0')
152 (void) snprintf(options, sizeof (options), "%s", NONE);
153 else
154 (void) snprintf(options, sizeof (options),
155 COMPRESS "(%s)", li.li_algorithm);
156
157 (void) printf(FORMAT, path, li.li_filename, options);
158 }
159 }
160
161 static void
162 usage(void)
163 {
164 (void) fprintf(stderr, gettext(USAGE), pname, pname,
165 pname, pname, pname);
166 exit(E_USAGE);
167 }
168
169 /*
170 * Translate a lofi device name to a minor number. We might be asked
171 * to do this when there is no association (such as when the user specifies
172 * a particular device), so we can only look at the string.
173 */
174 static int
175 name_to_minor(const char *devicename)
176 {
177 int minor;
178
179 if (sscanf(devicename, "/dev/" LOFI_BLOCK_NAME "/%d", &minor) == 1) {
180 return (minor);
181 }
182 if (sscanf(devicename, "/dev/" LOFI_CHAR_NAME "/%d", &minor) == 1) {
183 return (minor);
184 }
185 return (0);
186 }
187
188 /*
189 * This might be the first time we've used this minor number. If so,
190 * it might also be that the /dev links are in the process of being created
191 * by devfsadmd (or that they'll be created "soon"). We cannot return
192 * until they're there or the invoker of lofiadm might try to use them
193 * and not find them. This can happen if a shell script is running on
194 * an MP.
195 */
196 static int sleeptime = 2; /* number of seconds to sleep between stat's */
197 static int maxsleep = 120; /* maximum number of seconds to sleep */
198
199 static void
200 wait_until_dev_complete(int minor)
201 {
202 struct stat64 buf;
203 int cursleep;
204 char blkpath[MAXPATHLEN];
205 char charpath[MAXPATHLEN];
206 di_devlink_handle_t hdl;
207
208
209 (void) snprintf(blkpath, sizeof (blkpath), "/dev/%s/%d",
210 LOFI_BLOCK_NAME, minor);
211 (void) snprintf(charpath, sizeof (charpath), "/dev/%s/%d",
212 LOFI_CHAR_NAME, minor);
213
214 /* Check if links already present */
215 if (stat64(blkpath, &buf) == 0 && stat64(charpath, &buf) == 0)
216 return;
217
218 /* First use di_devlink_init() */
219 if (hdl = di_devlink_init("lofi", DI_MAKE_LINK)) {
220 (void) di_devlink_fini(&hdl);
221 goto out;
222 }
223
224 /*
225 * Under normal conditions, di_devlink_init(DI_MAKE_LINK) above will
226 * only fail if the caller is non-root. In that case, wait for
227 * link creation via sysevents.
228 */
229 cursleep = 0;
230 while (cursleep < maxsleep) {
231 if ((stat64(blkpath, &buf) == -1) ||
232 (stat64(charpath, &buf) == -1)) {
233 (void) sleep(sleeptime);
234 cursleep += sleeptime;
235 continue;
236 }
237 return;
238 }
239
240 /* one last try */
241
242 out:
243 if (stat64(blkpath, &buf) == -1) {
244 die(gettext("%s was not created"), blkpath);
245 }
246 if (stat64(charpath, &buf) == -1) {
247 die(gettext("%s was not created"), charpath);
248 }
249 }
250
251 /*
252 * Add a device association. If devicename is NULL, let the driver
253 * pick a device.
254 */
255 static void
256 add_mapping(int lfd, const char *devicename, const char *filename,
257 int *minor_created, int suppress)
258 {
259 struct lofi_ioctl li;
260 int minor;
261
262 if (devicename == NULL) {
263 /* pick one */
264 li.li_minor = 0;
265 (void) strlcpy(li.li_filename, filename,
266 sizeof (li.li_filename));
267 minor = ioctl(lfd, LOFI_MAP_FILE, &li);
268 if (minor == -1) {
269 die(gettext("could not map file %s"), filename);
270 }
271 wait_until_dev_complete(minor);
272 /* print one picked */
273 if (!suppress)
274 (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, minor);
275
276 /* fill in the minor if needed */
277 if (minor_created != NULL) {
278 *minor_created = minor;
279 }
280 return;
281 }
282 /* use device we were given */
283 minor = name_to_minor(devicename);
284 if (minor == 0) {
285 die(gettext("malformed device name %s\n"), devicename);
286 }
287 (void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
288 li.li_minor = minor;
289 if (ioctl(lfd, LOFI_MAP_FILE_MINOR, &li) == -1) {
290 die(gettext("could not map file %s to %s"), filename,
291 devicename);
292 }
293 wait_until_dev_complete(minor);
294 }
295
296 /*
297 * Remove an association. Delete by device name if non-NULL, or by
298 * filename otherwise.
299 */
300 static void
301 delete_mapping(int lfd, const char *devicename, const char *filename,
302 boolean_t force)
303 {
304 struct lofi_ioctl li;
305
306 li.li_force = force;
307 li.li_cleanup = 0;
308
309 if (devicename == NULL) {
310 /* delete by filename */
311 (void) strlcpy(li.li_filename, filename,
312 sizeof (li.li_filename));
313 li.li_minor = 0;
314 if (ioctl(lfd, LOFI_UNMAP_FILE, &li) == -1) {
315 die(gettext("could not unmap file %s"), filename);
316 }
317 return;
318 }
319 /* delete by device */
320
321 li.li_minor = name_to_minor(devicename);
322 if (li.li_minor == 0) {
323 die(gettext("malformed device name %s\n"), devicename);
324 }
325 if (ioctl(lfd, LOFI_UNMAP_FILE_MINOR, &li) == -1) {
326 die(gettext("could not unmap device %s"), devicename);
327 }
328 }
329
330 static void
331 print_one_mapping(int lfd, const char *devicename, const char *filename)
332 {
333 struct lofi_ioctl li;
334
335 if (devicename == NULL) {
336 /* given filename, print devicename */
337 li.li_minor = 0;
338 (void) strlcpy(li.li_filename, filename,
339 sizeof (li.li_filename));
340 if (ioctl(lfd, LOFI_GET_MINOR, &li) == -1) {
341 die(gettext("could not find device for %s"), filename);
342 }
343 (void) printf("/dev/%s/%d\n", LOFI_BLOCK_NAME, li.li_minor);
344 return;
345 }
346
347 /* given devicename, print filename */
348 li.li_minor = name_to_minor(devicename);
349 if (li.li_minor == 0) {
350 die(gettext("malformed device name %s\n"), devicename);
351 }
352 if (ioctl(lfd, LOFI_GET_FILENAME, &li) == -1) {
353 die(gettext("could not find filename for %s"), devicename);
354 }
355 (void) printf("%s\n", li.li_filename);
356 }
357
358 /*
359 * Uncompress a file.
360 *
361 * First map the file in to establish a device
362 * association, then read from it. On-the-fly
363 * decompression will automatically uncompress
364 * the file if it's compressed
365 *
366 * If the file is mapped and a device association
367 * has been established, disallow uncompressing
368 * the file until it is unmapped.
369 */
370 static void
371 lofi_uncompress(int lfd, const char *filename)
372 {
373 struct lofi_ioctl li;
374 char buf[MAXBSIZE];
375 char devicename[32];
376 char tmpfilename[MAXPATHLEN];
377 char *dir = NULL;
378 char *file = NULL;
379 int minor = 0;
380 struct stat64 statbuf;
381 int compfd = -1;
382 int uncompfd = -1;
383 ssize_t rbytes;
384
385 /*
386 * Disallow uncompressing the file if it is
387 * already mapped.
388 */
389 li.li_minor = 0;
390 (void) strlcpy(li.li_filename, filename, sizeof (li.li_filename));
391 if (ioctl(lfd, LOFI_GET_MINOR, &li) != -1)
392 die(gettext("%s must be unmapped before uncompressing"),
393 filename);
394
395 /* Zero length files don't need to be uncompressed */
396 if (stat64(filename, &statbuf) == -1)
397 die(gettext("stat: %s"), filename);
398 if (statbuf.st_size == 0)
399 return;
400
401 add_mapping(lfd, NULL, filename, &minor, 1);
402 (void) snprintf(devicename, sizeof (devicename), "/dev/%s/%d",
403 LOFI_BLOCK_NAME, minor);
404
405 /* If the file isn't compressed, we just return */
406 if ((ioctl(lfd, LOFI_CHECK_COMPRESSED, &li) == -1) ||
407 (li.li_algorithm == '\0')) {
408 delete_mapping(lfd, devicename, filename, B_TRUE);
409 return;
410 }
411
412 if ((compfd = open64(devicename, O_RDONLY | O_NONBLOCK)) == -1) {
413 delete_mapping(lfd, devicename, filename, B_TRUE);
414 die(gettext("open: %s"), filename);
415 }
416 /* Create a temp file in the same directory */
417 dir = strdup(filename);
418 dir = dirname(dir);
419 file = strdup(filename);
420 file = basename(file);
421 (void) snprintf(tmpfilename, sizeof (tmpfilename),
422 "%s/.%sXXXXXX", dir, file);
423
424 if ((uncompfd = mkstemp64(tmpfilename)) == -1) {
425 (void) close(compfd);
426 delete_mapping(lfd, devicename, filename, B_TRUE);
427 free(dir);
428 free(file);
429 return;
430 }
431
432 /*
433 * Set the mode bits and the owner of this temporary
434 * file to be that of the original uncompressed file
435 */
436 (void) fchmod(uncompfd, statbuf.st_mode);
437
438 if (fchown(uncompfd, statbuf.st_uid, statbuf.st_gid) == -1) {
439 (void) close(compfd);
440 (void) close(uncompfd);
441 delete_mapping(lfd, devicename, filename, B_TRUE);
442 free(dir);
443 free(file);
444 return;
445 }
446
447 /* Now read from the device in MAXBSIZE-sized chunks */
448 for (;;) {
449 rbytes = read(compfd, buf, sizeof (buf));
450
451 if (rbytes <= 0)
452 break;
453
454 if (write(uncompfd, buf, rbytes) != rbytes) {
455 rbytes = -1;
456 break;
457 }
458 }
459
460 (void) close(compfd);
461 (void) close(uncompfd);
462 free(dir);
463 free(file);
464
465 /* Delete the mapping */
466 delete_mapping(lfd, devicename, filename, B_TRUE);
467
468 /*
469 * If an error occured while reading or writing, rbytes will
470 * be negative
471 */
472 if (rbytes < 0) {
473 (void) unlink(tmpfilename);
474 die(gettext("could not read from %s"), filename);
475 }
476
477 /* Rename the temp file to the actual file */
478 if (rename(tmpfilename, filename) == -1)
479 (void) unlink(tmpfilename);
480 }
481
482 /*
483 * Compress a file
484 */
485 static void
486 lofi_compress(int lfd, const char *filename, int compress_index,
487 uint32_t segsize)
488 {
489 struct lofi_ioctl lic;
490 lofi_compress_info_t *li;
491 char tmpfilename[MAXPATHLEN];
492 char comp_filename[MAXPATHLEN];
493 char algorithm[MAXALGLEN];
494 char *dir = NULL, *file = NULL;
495 uchar_t *uncompressed_seg = NULL;
496 uchar_t *compressed_seg = NULL;
497 uint32_t compressed_segsize;
498 uint32_t len_compressed, count;
499 uint32_t index_entries, index_sz;
500 uint64_t *index = NULL;
501 uint64_t offset;
502 size_t real_segsize;
503 struct stat64 statbuf;
504 int compfd = -1, uncompfd = -1;
505 int tfd = -1;
506 ssize_t rbytes, wbytes, lastread;
507 int i, type;
508
509 /*
510 * Disallow compressing the file if it is
511 * already mapped
512 */
513 lic.li_minor = 0;
514 (void) strlcpy(lic.li_filename, filename, sizeof (lic.li_filename));
515 if (ioctl(lfd, LOFI_GET_MINOR, &lic) != -1)
516 die(gettext("%s must be unmapped before compressing"),
517 filename);
518
519 li = &lofi_compress_table[compress_index];
520
521 /*
522 * The size of the buffer to hold compressed data must
523 * be slightly larger than the compressed segment size.
524 *
525 * The compress functions use part of the buffer as
526 * scratch space to do calculations.
527 * Ref: http://www.zlib.net/manual.html#compress2
528 */
529 compressed_segsize = segsize + (segsize >> 6);
530 compressed_seg = (uchar_t *)malloc(compressed_segsize + SEGHDR);
531 uncompressed_seg = (uchar_t *)malloc(segsize);
532
533 if (compressed_seg == NULL || uncompressed_seg == NULL)
534 die(gettext("No memory"));
535
536 if ((uncompfd = open64(filename, O_RDONLY|O_LARGEFILE, 0)) == -1)
537 die(gettext("open: %s"), filename);
538
539 if (fstat64(uncompfd, &statbuf) == -1) {
540 (void) close(uncompfd);
541 die(gettext("fstat: %s"), filename);
542 }
543
544 /* Zero length files don't need to be compressed */
545 if (statbuf.st_size == 0) {
546 (void) close(uncompfd);
547 return;
548 }
549
550 /*
551 * Create temporary files in the same directory that
552 * will hold the intermediate data
553 */
554 dir = strdup(filename);
555 dir = dirname(dir);
556 file = strdup(filename);
557 file = basename(file);
558 (void) snprintf(tmpfilename, sizeof (tmpfilename),
559 "%s/.%sXXXXXX", dir, file);
560 (void) snprintf(comp_filename, sizeof (comp_filename),
561 "%s/.%sXXXXXX", dir, file);
562
563 if ((tfd = mkstemp64(tmpfilename)) == -1)
564 goto cleanup;
565
566 if ((compfd = mkstemp64(comp_filename)) == -1)
567 goto cleanup;
568
569 /*
570 * Set the mode bits and owner of the compressed
571 * file to be that of the original uncompressed file
572 */
573 (void) fchmod(compfd, statbuf.st_mode);
574
575 if (fchown(compfd, statbuf.st_uid, statbuf.st_gid) == -1)
576 goto cleanup;
577
578 /*
579 * Calculate the number of index entries required.
580 * index entries are stored as an array. adding
581 * a '2' here accounts for the fact that the last
582 * segment may not be a multiple of the segment size
583 */
584 index_sz = (statbuf.st_size / segsize) + 2;
585 index = malloc(sizeof (*index) * index_sz);
586
587 if (index == NULL)
588 goto cleanup;
589
590 offset = 0;
591 lastread = segsize;
592 count = 0;
593
594 /*
595 * Now read from the uncompressed file in 'segsize'
596 * sized chunks, compress what was read in and
597 * write it out to a temporary file
598 */
599 for (;;) {
600 rbytes = read(uncompfd, uncompressed_seg, segsize);
601
602 if (rbytes <= 0)
603 break;
604
605 if (lastread < segsize)
606 goto cleanup;
607
608 /*
609 * Account for the first byte that
610 * indicates whether a segment is
611 * compressed or not
612 */
613 real_segsize = segsize - 1;
614 (void) li->l_compress(uncompressed_seg, rbytes,
615 compressed_seg + SEGHDR, &real_segsize, li->l_level);
616
617 /*
618 * If the length of the compressed data is more
619 * than a threshold then there isn't any benefit
620 * to be had from compressing this segment - leave
621 * it uncompressed.
622 *
623 * NB. In case an error occurs during compression (above)
624 * the 'real_segsize' isn't changed. The logic below
625 * ensures that that segment is left uncompressed.
626 */
627 len_compressed = real_segsize;
628 if (real_segsize > segsize - COMPRESS_THRESHOLD) {
629 (void) memcpy(compressed_seg + SEGHDR, uncompressed_seg,
630 rbytes);
631 type = UNCOMPRESSED;
632 len_compressed = rbytes;
633 } else {
634 type = COMPRESSED;
635 }
636
637 /*
638 * Set the first byte or the SEGHDR to
639 * indicate if it's compressed or not
640 */
641 *compressed_seg = type;
642 wbytes = write(tfd, compressed_seg, len_compressed + SEGHDR);
643 if (wbytes != (len_compressed + SEGHDR)) {
644 rbytes = -1;
645 break;
646 }
647
648 index[count] = BE_64(offset);
649 offset += wbytes;
650 lastread = rbytes;
651 count++;
652 }
653
654 (void) close(uncompfd);
655
656 if (rbytes < 0)
657 goto cleanup;
658 /*
659 * The last index entry is a sentinel entry. It does not point to
660 * an actual compressed segment but helps in computing the size of
661 * the compressed segment. The size of each compressed segment is
662 * computed by subtracting the current index value from the next
663 * one (the compressed blocks are stored sequentially)
664 */
665 index[count++] = BE_64(offset);
666
667 /*
668 * Now write the compressed data along with the
669 * header information to this file which will
670 * later be renamed to the original uncompressed
671 * file name
672 *
673 * The header is as follows -
674 *
675 * Signature (name of the compression algorithm)
676 * Compression segment size (a multiple of 512)
677 * Number of index entries
678 * Size of the last block
679 * The array containing the index entries
680 *
681 * the header is always stored in network byte
682 * order
683 */
684 (void) bzero(algorithm, sizeof (algorithm));
685 (void) strlcpy(algorithm, li->l_name, sizeof (algorithm));
686 if (write(compfd, algorithm, sizeof (algorithm))
687 != sizeof (algorithm))
688 goto cleanup;
689
690 segsize = htonl(segsize);
691 if (write(compfd, &segsize, sizeof (segsize)) != sizeof (segsize))
692 goto cleanup;
693
694 index_entries = htonl(count);
695 if (write(compfd, &index_entries, sizeof (index_entries)) !=
696 sizeof (index_entries))
697 goto cleanup;
698
699 lastread = htonl(lastread);
700 if (write(compfd, &lastread, sizeof (lastread)) != sizeof (lastread))
701 goto cleanup;
702
703 for (i = 0; i < count; i++) {
704 if (write(compfd, index + i, sizeof (*index)) !=
705 sizeof (*index))
706 goto cleanup;
707 }
708
709 /* Header is written, now write the compressed data */
710 if (lseek(tfd, 0, SEEK_SET) != 0)
711 goto cleanup;
712
713 rbytes = wbytes = 0;
714
715 for (;;) {
716 rbytes = read(tfd, compressed_seg, compressed_segsize + SEGHDR);
717
718 if (rbytes <= 0)
719 break;
720
721 if (write(compfd, compressed_seg, rbytes) != rbytes)
722 goto cleanup;
723 }
724
725 if (fstat64(compfd, &statbuf) == -1)
726 goto cleanup;
727
728 /*
729 * Round up the compressed file size to be a multiple of
730 * DEV_BSIZE. lofi(7D) likes it that way.
731 */
732 if ((offset = statbuf.st_size % DEV_BSIZE) > 0) {
733
734 offset = DEV_BSIZE - offset;
735
736 for (i = 0; i < offset; i++)
737 uncompressed_seg[i] = '\0';
738 if (write(compfd, uncompressed_seg, offset) != offset)
739 goto cleanup;
740 }
741 (void) close(compfd);
742 (void) close(tfd);
743 (void) unlink(tmpfilename);
744 cleanup:
745 if (rbytes < 0) {
746 if (tfd != -1)
747 (void) unlink(tmpfilename);
748 if (compfd != -1)
749 (void) unlink(comp_filename);
750 die(gettext("error compressing file %s"), filename);
751 } else {
752 /* Rename the compressed file to the actual file */
753 if (rename(comp_filename, filename) == -1) {
754 (void) unlink(comp_filename);
755 die(gettext("error compressing file %s"), filename);
756 }
757 }
758 if (compressed_seg != NULL)
759 free(compressed_seg);
760 if (uncompressed_seg != NULL)
761 free(uncompressed_seg);
762 if (dir != NULL)
763 free(dir);
764 if (file != NULL)
765 free(file);
766 if (index != NULL)
767 free(index);
768 if (compfd != -1)
769 (void) close(compfd);
770 if (uncompfd != -1)
771 (void) close(uncompfd);
772 if (tfd != -1)
773 (void) close(tfd);
774 }
775
776 static int
777 lofi_compress_select(const char *algname)
778 {
779 int i;
780
781 for (i = 0; i < LOFI_COMPRESS_FUNCTIONS; i++) {
782 if (strcmp(lofi_compress_table[i].l_name, algname) == 0)
783 return (i);
784 }
785 return (-1);
786 }
787
788 static void
789 check_algorithm_validity(const char *algname, int *compress_index)
790 {
791 *compress_index = lofi_compress_select(algname);
792 if (*compress_index < 0)
793 die(gettext("invalid algorithm name: %s\n"), algname);
794 }
795
796 static void
797 check_file_validity(const char *filename)
798 {
799 struct stat64 buf;
800 int error;
801 int fd = -1;
802
803 fd = open64(filename, O_RDONLY);
804 if (fd == -1) {
805 die(gettext("open: %s"), filename);
806 }
807 error = fstat64(fd, &buf);
808 if (error == -1) {
809 die(gettext("fstat: %s"), filename);
810 } else if (!S_ISLOFIABLE(buf.st_mode)) {
811 die(gettext("%s is not a regular file, "
812 "block, or character device\n"),
813 filename);
814 } else if ((buf.st_size % DEV_BSIZE) != 0) {
815 die(gettext("size of %s is not a multiple "
816 "of %d\n"),
817 filename, DEV_BSIZE);
818 }
819 (void) close(fd);
820
821 if (name_to_minor(filename) != 0) {
822 die(gettext("cannot use " LOFI_DRIVER_NAME
823 " on itself\n"), NULL);
824 }
825 }
826
827 static uint32_t
828 convert_to_num(const char *str)
829 {
830 int len;
831 uint32_t segsize, mult = 1;
832
833 len = strlen(str);
834 if (len && isalpha(str[len - 1])) {
835 switch (str[len - 1]) {
836 case 'k':
837 case 'K':
838 mult = KILOBYTE;
839 break;
840 case 'b':
841 case 'B':
842 mult = BLOCK_SIZE;
843 break;
844 case 'm':
845 case 'M':
846 mult = MEGABYTE;
847 break;
848 case 'g':
849 case 'G':
850 mult = GIGABYTE;
851 break;
852 default:
853 die(gettext("invalid segment size %s\n"), str);
854 }
855 }
856
857 segsize = atol(str);
858 segsize *= mult;
859
860 return (segsize);
861 }
862
863 int
864 main(int argc, char *argv[])
865 {
866 int lfd;
867 int c;
868 const char *devicename = NULL;
869 const char *filename = NULL;
870 const char *algname = COMPRESS_ALGORITHM;
871 int openflag;
872 int minor;
873 int compress_index;
874 uint32_t segsize = SEGSIZE;
875 static char *lofictl = "/dev/" LOFI_CTL_NAME;
876 boolean_t force = B_FALSE;
877
878 pname = getpname(argv[0]);
879
880 (void) setlocale(LC_ALL, "");
881 (void) textdomain(TEXT_DOMAIN);
882
883 while ((c = getopt(argc, argv, "a:C:d:s:U:f")) != EOF) {
884 switch (c) {
885 case 'a':
886 addflag = 1;
887 filename = optarg;
888 check_file_validity(filename);
889
890 if (((argc - optind) > 0) && (*argv[optind] != '-')) {
891 /* optional device */
892 devicename = argv[optind];
893 optind++;
894 }
895 break;
896 case 'C':
897 compressflag = 1;
898
899 if (((argc - optind) > 0) &&
900 (*optarg == '-')) {
901 check_algorithm_validity(algname,
902 &compress_index);
903 optind--;
904 break;
905 } else if (((argc - optind) == 1) &&
906 (*argv[optind] != '-')) {
907 algname = optarg;
908 filename = argv[optind];
909 optind++;
910 } else if (((argc - optind) > 1) &&
911 (*argv[optind] == '-')) {
912 algname = optarg;
913 check_algorithm_validity(algname,
914 &compress_index);
915 break;
916 } else {
917 filename = optarg;
918 }
919
920 check_file_validity(filename);
921 check_algorithm_validity(algname, &compress_index);
922 break;
923 case 'd':
924 deleteflag = 1;
925
926 minor = name_to_minor(optarg);
927 if (minor != 0)
928 devicename = optarg;
929 else
930 filename = optarg;
931 break;
932 case 'f':
933 force = B_TRUE;
934 break;
935 case 's':
936 segsize = convert_to_num(optarg);
937
938 if (segsize == 0 || segsize % DEV_BSIZE)
939 die(gettext("segment size %s is invalid "
940 "or not a multiple of minimum block "
941 "size %ld\n"), optarg, DEV_BSIZE);
942
943 filename = argv[optind];
944 check_file_validity(filename);
945 optind++;
946 break;
947 case 'U':
948 uncompressflag = 1;
949 filename = optarg;
950 check_file_validity(filename);
951 break;
952 case '?':
953 default:
954 errflag = 1;
955 break;
956 }
957 }
958 if (errflag ||
959 (addflag && deleteflag) ||
960 ((compressflag || uncompressflag) && (addflag || deleteflag)))
961 usage();
962
963 switch (argc - optind) {
964 case 0: /* no more args */
965 break;
966 case 1: /* one arg without options means print the association */
967 if (addflag || deleteflag)
968 usage();
969 if (compressflag || uncompressflag)
970 usage();
971 minor = name_to_minor(argv[optind]);
972 if (minor != 0)
973 devicename = argv[optind];
974 else
975 filename = argv[optind];
976 break;
977 default:
978 usage();
979 break;
980 }
981
982 if (filename && !valid_abspath(filename))
983 exit(E_ERROR);
984
985 /*
986 * Here, we know the arguments are correct, the filename is an
987 * absolute path, it exists and is a regular file. We don't yet
988 * know that the device name is ok or not.
989 */
990 /*
991 * Now to the real work.
992 */
993 openflag = O_EXCL;
994 if (addflag || deleteflag || compressflag || uncompressflag)
995 openflag |= O_RDWR;
996 else
997 openflag |= O_RDONLY;
998 lfd = open(lofictl, openflag);
999 if (lfd == -1) {
1000 if ((errno == EPERM) || (errno == EACCES)) {
1001 die("you do not have permission to perform "
1002 "that operation.\n");
1003 } else {
1004 die("%s", lofictl);
1005 }
1006 /*NOTREACHED*/
1007 }
1008 if (addflag)
1009 add_mapping(lfd, devicename, filename, NULL, 0);
1010 else if (compressflag)
1011 lofi_compress(lfd, filename, compress_index, segsize);
1012 else if (uncompressflag)
1013 lofi_uncompress(lfd, filename);
1014 else if (deleteflag)
1015 delete_mapping(lfd, devicename, filename, force);
1016 else if (filename || devicename)
1017 print_one_mapping(lfd, devicename, filename);
1018 else
1019 print_mappings(lfd);
1020
1021 (void) close(lfd);
1022 closelib();
1023 return (E_SUCCESS);
1024 }