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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * University Copyright- Copyright (c) 1982, 1986, 1988 31 * The Regents of the University of California 32 * All Rights Reserved 33 * 34 * University Acknowledgment- Portions of this document are derived from 35 * software developed by the University of California, Berkeley, and its 36 * contributors. 37 */ 38 39 #pragma ident "%Z%%M% %I% %E% SMI" 40 41 /* 42 * Swap administrative interface 43 * Used to add/delete/list swap devices. 44 */ 45 46 #include <sys/types.h> 47 #include <sys/dumpadm.h> 48 #include <string.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <unistd.h> 52 #include <errno.h> 53 #include <sys/param.h> 54 #include <dirent.h> 55 #include <sys/swap.h> 56 #include <sys/sysmacros.h> 57 #include <sys/mkdev.h> 58 #include <sys/stat.h> 59 #include <sys/statvfs.h> 60 #include <sys/uadmin.h> 61 #include <vm/anon.h> 62 #include <fcntl.h> 63 #include <locale.h> 64 #include <libintl.h> 65 #include <libdiskmgt.h> 66 67 #define LFLAG 0x01 /* swap -l (list swap devices) */ 68 #define DFLAG 0x02 /* swap -d (delete swap device) */ 69 #define AFLAG 0x04 /* swap -a (add swap device) */ 70 #define SFLAG 0x08 /* swap -s (swap info summary) */ 71 #define P1FLAG 0x10 /* swap -1 (swapadd pass1; do not modify dump device) */ 72 #define P2FLAG 0x20 /* swap -2 (swapadd pass2; do not modify dump device) */ 73 #define HFLAG 0x40 /* swap -h (size in human readable format) */ 74 #define KFLAG 0x80 /* swap -k (size in kilobytes) */ 75 76 #define NUMBER_WIDTH 64 77 typedef char numbuf_t[NUMBER_WIDTH]; 78 79 static char *prognamep; 80 81 static int add(char *, off_t, off_t, int); 82 static int delete(char *, off_t); 83 static void usage(void); 84 static int doswap(int flag); 85 static int valid(char *, off_t, off_t); 86 static int list(int flag); 87 static char *number_to_scaled_string(numbuf_t buf, unsigned long long number, 88 unsigned long long unit_from, unsigned long long scale); 89 90 91 int 92 main(int argc, char **argv) 93 { 94 int c, flag = 0; 95 int ret; 96 int error = 0; 97 off_t s_offset = 0; 98 off_t length = 0; 99 char *pathname; 100 char *msg; 101 102 (void) setlocale(LC_ALL, ""); 103 104 #if !defined(TEXT_DOMAIN) 105 #define TEXT_DOMAIN "SYS_TEST" 106 #endif 107 (void) textdomain(TEXT_DOMAIN); 108 109 prognamep = argv[0]; 110 if (argc < 2) { 111 usage(); 112 exit(1); 113 } 114 115 while ((c = getopt(argc, argv, "khlsd:a:12")) != EOF) { 116 char *char_p; 117 switch (c) { 118 case 'l': /* list all the swap devices */ 119 flag |= LFLAG; 120 break; 121 case 's': 122 flag |= SFLAG; 123 break; 124 case 'd': 125 /* 126 * The argument for starting offset is optional. 127 * If no argument is specified, the entire swap file 128 * is added although this will fail if a non-zero 129 * starting offset was specified when added. 130 */ 131 if ((argc - optind) > 1 || flag != 0) { 132 usage(); 133 exit(1); 134 } 135 flag |= DFLAG; 136 pathname = optarg; 137 if (optind < argc) { 138 errno = 0; 139 s_offset = strtol(argv[optind++], &char_p, 10); 140 if (errno != 0 || *char_p != '\0') { 141 (void) fprintf(stderr, 142 gettext("error in [low block]\n")); 143 exit(1); 144 } 145 } 146 ret = delete(pathname, s_offset); 147 break; 148 149 case 'a': 150 /* 151 * The arguments for starting offset and number of 152 * blocks are optional. If only the starting offset 153 * is specified, all the blocks to the end of the swap 154 * file will be added. If no starting offset is 155 * specified, the entire swap file is assumed. 156 */ 157 if ((argc - optind) > 2 || 158 (flag & ~(P1FLAG | P2FLAG)) != 0) { 159 usage(); 160 exit(1); 161 } 162 if (*optarg != '/') { 163 (void) fprintf(stderr, 164 gettext("%s: path must be absolute\n"), 165 prognamep); 166 exit(1); 167 } 168 flag |= AFLAG; 169 pathname = optarg; 170 if (optind < argc) { 171 errno = 0; 172 s_offset = strtol(argv[optind++], &char_p, 10); 173 if (errno != 0 || *char_p != '\0') { 174 (void) fprintf(stderr, 175 gettext("error in [low block]\n")); 176 exit(1); 177 } 178 } 179 if (optind < argc) { 180 errno = 0; 181 length = strtol(argv[optind++], &char_p, 10); 182 if (errno != 0 || *char_p != '\0') { 183 (void) fprintf(stderr, 184 gettext("error in [nbr of blocks]\n")); 185 exit(1); 186 } 187 } 188 break; 189 case 'h': 190 flag |= HFLAG; 191 break; 192 193 case 'k': 194 flag |= KFLAG; 195 break; 196 197 case '1': 198 flag |= P1FLAG; 199 break; 200 201 case '2': 202 flag |= P2FLAG; 203 break; 204 205 case '?': 206 usage(); 207 exit(1); 208 } 209 } 210 211 if (flag & SFLAG) { 212 if (flag & ~SFLAG & ~HFLAG) { 213 /* 214 * The only option that can be used with -s is -h. 215 */ 216 usage(); 217 exit(1); 218 } 219 220 ret = doswap(flag); 221 222 } 223 224 if (flag & LFLAG) { 225 if (flag & ~KFLAG & ~HFLAG & ~LFLAG) { 226 usage(); 227 exit(1); 228 } 229 ret = list(flag); 230 } 231 232 /* 233 * do the add here. Check for in use prior to add. 234 * The values for length and offset are set above. 235 */ 236 if (flag & AFLAG) { 237 /* 238 * If device is in use for a swap device, print message 239 * and exit. 240 */ 241 if (dm_inuse(pathname, &msg, DM_WHO_SWAP, &error) || 242 error) { 243 if (error != 0) { 244 (void) fprintf(stderr, gettext("Error occurred" 245 " with device in use checking: %s\n"), 246 strerror(error)); 247 } else { 248 (void) fprintf(stderr, "%s", msg); 249 free(msg); 250 exit(1); 251 } 252 } 253 if ((ret = valid(pathname, 254 s_offset * 512, length * 512)) == 0) { 255 ret = add(pathname, s_offset, length, flag); 256 } 257 } 258 if (!(flag & ~HFLAG & ~KFLAG)) { 259 /* only -h and/or -k flag, or no flag */ 260 usage(); 261 exit(1); 262 } 263 return (ret); 264 } 265 266 267 static void 268 usage(void) 269 { 270 (void) fprintf(stderr, gettext("Usage:\t%s -l\n"), prognamep); 271 (void) fprintf(stderr, gettext("\tsub option :\n")); 272 (void) fprintf(stderr, gettext("\t\t-h : displays size in human " 273 "readable format\n")); 274 (void) fprintf(stderr, gettext("\t\t-k : displays size in KB\n")); 275 (void) fprintf(stderr, "\t%s -s\n", prognamep); 276 (void) fprintf(stderr, gettext("\tsub option :\n")); 277 (void) fprintf(stderr, gettext("\t\t-h : displays size in human " 278 "readable format rather than KB\n")); 279 (void) fprintf(stderr, gettext("\t%s -d <file name> [low block]\n"), 280 prognamep); 281 (void) fprintf(stderr, gettext("\t%s -a <file name> [low block]" 282 " [nbr of blocks]\n"), prognamep); 283 } 284 285 /* 286 * Implement: 287 * #define ctok(x) ((ctob(x))>>10) 288 * in a machine independent way. (Both assume a click > 1k) 289 */ 290 static size_t 291 ctok(pgcnt_t clicks) 292 { 293 static int factor = -1; 294 295 if (factor == -1) 296 factor = (int)(sysconf(_SC_PAGESIZE) >> 10); 297 return ((size_t)(clicks * factor)); 298 } 299 300 301 static int 302 doswap(int flag) 303 { 304 struct anoninfo ai; 305 pgcnt_t allocated, reserved, available; 306 numbuf_t numbuf; 307 unsigned long long scale = 1024L; 308 309 /* 310 * max = total amount of swap space including physical memory 311 * ai.ani_max = MAX(anoninfo.ani_resv, anoninfo.ani_max) + 312 * availrmem - swapfs_minfree; 313 * ai.ani_free = amount of unallocated anonymous memory 314 * (ie. = resverved_unallocated + unreserved) 315 * ai.ani_free = anoninfo.ani_free + (availrmem - swapfs_minfree); 316 * ai.ani_resv = total amount of reserved anonymous memory 317 * ai.ani_resv = anoninfo.ani_resv; 318 * 319 * allocated = anon memory not free 320 * reserved = anon memory reserved but not allocated 321 * available = anon memory not reserved 322 */ 323 if (swapctl(SC_AINFO, &ai) == -1) { 324 perror(prognamep); 325 return (2); 326 } 327 328 allocated = ai.ani_max - ai.ani_free; 329 reserved = ai.ani_resv - allocated; 330 available = ai.ani_max - ai.ani_resv; 331 332 /* 333 * TRANSLATION_NOTE 334 * Translations (if any) of these keywords should match with 335 * translations (if any) of the swap.1M man page keywords for 336 * -s option: "allocated", "reserved", "used", "available" 337 */ 338 339 if (flag & HFLAG) { 340 int factor = (int)(sysconf(_SC_PAGESIZE)); 341 (void) printf(gettext("total: %s allocated + "), 342 number_to_scaled_string(numbuf, allocated, 343 factor, scale)); 344 (void) printf(gettext("%s reserved = "), 345 number_to_scaled_string(numbuf, reserved, 346 factor, scale)); 347 (void) printf(gettext("%s used, "), 348 number_to_scaled_string(numbuf, 349 allocated + reserved, factor, scale)); 350 (void) printf(gettext("%s available\n"), 351 number_to_scaled_string(numbuf, available, 352 factor, scale)); 353 } else { 354 (void) printf(gettext("total: %luk bytes allocated + %luk" 355 " reserved = %luk used, %luk available\n"), 356 ctok(allocated), ctok(reserved), 357 ctok(reserved) + ctok(allocated), 358 ctok(available)); 359 } 360 361 return (0); 362 } 363 364 static int 365 list(int flag) 366 { 367 struct swaptable *st; 368 struct swapent *swapent; 369 int i; 370 struct stat64 statbuf; 371 char *path; 372 char fullpath[MAXPATHLEN+1]; 373 int num; 374 numbuf_t numbuf; 375 unsigned long long scale = 1024L; 376 377 if ((num = swapctl(SC_GETNSWP, NULL)) == -1) { 378 perror(prognamep); 379 return (2); 380 } 381 if (num == 0) { 382 (void) fprintf(stderr, gettext("No swap devices configured\n")); 383 return (1); 384 } 385 386 if ((st = malloc(num * sizeof (swapent_t) + sizeof (int))) 387 == NULL) { 388 (void) fprintf(stderr, 389 gettext("Malloc failed. Please try later.\n")); 390 perror(prognamep); 391 return (2); 392 } 393 if ((path = malloc(num * MAXPATHLEN)) == NULL) { 394 (void) fprintf(stderr, 395 gettext("Malloc failed. Please try later.\n")); 396 perror(prognamep); 397 return (2); 398 } 399 swapent = st->swt_ent; 400 for (i = 0; i < num; i++, swapent++) { 401 swapent->ste_path = path; 402 path += MAXPATHLEN; 403 } 404 405 st->swt_n = num; 406 if ((num = swapctl(SC_LIST, st)) == -1) { 407 perror(prognamep); 408 return (2); 409 } 410 411 /* 412 * TRANSLATION_NOTE 413 * Following translations for "swap -l" should account for for 414 * alignment of header and output. 415 * The first translation is for the header. If the alignment 416 * of the header changes, change the next 5 formats as needed 417 * to make alignment of output agree with alignment of the header. 418 * The next four translations are four cases for printing the 419 * 1st & 2nd fields. 420 * The next translation is for printing the 3rd, 4th & 5th fields. 421 * 422 * Translations (if any) of the following keywords should match the 423 * translations (if any) of the swap.1M man page keywords for 424 * -l option: "swapfile", "dev", "swaplo", "blocks", "free" 425 */ 426 (void) printf( 427 gettext("swapfile dev swaplo blocks free\n")); 428 429 swapent = st->swt_ent; 430 for (i = 0; i < num; i++, swapent++) { 431 if (*swapent->ste_path != '/') 432 (void) snprintf(fullpath, sizeof (fullpath), 433 "/dev/%s", swapent->ste_path); 434 else 435 (void) snprintf(fullpath, sizeof (fullpath), 436 "%s", swapent->ste_path); 437 if (stat64(fullpath, &statbuf) < 0) 438 if (*swapent->ste_path != '/') 439 (void) printf(gettext("%-20s - "), 440 swapent->ste_path); 441 else 442 (void) printf(gettext("%-20s ?,? "), 443 fullpath); 444 else { 445 if (S_ISBLK(statbuf.st_mode) || 446 S_ISCHR(statbuf.st_mode)) { 447 (void) printf(gettext("%-19s %2lu,%-2lu"), 448 fullpath, 449 major(statbuf.st_rdev), 450 minor(statbuf.st_rdev)); 451 } else { 452 (void) printf(gettext("%-20s - "), fullpath); 453 } 454 } 455 { 456 int diskblks_per_page = 457 (int)(sysconf(_SC_PAGESIZE) >> DEV_BSHIFT); 458 if (flag & HFLAG) { 459 (void) printf(gettext(" %8s"), 460 number_to_scaled_string(numbuf, 461 swapent->ste_start, DEV_BSIZE, 462 scale)); 463 (void) printf(gettext(" %8s"), 464 number_to_scaled_string(numbuf, 465 swapent->ste_pages * 466 diskblks_per_page, 467 DEV_BSIZE, scale)); 468 (void) printf(gettext(" %8s"), 469 number_to_scaled_string(numbuf, 470 swapent->ste_free * 471 diskblks_per_page, 472 DEV_BSIZE, scale)); 473 } else if (flag & KFLAG) { 474 (void) printf(gettext(" %7luK %7luK %7luK"), 475 swapent->ste_start * DEV_BSIZE / 1024, 476 swapent->ste_pages * diskblks_per_page * 477 DEV_BSIZE / 1024, 478 swapent->ste_free * diskblks_per_page * 479 DEV_BSIZE / 1024); 480 } else { 481 (void) printf(gettext(" %8lu %8lu %8lu"), 482 swapent->ste_start, 483 swapent->ste_pages * diskblks_per_page, 484 swapent->ste_free * diskblks_per_page); 485 } 486 } 487 if (swapent->ste_flags & ST_INDEL) 488 (void) printf(" INDEL\n"); 489 else 490 (void) printf("\n"); 491 } 492 return (0); 493 } 494 495 /* Copied from du.c */ 496 static char * 497 number_to_scaled_string( 498 numbuf_t buf, /* put the result here */ 499 unsigned long long number, /* convert this number */ 500 unsigned long long unit_from, /* number of byes per input unit */ 501 unsigned long long scale) /* 1024 (-h) or 1000 (-H) */ 502 { 503 unsigned long long save = 0; 504 char *M = "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */ 505 char *uom = M; /* unit of measurement, initially 'K' (=M[0]) */ 506 507 if ((long long)number == (long long) -1) { 508 (void) strcpy(buf, "-1"); 509 return (buf); 510 } 511 512 /* 513 * Convert number from unit_from to given scale (1024 or 1000) 514 * This means multiply number with unit_from and divide by scale. 515 * if number is large enough, we first divide and then multiply 516 * to avoid an overflow (large enough here means 100 (rather arbitrary 517 * value) times scale in order to reduce rounding errors) 518 * otherwise, we first multiply and then divide to avoid an underflow. 519 */ 520 if (number >= 100L * scale) { 521 number = number / scale; 522 number = number * unit_from; 523 } else { 524 number = number * unit_from; 525 number = number / scale; 526 } 527 528 /* 529 * Now we have number as a count of scale units. 530 * Stop scaling when we reached exa bytes, then something is 531 * probably wrong with our number. 532 */ 533 while ((number >= scale) && (*uom != 'E')) { 534 uom++; /* Next unit of measurement */ 535 save = number; 536 number = (number + (scale / 2)) / scale; 537 } 538 539 /* Check if we should output a decimal place after the point */ 540 if (save && ((save / scale) < 10)) { 541 /* sprintf() will round for us */ 542 float fnum = (float)save / scale; 543 (void) sprintf(buf, "%.1f%c", fnum, *uom); 544 } else { 545 (void) sprintf(buf, "%llu%c", number, *uom); 546 } 547 return (buf); 548 } 549 550 551 552 553 static void 554 dumpadm_err(const char *warning) 555 { 556 (void) fprintf(stderr, "%s (%s):\n", warning, strerror(errno)); 557 (void) fprintf(stderr, gettext( 558 "run dumpadm(1M) to verify dump configuration\n")); 559 } 560 561 static int 562 delete(char *path, off_t offset) 563 { 564 swapres_t swr; 565 int fd; 566 567 swr.sr_name = path; 568 swr.sr_start = offset; 569 570 if (swapctl(SC_REMOVE, &swr) < 0) { 571 switch (errno) { 572 case (ENOSYS): 573 (void) fprintf(stderr, gettext( 574 "%s: Invalid operation for this filesystem type\n"), 575 path); 576 break; 577 default: 578 perror(path); 579 break; 580 } 581 return (2); 582 } 583 584 /* 585 * If our swap -d succeeded, open up /dev/dump and ask what the dump 586 * device is set to. If this returns ENODEV, we just deleted the 587 * dump device, so try to change the dump device to another swap 588 * device. We do this by firing up /usr/sbin/dumpadm -ud swap. 589 */ 590 if ((fd = open("/dev/dump", O_RDONLY)) >= 0) { 591 char dumpdev[MAXPATHLEN]; 592 593 if (ioctl(fd, DIOCGETDEV, dumpdev) == -1) { 594 if (errno == ENODEV) { 595 (void) printf(gettext("%s was dump device --\n" 596 "invoking dumpadm(1M) -d swap to " 597 "select new dump device\n"), path); 598 /* 599 * Close /dev/dump prior to executing dumpadm 600 * since /dev/dump mandates exclusive open. 601 */ 602 (void) close(fd); 603 604 if (system("/usr/sbin/dumpadm -ud swap") == -1) 605 dumpadm_err(gettext( 606 "Warning: failed to execute dumpadm -d swap")); 607 } else 608 dumpadm_err(gettext( 609 "Warning: failed to check dump device")); 610 } 611 (void) close(fd); 612 } else 613 dumpadm_err(gettext("Warning: failed to open /dev/dump")); 614 615 return (0); 616 } 617 618 /* 619 * swapres_t structure units are in 512-blocks 620 */ 621 static int 622 add(char *path, off_t offset, off_t cnt, int flags) 623 { 624 swapres_t swr; 625 626 int fd, have_dumpdev = 1; 627 struct statvfs fsb; 628 629 /* 630 * Before adding swap, we first check to see if we have a dump 631 * device configured. If we don't (errno == ENODEV), and if 632 * our SC_ADD is successful, then run /usr/sbin/dumpadm -ud swap 633 * to attempt to reconfigure the dump device to the new swap. 634 */ 635 if ((fd = open("/dev/dump", O_RDONLY)) >= 0) { 636 char dumpdev[MAXPATHLEN]; 637 638 if (ioctl(fd, DIOCGETDEV, dumpdev) == -1) { 639 if (errno == ENODEV) 640 have_dumpdev = 0; 641 else 642 dumpadm_err(gettext( 643 "Warning: failed to check dump device")); 644 } 645 646 (void) close(fd); 647 648 } else if (!(flags & P1FLAG)) 649 dumpadm_err(gettext("Warning: failed to open /dev/dump")); 650 651 swr.sr_name = path; 652 swr.sr_start = offset; 653 swr.sr_length = cnt; 654 655 if (swapctl(SC_ADD, &swr) < 0) { 656 switch (errno) { 657 case (ENOSYS): 658 (void) fprintf(stderr, gettext( 659 "%s: Invalid operation for this filesystem type\n"), 660 path); 661 break; 662 case (EEXIST): 663 (void) fprintf(stderr, gettext( 664 "%s: Overlapping swap files are not allowed\n"), 665 path); 666 break; 667 default: 668 perror(path); 669 break; 670 } 671 return (2); 672 } 673 674 /* 675 * If the swapctl worked and we don't have a dump device, and /etc 676 * is part of a writeable filesystem, then run dumpadm -ud swap. 677 * If /etc (presumably part of /) is still mounted read-only, then 678 * dumpadm will fail to write its config file, so there's no point 679 * running it now. This also avoids spurious messages during boot 680 * when the first swapadd takes place, at which point / is still ro. 681 * Similarly, if swapadd invoked us with -1 or -2 (but root is 682 * writeable), we don't want to modify the dump device because 683 * /etc/init.d/savecore has yet to execute; if we run dumpadm now 684 * we would lose the user's previous setting. 685 */ 686 if (!have_dumpdev && !(flags & (P1FLAG | P2FLAG)) && 687 statvfs("/etc", &fsb) == 0 && !(fsb.f_flag & ST_RDONLY)) { 688 689 (void) printf( 690 gettext("operating system crash dump was previously " 691 "disabled --\ninvoking dumpadm(1M) -d swap to select " 692 "new dump device\n")); 693 694 if (system("/usr/sbin/dumpadm -ud swap") == -1) 695 dumpadm_err(gettext( 696 "Warning: failed to execute dumpadm -d swap")); 697 } 698 699 return (0); 700 } 701 702 static int 703 valid(char *pathname, off_t offset, off_t length) 704 { 705 struct stat64 f; 706 struct statvfs64 fs; 707 off_t need; 708 709 if (stat64(pathname, &f) < 0 || statvfs64(pathname, &fs) < 0) { 710 (void) perror(pathname); 711 return (errno); 712 } 713 714 if (!((S_ISREG(f.st_mode) && (f.st_mode & S_ISVTX) == S_ISVTX) || 715 S_ISBLK(f.st_mode))) { 716 (void) fprintf(stderr, 717 gettext("\"%s\" is not valid for swapping.\n" 718 "It must be a block device or a regular file with the\n" 719 "\"save user text on execution\" bit set.\n"), 720 pathname); 721 return (EINVAL); 722 } 723 724 if (S_ISREG(f.st_mode)) { 725 if (length == 0) 726 length = (off_t)f.st_size; 727 728 /* 729 * "f.st_blocks < 8" because the first eight 730 * 512-byte sectors are always skipped 731 */ 732 733 if (f.st_size < (length - offset) || f.st_size == 0 || 734 f.st_size > MAXOFF_T || f.st_blocks < 8 || length < 0) { 735 (void) fprintf(stderr, gettext("%s: size is invalid\n"), 736 pathname); 737 return (EINVAL); 738 } 739 740 if (offset < 0) { 741 (void) fprintf(stderr, 742 gettext("%s: low block is invalid\n"), 743 pathname); 744 return (EINVAL); 745 } 746 747 need = roundup(length, fs.f_bsize) / DEV_BSIZE; 748 749 /* 750 * "need > f.st_blocks" to account for indirect blocks 751 * Note: 752 * This can be fooled by a file large enough to 753 * contain indirect blocks that also contains holes. 754 * However, we don't know (and don't want to know) 755 * about the underlying storage implementation. 756 * But, if it doesn't have at least this many blocks, 757 * there must be a hole. 758 */ 759 760 if (need > f.st_blocks) { 761 (void) fprintf(stderr, gettext( 762 "\"%s\" may contain holes - can't swap on it.\n"), 763 pathname); 764 return (EINVAL); 765 } 766 } 767 /* 768 * else, we cannot get st_size for S_ISBLK device and 769 * no meaningful checking can be done. 770 */ 771 772 return (0); 773 } 774