swapon - Implement encrypted swap
[dragonfly.git] / sbin / swapon / swapon.c
1 /*
2  * Copyright (c) 1980, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#) Copyright (c) 1980, 1993 The Regents of the University of California.  All rights reserved.
30  * @(#)swapon.c 8.1 (Berkeley) 6/5/93
31  * $FreeBSD: src/sbin/swapon/swapon.c,v 1.8.2.2 2001/07/30 10:30:11 dd Exp $
32  */
33
34 #include <sys/param.h>
35 #include <sys/stat.h>
36 #include <sys/sysctl.h>
37 #include <sys/linker.h>
38 #include <sys/diskslice.h>
39 #include <sys/ioctl_compat.h>
40 #include <vm/vm_param.h>
41
42 #include <err.h>
43 #include <errno.h>
44 #include <fstab.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <fcntl.h>
50 #include <libutil.h>
51
52 static void usage(void);
53 static int swap_on_off(char *name, int doingall, int trim, int ask);
54 static char *docrypt(char *fs_spec, int pass);
55 static void swaplist(int lflag, int sflag, int hflag);
56
57 static int qflag;
58
59 enum { SWAPON, SWAPOFF, SWAPCTL } orig_prog, which_prog = SWAPCTL;
60
61 int
62 main(int argc, char **argv)
63 {
64         struct fstab *fsp;
65         char *ptr;
66         int ret;
67         int ch;
68         int doall, sflag, lflag, hflag, eflag, cflag, iflag;
69
70         if ((ptr = strrchr(argv[0], '/')) == NULL)
71                 ptr = argv[0];
72         if (strstr(ptr, "swapon"))
73                 which_prog = SWAPON;
74         else if (strstr(ptr, "swapoff"))
75                 which_prog = SWAPOFF;
76         orig_prog = which_prog;
77
78         sflag = lflag = hflag = doall = eflag = cflag = iflag = 0;
79         while ((ch = getopt(argc, argv, "AacdeghiklmqsU")) != -1) {
80                 switch((char)ch) {
81                 case 'A':
82                         if (which_prog == SWAPCTL) {
83                                 doall = 1;
84                                 which_prog = SWAPON;
85                         } else {
86                                 usage();
87                         }
88                         break;
89                 case 'a':
90                         if (which_prog == SWAPON || which_prog == SWAPOFF)
91                                 doall = 1;
92                         else
93                                 which_prog = SWAPON;
94                         break;
95                 case 'd':
96                         if (which_prog == SWAPCTL)
97                                 which_prog = SWAPOFF;
98                         else
99                                 usage();
100                         break;
101                 case 'c':
102                         cflag = 1;
103                         break;
104                 case 'e':
105                         eflag = 1;
106                         break;
107                 case 'g':
108                         hflag = 'G';
109                         break;
110                 case 'h':
111                         hflag = 'H';
112                         break;
113                 case 'i':
114                         iflag = 1;
115                         break;
116                 case 'k':
117                         hflag = 'K';
118                         break;
119                 case 'l':
120                         lflag = 1;
121                         break;
122                 case 'm':
123                         hflag = 'M';
124                         break;
125                 case 'q':
126                         if (which_prog == SWAPON || which_prog == SWAPOFF)
127                                 qflag = 1;
128                         break;
129                 case 's':
130                         sflag = 1;
131                         break;
132                 case 'U':
133                         if (which_prog == SWAPCTL) {
134                                 doall = 1;
135                                 which_prog = SWAPOFF;
136                         } else {
137                                 usage();
138                         }
139                         break;
140                 case '?':
141                 default:
142                         usage();
143                 }
144         }
145         argv += optind;
146
147         ret = 0;
148         if (which_prog == SWAPON || which_prog == SWAPOFF) {
149                 if (doall) {
150                         while ((fsp = getfsent()) != NULL) {
151                                 char *fs_spec;
152                                 int dotrim = eflag;
153
154                                 if (strcmp(fsp->fs_type, FSTAB_SW))
155                                         continue;
156                                 if (strstr(fsp->fs_mntops, "noauto"))
157                                         continue;
158
159                                 if (strstr(fsp->fs_mntops, "notrim"))
160                                         dotrim = 0;
161                                 else if (strstr(fsp->fs_mntops, "trim"))
162                                         dotrim = 1;
163
164                                 if (cflag || strstr(fsp->fs_mntops, "crypt"))
165                                         fs_spec = docrypt(fsp->fs_spec, 1);
166                                 else
167                                         fs_spec = strdup(fsp->fs_spec);
168                                 if (swap_on_off(fs_spec, 1, dotrim, iflag)) {
169                                         ret = 1;
170                                 } else {
171                                         if (cflag ||
172                                             strstr(fsp->fs_mntops, "crypt")) {
173                                                 docrypt(fsp->fs_spec, 2);
174                                         }
175                                         if (!qflag) {
176                                                 printf("%s: %sing %s as swap "
177                                                        "device\n",
178                                                     getprogname(),
179                                                     (which_prog == SWAPOFF ?
180                                                             "remov" : "add"),
181                                                     fs_spec);
182                                         }
183                                 }
184                                 free(fs_spec);
185                         }
186                 } else if (*argv == NULL) {
187                         usage();
188                 }
189                 for (; *argv; ++argv) {
190                         char *ospec = getdevpath(*argv, 0);
191                         char *fs_spec;
192                         if (cflag)
193                                 fs_spec = docrypt(ospec, 1);
194                         else
195                                 fs_spec = strdup(ospec);
196                         if (swap_on_off(fs_spec, 0, eflag, iflag)) {
197                                 ret = 1;
198                         } else {
199                                 if (cflag)
200                                         docrypt(ospec, 2);
201                                 if (!qflag) {
202                                         printf("%s: %sing %s as swap device\n",
203                                             getprogname(),
204                                             (which_prog == SWAPOFF ?
205                                                 "remov" : "add"),
206                                             fs_spec);
207                                 }
208                         }
209                         free(fs_spec);
210                 }
211         } else {
212                 if (lflag || sflag)
213                         swaplist(lflag, sflag, hflag);
214                 else
215                         usage();
216         }
217         exit(ret);
218 }
219
220 static
221 char *
222 docrypt(char *fs_spec, int pass)
223 {
224         char *id;
225         char *res;
226         char *buf;
227
228         if ((id = strrchr(fs_spec, '/')) == NULL)
229                 id = fs_spec;
230         else
231                 ++id;
232         asprintf(&id, "swap-%s", id);
233         asprintf(&res, "/dev/mapper/%s", id);
234
235         switch(which_prog) {
236         case SWAPOFF:
237                 if (pass != 2)
238                         break;
239                 asprintf(&buf, "/sbin/cryptsetup remove %s", id);
240                 system(buf);
241                 free(id);
242                 break;
243         case SWAPON:
244                 if (pass != 1)
245                         break;
246                 if (kldfind("dm_target_crypt") < 0)
247                         kldload("dm_target_crypt");
248
249                 asprintf(&buf,
250                          "/sbin/cryptsetup --key-file /dev/urandom "
251                          "--key-size 256 create %s %s",
252                          id, fs_spec);
253                 if (qflag == 0)
254                         printf("%s\n", buf);
255                 system(buf);
256                 free(buf);
257                 free(id);
258
259                 /*
260                  * NOTE: Don't revert to /dev/da* on error because this could
261                  *       inadvertently add both /dev/da* and
262                  *       /dev/mapper/swap-da*.
263                  *
264                  *       Allow the swapon operation to report failure or
265                  *       report a duplicate.
266                  */
267                 break;
268         default:
269                 free(res);
270                 free(id);
271                 res = strdup(fs_spec);
272                 break;
273         }
274
275         if (pass == 2) {
276                 free (res);
277                 res = NULL;
278         }
279         return res;
280 }
281
282 /*
283  * TRIM the device
284  */
285 static
286 void
287 trim_volume(char * name)
288 {       
289         struct partinfo pinfo;
290         int fd,i,n;
291         size_t bytes = 0,ksize;
292         char *xswbuf;
293         struct xswdev *xsw;
294
295
296         /*
297         * Determine if this device is already being used by swap without 
298         * calling swapon(). 
299         */
300         if ((sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0) ||
301             bytes == 0) {
302                 err(1, "sysctlbyname()");
303         }
304
305         xswbuf = malloc(bytes);
306         if ((sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) ||
307             bytes == 0) {
308                         free(xswbuf);
309                         err(1, "sysctlbyname()");
310         }
311
312         ksize = ((struct xswdev *)xswbuf)->xsw_size;
313         n = (int)(bytes / ksize);
314         for (i = 0; i < n; ++i) {
315                 xsw = (void *)((char *)xswbuf + i * ksize);
316
317                 if (xsw->xsw_dev == NODEV )
318                         continue;
319                 if(!strcmp(devname(xsw->xsw_dev, S_IFCHR),
320                     name + strlen("/dev/"))) {
321                         warnx("%s: device already a swap device", name);
322                         exit(1);
323                 }
324         }
325         
326         /*
327          * Get the size and offset of this parititon/device
328          */
329         fd = open(name, O_RDWR);
330         if (fd < 0)
331                 err(1, "Unable to open %s R+W", name);
332         if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
333                 printf("Cannot trim regular file\n");
334                 usage ();
335         }
336         off_t ioarg[2];
337         
338         /*Trim the Device*/     
339         ioarg[0] = pinfo.media_offset;
340         ioarg[1] = pinfo.media_size;
341         printf("Trimming Device:%s, sectors (%llu -%llu)\n",name,
342              (unsigned long long)ioarg[0]/512,
343              (unsigned long long)ioarg[1]/512);
344         if (ioctl(fd, IOCTLTRIM, ioarg) < 0) {
345                 printf("Device trim failed\n");
346                 usage ();
347         }
348         close(fd);
349 }
350
351 static int
352 swap_on_off(char *name, int doingall, int trim, int ask)
353 {
354
355         if (ask && which_prog == SWAPON) {
356                 printf("Do you really want to use device %s as a swap device ?\n", name);
357                 printf("You might loose data. [Y/N]");
358
359                 int c = fgetc(stdin);
360                 printf("\n");
361                 if (c != 'y' && c != 'Y')
362                         return(1);
363
364         }
365         if (which_prog == SWAPON && trim) {
366                 char sysctl_name[64];
367                 int trim_enabled = 0;
368                 size_t olen = sizeof(trim_enabled);
369                 char *dev_name = strdup(name);
370                 dev_name = strtok(dev_name + strlen("/dev/da"),"s");
371                 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", dev_name);
372                 sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
373                 if (errno == ENOENT) {
374                         if (qflag == 0) {
375                                 printf("TRIM not supported on %s, "
376                                        "ignoring\n",
377                                        name);
378                         }
379                         errno = 0;
380                 } else if (!trim_enabled) {
381                         if (qflag == 0) {
382                                 printf("TRIM not enabled on %s (%s), "
383                                        "ignoring\n",
384                                        name, sysctl_name);
385                         }
386                 } else {
387                         trim_volume(name);
388                 }
389         }
390         if ((which_prog == SWAPOFF ? swapoff(name) : swapon(name)) == -1) {
391                 switch(errno) {
392                 case EBUSY:
393                         if (!doingall)
394                                 warnx("%s: device already in use", name);
395                         break;
396                 case EINVAL:
397                         if (which_prog == SWAPON)
398                                 warnx("%s: NSWAPDEV limit reached", name);
399                         else if (!doingall)
400                                 warn("%s", name);
401                         break;
402                 default:
403                         warn("%s", name);
404                         break;
405                 }
406                 return(1);
407         }
408         return(0);
409 }
410
411 static void
412 usage(void)
413 {
414         fprintf(stderr, "usage: %s ", getprogname());
415         switch (orig_prog) {
416         case SWAPON:
417         case SWAPOFF:
418                 fprintf(stderr, "-aeiq | file ...\n");
419                 break;
420         case SWAPCTL:
421                 fprintf(stderr, "[-AeghiklmsU] [-a file ... | -d file ...]\n");
422                 break;
423         }
424         exit(1);
425 }
426
427 static void
428 sizetobuf(char *buf, size_t bufsize, int hflag, long long val, int hlen,
429     long blocksize)
430 {
431         if (hflag == 'H') {
432                 char tmp[16];
433
434                 humanize_number(tmp, 5, (int64_t)val, "", HN_AUTOSCALE,
435                     HN_B | HN_NOSPACE | HN_DECIMAL);
436                 snprintf(buf, bufsize, "%*s", hlen, tmp);
437         } else {
438                 snprintf(buf, bufsize, "%*lld", hlen, val / blocksize);
439         }
440 }
441
442 static void
443 swaplist(int lflag, int sflag, int hflag)
444 {
445         size_t ksize, bytes = 0;
446         char *xswbuf;
447         struct xswdev *xsw;
448         int hlen, pagesize;
449         int i, n;
450         long blocksize;
451         long long total, used, tmp_total, tmp_used;
452         char buf[32];
453
454         pagesize = getpagesize();
455         switch(hflag) {
456         case 'G':
457                 blocksize = 1024 * 1024 * 1024;
458                 strlcpy(buf, "1GB-blocks", sizeof(buf));
459                 hlen = 10;
460                 break;
461         case 'H':
462                 blocksize = -1;
463                 strlcpy(buf, "Bytes", sizeof(buf));
464                 hlen = 10;
465                 break;
466         case 'K':
467                 blocksize = 1024;
468                 strlcpy(buf, "1kB-blocks", sizeof(buf));
469                 hlen = 10;
470                 break;
471         case 'M':
472                 blocksize = 1024 * 1024;
473                 strlcpy(buf, "1MB-blocks", sizeof(buf));
474                 hlen = 10;
475                 break;
476         default:
477                 getbsize(&hlen, &blocksize);
478                 snprintf(buf, sizeof(buf), "%ld-blocks", blocksize);
479                 break;
480         }
481
482         if (sysctlbyname("vm.swap_info_array", NULL, &bytes, NULL, 0) < 0)
483                 err(1, "sysctlbyname()");
484         if (bytes == 0)
485                 err(1, "sysctlbyname()");
486
487         xswbuf = malloc(bytes);
488         if (sysctlbyname("vm.swap_info_array", xswbuf, &bytes, NULL, 0) < 0) {
489                 free(xswbuf);
490                 err(1, "sysctlbyname()");
491         }
492         if (bytes == 0) {
493                 free(xswbuf);
494                 err(1, "sysctlbyname()");
495         }
496
497         /*
498          * Calculate size of xsw entry returned by kernel (it can be larger
499          * than the one we have if there is a version mismatch).
500          */
501         ksize = ((struct xswdev *)xswbuf)->xsw_size;
502         n = (int)(bytes / ksize);
503
504         if (lflag) {
505                 printf("%-13s %*s %*s\n",
506                     "Device:",
507                     hlen, buf,
508                     hlen, "Used:");
509         }
510
511         total = used = tmp_total = tmp_used = 0;
512         for (i = 0; i < n; ++i) {
513                 xsw = (void *)((char *)xswbuf + i * ksize);
514
515                 if (xsw->xsw_nblks == 0)
516                         continue;
517
518                 tmp_total = (long long)xsw->xsw_nblks * pagesize;
519                 tmp_used = (long long)xsw->xsw_used * pagesize;
520                 total += tmp_total;
521                 used += tmp_used;
522                 if (lflag) {
523                         sizetobuf(buf, sizeof(buf), hflag, tmp_total, hlen,
524                             blocksize);
525                         if (xsw->xsw_dev == NODEV) {
526                                 printf("%-13s %s ", "[NFS swap]", buf);
527                         } else {
528                                 printf("/dev/%-8s %s ",
529                                     devname(xsw->xsw_dev, S_IFCHR), buf);
530                         }
531
532                         sizetobuf(buf, sizeof(buf), hflag, tmp_used, hlen,
533                             blocksize);
534                         printf("%s\n", buf);
535                 }
536         }
537
538         if (sflag) {
539                 sizetobuf(buf, sizeof(buf), hflag, total, hlen, blocksize);
540                 printf("Total:        %s ", buf);
541                 sizetobuf(buf, sizeof(buf), hflag, used, hlen, blocksize);
542                 printf("%s\n", buf);
543         }
544 }