xref: /illumos-gate/usr/src/cmd/swap/swap.c (revision 1a220b56b93ff1dc80855691548503117af4cc10)
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