42
42
#include "kernel-shared/compression.h"
43
43
#include "kernel-shared/volumes.h"
44
44
#include "kernel-shared/disk-io.h"
45
+ #include "kernel-shared/transaction.h"
45
46
#include "common/defs.h"
46
47
#include "common/internal.h"
47
48
#include "common/messages.h"
@@ -1287,6 +1288,7 @@ static const char * const cmd_filesystem_resize_usage[] = {
1287
1288
"[kK] means KiB, which denotes 1KiB = 1024B, 1MiB = 1024KiB, etc." ,
1288
1289
"" ,
1289
1290
OPTLINE ("--enqueue" , "wait if there's another exclusive operation running, otherwise continue" ),
1291
+ OPTLINE ("--offline" , "resize an offline/unmounted filesystem (limitations: shrinking and multi-device not supported)" ),
1290
1292
NULL
1291
1293
};
1292
1294
@@ -1358,6 +1360,154 @@ static bool parse_resize_args(const char *amount, struct resize_args *ret)
1358
1360
return true;
1359
1361
}
1360
1362
1363
+ static bool check_offline_resize_args (const char * path , const char * amount ,
1364
+ const struct btrfs_fs_info * fs_info ,
1365
+ struct btrfs_device * * device_ret ,
1366
+ u64 * new_size_ret )
1367
+ {
1368
+ struct btrfs_device * device = NULL ;
1369
+ struct resize_args args ;
1370
+ struct stat stat_buf ;
1371
+ u64 new_size = 0 , old_size = 0 , device_size = 0 ;
1372
+
1373
+ if (check_mounted (path )) {
1374
+ error ("%s must not be mounted to use --offline" , path );
1375
+ return false;
1376
+ }
1377
+
1378
+ if (fs_info -> fs_devices -> num_devices > 1 ) {
1379
+ error ("multi-device not supported with --offline" );
1380
+ return false;
1381
+ }
1382
+ device = list_first_entry_or_null (& fs_info -> fs_devices -> devices ,
1383
+ struct btrfs_device , dev_list );
1384
+ if (!device ) {
1385
+ error ("no device found" );
1386
+ return false;
1387
+ }
1388
+ * device_ret = device ;
1389
+ old_size = device -> total_bytes ;
1390
+
1391
+ fstat (device -> fd , & stat_buf );
1392
+ if (device_get_partition_size_fd_stat (device -> fd , & stat_buf , & device_size ))
1393
+ device_size = 0 ;
1394
+ if (!device_size ) {
1395
+ error ("unable to get size at path %s" , device -> name );
1396
+ return false;
1397
+ }
1398
+
1399
+ if (!parse_resize_args (amount , & args ))
1400
+ return false;
1401
+
1402
+ if (args .is_cancel ) {
1403
+ error ("can not cancel --offline resize" );
1404
+ return false;
1405
+ }
1406
+ if (args .specified_dev_id && args .devid != device -> devid ) {
1407
+ error ("invalid device id %llu" , args .devid );
1408
+ return false;
1409
+ }
1410
+ if (args .is_max ) {
1411
+ new_size = device_size ;
1412
+ } else {
1413
+ if (args .mod == 0 ) {
1414
+ new_size = args .size ;
1415
+ } else if (args .mod < 0 ) {
1416
+ error ("offline resize does not support shrinking" );
1417
+ return false;
1418
+ } else {
1419
+ if (args .size > ULLONG_MAX - old_size ) {
1420
+ error ("increasing (%llu) %s is out of range" ,
1421
+ args .size , pretty_size_mode (args .size , UNITS_DEFAULT ));
1422
+ return false;
1423
+ }
1424
+ new_size = old_size + args .size ;
1425
+ }
1426
+ }
1427
+ new_size = round_down (new_size , fs_info -> sectorsize );
1428
+ if (new_size < old_size ) {
1429
+ error ("offline resize does not support shrinking" );
1430
+ return false;
1431
+ }
1432
+ * new_size_ret = new_size ;
1433
+
1434
+ if (path_is_block_device (device -> name ) && new_size > device_size ) {
1435
+ error ("unable to resize '%s': not enough free space" , device -> name );
1436
+ return false;
1437
+ }
1438
+
1439
+ if (new_size < 256 * SZ_1M )
1440
+ warning ("the new size %lld (%s) is < 256MiB, this may be rejected by kernel" ,
1441
+ new_size , pretty_size_mode (new_size , UNITS_DEFAULT ));
1442
+
1443
+ pr_verbose (LOG_DEFAULT , "Resize from %s to %s\n" ,
1444
+ pretty_size_mode (old_size , UNITS_DEFAULT ),
1445
+ pretty_size_mode (new_size , UNITS_DEFAULT ));
1446
+ return true;
1447
+ }
1448
+
1449
+ static bool offline_resize (const char * path , const char * amount )
1450
+ {
1451
+ int ret = false;
1452
+ struct btrfs_root * root ;
1453
+ struct btrfs_fs_info * fs_info ;
1454
+ struct btrfs_device * device ;
1455
+ struct btrfs_super_block * super ;
1456
+ struct btrfs_trans_handle * trans ;
1457
+ u64 new_size ;
1458
+ u64 old_total ;
1459
+ u64 diff ;
1460
+
1461
+ root = open_ctree (path , 0 , OPEN_CTREE_WRITES | OPEN_CTREE_CHUNK_ROOT_ONLY );
1462
+ if (!root )
1463
+ return false;
1464
+ fs_info = root -> fs_info ;
1465
+ super = fs_info -> super_copy ;
1466
+
1467
+ if (!check_offline_resize_args (path , amount , fs_info , & device , & new_size )) {
1468
+ ret = false;
1469
+ goto close ;
1470
+ }
1471
+
1472
+ trans = btrfs_start_transaction (root , 1 );
1473
+ if (IS_ERR (trans )) {
1474
+ errno = - PTR_ERR (trans );
1475
+ error_msg (ERROR_MSG_START_TRANS , "%m" );
1476
+ ret = false;
1477
+ goto close ;
1478
+ }
1479
+
1480
+ old_total = btrfs_super_total_bytes (super );
1481
+ diff = round_down (new_size - device -> total_bytes , fs_info -> sectorsize );
1482
+ btrfs_set_super_total_bytes (super , round_down (old_total + diff , fs_info -> sectorsize ));
1483
+ device -> total_bytes = new_size ;
1484
+ ret = btrfs_update_device (trans , device );
1485
+ if (ret ) {
1486
+ btrfs_abort_transaction (trans , ret );
1487
+ ret = false;
1488
+ goto close ;
1489
+ }
1490
+
1491
+ if (path_is_reg_file (device -> name )) {
1492
+ if (truncate (device -> name , new_size )) {
1493
+ error ("unable to truncate %s to new size %llu" , device -> name , new_size );
1494
+ btrfs_abort_transaction (trans , ret );
1495
+ ret = false;
1496
+ goto close ;
1497
+ }
1498
+ }
1499
+
1500
+ if (btrfs_commit_transaction (trans , root )) {
1501
+ ret = false;
1502
+ goto close ;
1503
+ }
1504
+
1505
+ ret = true;
1506
+ close :
1507
+ close_ctree (root );
1508
+ return ret ;
1509
+ }
1510
+
1361
1511
static int check_resize_args (const char * amount , const char * path , u64 * devid_ret )
1362
1512
{
1363
1513
struct btrfs_ioctl_fs_info_args fi_args ;
@@ -1484,6 +1634,7 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1484
1634
u64 devid ;
1485
1635
int ret ;
1486
1636
bool enqueue = false;
1637
+ bool offline = false;
1487
1638
bool cancel = false;
1488
1639
1489
1640
/*
@@ -1493,6 +1644,8 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1493
1644
for (optind = 1 ; optind < argc ; optind ++ ) {
1494
1645
if (strcmp (argv [optind ], "--enqueue" ) == 0 ) {
1495
1646
enqueue = true;
1647
+ } else if (strcmp (argv [optind ], "--offline" ) == 0 ) {
1648
+ offline = true;
1496
1649
} else if (strcmp (argv [optind ], "--" ) == 0 ) {
1497
1650
/* Separator: options -- non-options */
1498
1651
} else if (strncmp (argv [optind ], "--" , 2 ) == 0 ) {
@@ -1507,6 +1660,11 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1507
1660
if (check_argc_exact (argc - optind , 2 ))
1508
1661
return 1 ;
1509
1662
1663
+ if (offline && enqueue ) {
1664
+ error ("--enqueue is not compatible with --offline" );
1665
+ return 1 ;
1666
+ }
1667
+
1510
1668
amount = argv [optind ];
1511
1669
path = argv [optind + 1 ];
1512
1670
@@ -1516,17 +1674,16 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1516
1674
return 1 ;
1517
1675
}
1518
1676
1677
+ if (offline )
1678
+ return !offline_resize (path , amount );
1679
+
1519
1680
cancel = (strcmp ("cancel" , amount ) == 0 );
1520
1681
1521
1682
fd = btrfs_open_dir (path );
1522
1683
if (fd < 0 ) {
1523
- /* The path is a directory */
1524
- if (fd == - ENOTDIR ) {
1525
- error (
1526
- "resize works on mounted filesystems and accepts only\n"
1527
- "directories as argument. Passing file containing a btrfs image\n"
1528
- "would resize the underlying filesystem instead of the image.\n" );
1529
- }
1684
+ /* The path is not a directory. */
1685
+ if (fd == - ENOTDIR )
1686
+ error ("to resize a file containing a BTRFS image use the --offline flag" );
1530
1687
return 1 ;
1531
1688
}
1532
1689
0 commit comments