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