Skip to content

Commit 90548b8

Browse files
committed
btrfs-progs: filesystem: new subcommand mkswapfile
Add a command to create a new swapfile. The same can be achieved by seandalone tools but they're just wrappers around the syscalls. The swap format is simple enough to be created directly without mkswap command so the swapfile can be created in one go. The file must not exist before, this is to avoid problems with file attributes or any other effects of existing extents. This also means the command can't be used on block devices. Default size is 2G, minimum size is 40KiB. Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 441d015 commit 90548b8

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed

Documentation/btrfs-filesystem.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,23 @@ label [<device>|<mountpoint>] [<newlabel>]
188188
The maximum allowable length shall be less than 256 chars and must not contain
189189
a newline. The trailing newline is stripped automatically.
190190

191+
mkswapfile [-s size] file
192+
Create a new file that's suitable and formatted as a swapfile. Default
193+
size is 2GiB, minimum size is 40KiB.
194+
195+
A swapfile must be created in a specific way: NOCOW and preallocated.
196+
Subvolume containing a swapfile cannot be snapshotted and blocks of an
197+
activated swapfile cannot be balanced.
198+
199+
Swapfile creation can be achieved by standalone commands too. Activation
200+
needs to be done by command ``swapon(8)``.
201+
202+
``Options``
203+
204+
-s|--size SIZE
205+
Create swapfile of a given size SIZE (accepting k/m/g/e/p
206+
suffix).
207+
191208
resize [options] [<devid>:][+/-]<size>[kKmMgGtTpPeE]|[<devid>:]max <path>
192209
Resize a mounted filesystem identified by *path*. A particular device
193210
can be resized by specifying a *devid*.

