38
38
#include "kernel-lib/sizes.h"
39
39
#include "kernel-lib/list_sort.h"
40
40
#include "kernel-lib/overflow.h"
41
+ #include "kernel-shared/accessors.h"
41
42
#include "kernel-shared/ctree.h"
42
43
#include "kernel-shared/compression.h"
43
44
#include "kernel-shared/volumes.h"
44
45
#include "kernel-shared/disk-io.h"
46
+ #include "kernel-shared/transaction.h"
45
47
#include "common/defs.h"
46
48
#include "common/internal.h"
47
49
#include "common/messages.h"
@@ -1287,9 +1289,241 @@ static const char * const cmd_filesystem_resize_usage[] = {
1287
1289
"[kK] means KiB, which denotes 1KiB = 1024B, 1MiB = 1024KiB, etc." ,
1288
1290
"" ,
1289
1291
OPTLINE ("--enqueue" , "wait if there's another exclusive operation running, otherwise continue" ),
1292
+ OPTLINE ("--offline" , "resize an offline filesystem, only increases are allowed" ),
1290
1293
NULL
1291
1294
};
1292
1295
1296
+ static int check_offline_resize_args (const char * path , const char * amount ,
1297
+ const struct btrfs_fs_info * fs_info ,
1298
+ struct btrfs_device * * device_ret ,
1299
+ u64 * new_size_ret )
1300
+ {
1301
+ int ret = 0 ;
1302
+ char amount_dup [BTRFS_VOL_NAME_MAX ];
1303
+ struct btrfs_device * device = NULL ;
1304
+ struct btrfs_device * mindev = NULL ;
1305
+ bool dev_found = false;
1306
+ u64 devid = 1 ;
1307
+ u64 mindevid = (u64 )- 1 ;
1308
+ char * devstr = NULL ;
1309
+ struct stat stat_buf ;
1310
+ char * sizestr = NULL ;
1311
+ u64 new_size = 0 , old_size = 0 , diff = 0 , device_size = 0 ;
1312
+ int mod = 0 ;
1313
+
1314
+ if (check_mounted (path )) {
1315
+ error ("%s must not be mounted to use --offline" , path );
1316
+ return 1 ;
1317
+ }
1318
+
1319
+ if (!fs_info -> fs_devices -> num_devices ) {
1320
+ error ("no devices found" );
1321
+ return 1 ;
1322
+ }
1323
+
1324
+ ret = snprintf (amount_dup , BTRFS_VOL_NAME_MAX , "%s" , amount );
1325
+ if (strlen (amount ) != ret ) {
1326
+ error ("newsize argument is too long" );
1327
+ return 1 ;
1328
+ }
1329
+
1330
+ if (strcmp (amount , "cancel" ) == 0 ) {
1331
+ error ("cannot cancel offline resize since the operation is synchronous" );
1332
+ return 1 ;
1333
+ }
1334
+
1335
+ /* Parse device id. */
1336
+ sizestr = amount_dup ;
1337
+ devstr = strchr (sizestr , ':' );
1338
+ if (devstr ) {
1339
+ sizestr = devstr + 1 ;
1340
+ * devstr = 0 ;
1341
+ devstr = amount_dup ;
1342
+
1343
+ errno = 0 ;
1344
+ devid = strtoull (devstr , NULL , 10 );
1345
+
1346
+ if (errno ) {
1347
+ error ("failed to parse devid %s: %m" , devstr );
1348
+ return 1 ;
1349
+ }
1350
+ }
1351
+
1352
+ /* Find device matching device id */
1353
+ list_for_each_entry (device , & fs_info -> fs_devices -> devices , dev_list ) {
1354
+ if (device -> devid < mindevid ) {
1355
+ mindevid = device -> devid ;
1356
+ mindev = device ;
1357
+ }
1358
+ if (device -> devid == devid ) {
1359
+ dev_found = true;
1360
+ break ;
1361
+ }
1362
+ }
1363
+
1364
+ if (devstr && !dev_found ) {
1365
+ /* Devid specified but not found. */
1366
+ error ("cannot find devid: %lld" , devid );
1367
+ return 1 ;
1368
+ } else if (!devstr && devid == 1 && !dev_found ) {
1369
+ /*
1370
+ * No device specified, assuming implicit 1 but it does not
1371
+ * exist. Use minimum device as fallback.
1372
+ */
1373
+ warning ("no devid specified means devid 1 which does not exist, using\n"
1374
+ "\t lowest devid %llu as a fallback" ,
1375
+ mindevid );
1376
+ devid = mindevid ;
1377
+ device = mindev ;
1378
+ }
1379
+ if (!device ) {
1380
+ error ("unable to find device" );
1381
+ return 1 ;
1382
+ }
1383
+ * device_ret = device ;
1384
+ old_size = device -> total_bytes ;
1385
+
1386
+ if (strcmp (sizestr , "max" ) == 0 ) {
1387
+ if (path_is_block_device (device -> name )) {
1388
+ ret = device_get_partition_size (device -> name , & new_size );
1389
+ if (ret )
1390
+ new_size = 0 ;
1391
+ } else if (path_is_reg_file (device -> name )) {
1392
+ ret = stat (device -> name , & stat_buf );
1393
+ if (ret ) {
1394
+ new_size = 0 ;
1395
+ } else {
1396
+ new_size = stat_buf .st_size ;
1397
+ }
1398
+ }
1399
+
1400
+ if (new_size == 0 ) {
1401
+ error ("unable to get size for device: %s" ,
1402
+ device -> name );
1403
+ return 1 ;
1404
+ }
1405
+ } else {
1406
+ if (sizestr [0 ] == '-' ) {
1407
+ error ("offline resize does not support shrinking" );
1408
+ return 1 ;
1409
+ } else if (sizestr [0 ] == '+' ) {
1410
+ mod = 1 ;
1411
+ sizestr ++ ;
1412
+ }
1413
+ ret = parse_u64_with_suffix (sizestr , & diff );
1414
+ if (ret < 0 ) {
1415
+ error ("failed to parse size %s" , sizestr );
1416
+ return 1 ;
1417
+ }
1418
+
1419
+ /* For target sizes without +/- sign prefix (e.g. 1:150g) */
1420
+ if (mod == 0 ) {
1421
+ new_size = diff ;
1422
+ } else if (mod > 0 ) {
1423
+ if (diff > ULLONG_MAX - old_size ) {
1424
+ error ("increasing %s is out of range" ,
1425
+ pretty_size_mode (diff , UNITS_DEFAULT ));
1426
+ return 1 ;
1427
+ }
1428
+ new_size = old_size + diff ;
1429
+ }
1430
+ }
1431
+ new_size = round_down (new_size , fs_info -> sectorsize );
1432
+ if (new_size < old_size ) {
1433
+ error ("offline resize does not support shrinking" );
1434
+ return 1 ;
1435
+ }
1436
+ * new_size_ret = new_size ;
1437
+
1438
+ ret = device_get_partition_size (device -> name , & device_size );
1439
+ if (ret ) {
1440
+ error ("unable to get size for device: %s" , device -> name );
1441
+ return -1 ;
1442
+ }
1443
+ if (path_is_block_device (device -> name ) && new_size > device_size ) {
1444
+ error ("unable to resize '%s': not enough free space" ,
1445
+ device -> name );
1446
+ return 1 ;
1447
+ }
1448
+
1449
+ if (new_size < 256 * SZ_1M )
1450
+ warning ("the new size %lld (%s) is < 256MiB, this may be rejected by kernel" ,
1451
+ new_size , pretty_size_mode (new_size , UNITS_DEFAULT ));
1452
+
1453
+ pr_verbose (LOG_DEFAULT , "Resize device id %lld from %s to %s\n" , devid ,
1454
+ pretty_size_mode (old_size , UNITS_DEFAULT ),
1455
+ pretty_size_mode (new_size , UNITS_DEFAULT ));
1456
+ return 0 ;
1457
+ }
1458
+
1459
+ static int offline_resize (const char * path , const char * amount )
1460
+ {
1461
+ int ret = 0 ;
1462
+ struct btrfs_root * root ;
1463
+ struct btrfs_fs_info * fs_info ;
1464
+ struct btrfs_device * device ;
1465
+ struct btrfs_super_block * super ;
1466
+ struct btrfs_trans_handle * trans ;
1467
+ u64 new_size ;
1468
+ u64 old_total ;
1469
+ u64 diff ;
1470
+ char dev_name [BTRFS_VOL_NAME_MAX ];
1471
+
1472
+ root = open_ctree (path , 0 , OPEN_CTREE_WRITES | OPEN_CTREE_CHUNK_ROOT_ONLY );
1473
+ if (!root ) {
1474
+ error ("could not open file at %s\n"
1475
+ "offline resize works on a file containing a btrfs image." ,
1476
+ path );
1477
+ return 1 ;
1478
+ }
1479
+ fs_info = root -> fs_info ;
1480
+ super = fs_info -> super_copy ;
1481
+
1482
+ ret = check_offline_resize_args (path , amount , fs_info , & device ,
1483
+ & new_size );
1484
+ if (ret ) {
1485
+ ret = 1 ;
1486
+ goto close ;
1487
+ }
1488
+
1489
+ ret = snprintf (dev_name , BTRFS_VOL_NAME_MAX , "%s" , device -> name );
1490
+ if (strlen (device -> name ) != ret ) {
1491
+ error ("device name too long %s" , device -> name );
1492
+ ret = 1 ;
1493
+ goto close ;
1494
+ }
1495
+ ret = 0 ;
1496
+
1497
+ trans = btrfs_start_transaction (root , 1 );
1498
+ if (IS_ERR (trans )) {
1499
+ ret = PTR_ERR (trans );
1500
+ errno = - ret ;
1501
+ error_msg (ERROR_MSG_START_TRANS , "%m" );
1502
+ return ret ;
1503
+ }
1504
+ old_total = btrfs_super_total_bytes (super );
1505
+ diff = round_down (new_size - device -> total_bytes , fs_info -> sectorsize );
1506
+ btrfs_set_super_total_bytes (super , round_down (old_total + diff ,
1507
+ fs_info -> sectorsize ));
1508
+ device -> total_bytes = new_size ;
1509
+ ret = btrfs_update_device (trans , device );
1510
+ if (ret ) {
1511
+ btrfs_abort_transaction (trans , ret );
1512
+ goto close ;
1513
+ }
1514
+ ret |= btrfs_commit_transaction (trans , root );
1515
+ close :
1516
+ ret |= close_ctree (root );
1517
+ if (ret )
1518
+ return ret ;
1519
+
1520
+ if (path_is_reg_file (dev_name ))
1521
+ ret = truncate (dev_name , new_size );
1522
+ if (ret )
1523
+ error ("failed to truncate %s" , device -> name );
1524
+ return ret ;
1525
+ }
1526
+
1293
1527
static int check_resize_args (const char * amount , const char * path , u64 * devid_ret )
1294
1528
{
1295
1529
struct btrfs_ioctl_fs_info_args fi_args ;
@@ -1449,6 +1683,7 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1449
1683
u64 devid ;
1450
1684
int ret ;
1451
1685
bool enqueue = false;
1686
+ bool offline = false;
1452
1687
bool cancel = false;
1453
1688
1454
1689
/*
@@ -1458,6 +1693,8 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1458
1693
for (optind = 1 ; optind < argc ; optind ++ ) {
1459
1694
if (strcmp (argv [optind ], "--enqueue" ) == 0 ) {
1460
1695
enqueue = true;
1696
+ } else if (strcmp (argv [optind ], "--offline" ) == 0 ) {
1697
+ offline = true;
1461
1698
} else if (strcmp (argv [optind ], "--" ) == 0 ) {
1462
1699
/* Separator: options -- non-options */
1463
1700
} else if (strncmp (argv [optind ], "--" , 2 ) == 0 ) {
@@ -1472,6 +1709,12 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1472
1709
if (check_argc_exact (argc - optind , 2 ))
1473
1710
return 1 ;
1474
1711
1712
+ if (offline && enqueue ) {
1713
+ error ("--enqueue is not compatible with --offline\n"
1714
+ "since offline resizing is synchronous" );
1715
+ return 1 ;
1716
+ }
1717
+
1475
1718
amount = argv [optind ];
1476
1719
path = argv [optind + 1 ];
1477
1720
@@ -1481,6 +1724,9 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1481
1724
return 1 ;
1482
1725
}
1483
1726
1727
+ if (offline )
1728
+ return offline_resize (path , amount );
1729
+
1484
1730
cancel = (strcmp ("cancel" , amount ) == 0 );
1485
1731
1486
1732
fd = btrfs_open_dir (path );
@@ -1490,7 +1736,8 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1490
1736
error (
1491
1737
"resize works on mounted filesystems and accepts only\n"
1492
1738
"directories as argument. Passing file containing a btrfs image\n"
1493
- "would resize the underlying filesystem instead of the image.\n" );
1739
+ "would resize the underlying filesystem instead of the image.\n"
1740
+ "To resize a file containing a btrfs image please use the --offline flag.\n" );
1494
1741
}
1495
1742
return 1 ;
1496
1743
}
0 commit comments