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