cmds/filesystem.c

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <sys/ioctl.h>
1919
#include <sys/stat.h>
2020
#include <linux/version.h>
21+
#include <linux/fs.h>
2122
#include <stdio.h>
2223
#include <stdlib.h>
2324
#include <string.h>
@@ -50,6 +51,7 @@
5051
#include "common/device-utils.h"
5152
#include "common/open-utils.h"
5253
#include "common/parse-utils.h"
54+
#include "common/string-utils.h"
5355
#include "common/filesystem-utils.h"
5456
#include "cmds/commands.h"
5557
#include "cmds/filesystem-usage.h"
@@ -1373,6 +1375,135 @@ static int cmd_filesystem_balance(const struct cmd_struct *unused,
13731375
static DEFINE_COMMAND(filesystem_balance, "balance", cmd_filesystem_balance,
13741376
cmd_filesystem_balance_usage, NULL, CMD_HIDDEN);
13751377

1378+
static const char * const cmd_filesystem_mkswapfile_usage[] = {
1379+
"btrfs filesystem mkswapfile <file>",
1380+
"Force a sync on a filesystem",
1381+
HELPINFO_INSERT_GLOBALS,
1382+
HELPINFO_INSERT_VERBOSE,
1383+
HELPINFO_INSERT_QUIET,
1384+
NULL
1385+
};
1386+
1387+
/*
1388+
* Swap signature in the first 4KiB, v2:
1389+
*
1390+
* 00000400 .. = 01 00 00 00 ff ff 03 00 00 00 00 00 cb 70 8e 60
1391+
* ^^^^^^^^^^^
1392+
* uuid 4B
1393+
* 00000420 .. = 1d fb 4e ca be d4 3f 1f 6a 6b 0c 03 00 00 00 00
1394+
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1395+
* uuid 8B
1396+
* 00000ff0 .. = 00 00 00 00 00 00 53 57 41 50 53 50 41 43 45 32
1397+
* S W A P S P A C E 2
1398+
*/
1399+
static int write_swap_signature(int fd)
1400+
{
1401+
int ret;
1402+
static unsigned char swap[4096] = {
1403+
[0x400] = 0x01,
1404+
[0x404] = 0xff,
1405+
[0x405] = 0xff,
1406+
[0x406] = 0x03,
1407+
/* 0x40c .. 0x42b UUID */
1408+
[0xff6] = 'S',
1409+
[0xff7] = 'W',
1410+
[0xff8] = 'A',
1411+
[0xff9] = 'P',
1412+
[0xffa] = 'S',
1413+
[0xffb] = 'P',
1414+
[0xffc] = 'A',
1415+
[0xffd] = 'C',
1416+
[0xffe] = 'E',
1417+
[0xfff] = '2',
1418+
};
1419+
1420+
uuid_generate(&swap[0x40c]);
1421+
ret = pwrite(fd, swap, 4096, 0);
1422+
1423+
return ret;
1424+
}
1425+
1426+
static int cmd_filesystem_mkswapfile(const struct cmd_struct *cmd, int argc, char **argv)
1427+
{
1428+
int ret;
1429+
int fd;
1430+
const char *fname;
1431+
unsigned long flags;
1432+
u64 size = SZ_2G;
1433+
1434+
optind = 0;
1435+
while (1) {
1436+
int c;
1437+
static const struct option long_options[] = {
1438+
{ "size", required_argument, NULL, 's' },
1439+
{ NULL, 0, NULL, 0 }
1440+
};
1441+
1442+
c = getopt_long(argc, argv, "s:", long_options, NULL);
1443+
if (c < 0)
1444+
break;
1445+
1446+
switch (c) {
1447+
case 's':
1448+
size = parse_size_from_string(optarg);
1449+
/* Minimum limit reported by mkswap */
1450+
if (size < 40 * SZ_1K) {
1451+
error("swapfile needs to be at least 40 KiB");
1452+
return 1;
1453+
}
1454+
break;
1455+
default:
1456+
usage_unknown_option(cmd, argv);
1457+
}
1458+
}
1459+
1460+
if (check_argc_exact(argc - optind, 1))
1461+
return 1;
1462+
1463+
fname = argv[optind];
1464+
pr_verbose(LOG_INFO, "create file %s with mode 0600\n", fname);
1465+
fd = open(fname, O_RDWR | O_CREAT | O_EXCL, 0600);
1466+
if (fd < 0) {
1467+
error("cannot create new swapfile: %m");
1468+
return 1;
1469+
}
1470+
ret = ftruncate(fd, 0);
1471+
if (ret < 0) {
1472+
error("cannot truncate file: %m");
1473+
ret = 1;
1474+
goto out;
1475+
}
1476+
pr_verbose(LOG_INFO, "set NOCOW attribute\n");
1477+
flags = FS_NOCOW_FL;
1478+
ret = ioctl(fd, FS_IOC_SETFLAGS, &flags);
1479+
if (ret < 0) {
1480+
error("cannot set NOCOW flag: %m");
1481+
ret = 1;
1482+
goto out;
1483+
}
1484+
pr_verbose(LOG_INFO, "fallocate to size %llu\n", size);
1485+
ret = fallocate(fd, 0, 0, size);
1486+
if (ret < 0) {
1487+
error("cannot fallocate file: %m");
1488+
ret = 1;
1489+
goto out;
1490+
}
1491+
pr_verbose(LOG_INFO, "write swap signature\n");
1492+
ret = write_swap_signature(fd);
1493+
if (ret < 0) {
1494+
error("cannot write swap signature: %m");
1495+
ret = 1;
1496+
goto out;
1497+
}
1498+
pr_verbose(LOG_DEFAULT, "create swapfile %s size %s (%llu)\n",
1499+
fname, pretty_size_mode(size, UNITS_HUMAN), size);
1500+
out:
1501+
close(fd);
1502+
1503+
return 0;
1504+
}
1505+
static DEFINE_SIMPLE_COMMAND(filesystem_mkswapfile, "mkswapfile");
1506+
13761507
static const char filesystem_cmd_group_info[] =
13771508
"overall filesystem tasks and information";
13781509

@@ -1387,6 +1518,7 @@ static const struct cmd_group filesystem_cmd_group = {
13871518
&cmd_struct_filesystem_resize,
13881519
&cmd_struct_filesystem_label,
13891520
&cmd_struct_filesystem_usage,
1521+
&cmd_struct_filesystem_mkswapfile,
13901522
NULL
13911523
}
13921524
};

0 commit comments

Comments
 (0)