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