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