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
- #include "kernel-shared/volumes.h"
44
44
#include "kernel-shared/disk-io.h"
45
+ #include "kernel-shared/transaction.h"
46
+ #include "kernel-shared/volumes.h"
45
47
#include "common/defs.h"
46
48
#include "common/internal.h"
47
49
#include "common/messages.h"
@@ -1287,6 +1289,7 @@ 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
@@ -1440,6 +1443,246 @@ static int check_resize_args(const char *amount, const char *path, u64 *devid_re
1440
1443
return ret ;
1441
1444
}
1442
1445
1446
+ static int check_offline_resize_args (const struct btrfs_fs_info * fs_info ,
1447
+ const char * amount , const char * path ,
1448
+ struct btrfs_device * * device_ret ,
1449
+ u64 * new_size_ret )
1450
+ {
1451
+ int ret = 0 ;
1452
+
1453
+ char amount_dup [BTRFS_VOL_NAME_MAX ];
1454
+
1455
+ struct btrfs_device * device = NULL ;
1456
+ struct btrfs_device * mindev = NULL ;
1457
+ bool dev_found = false;
1458
+ u64 devid = 1 ;
1459
+ u64 mindevid = (u64 )- 1 ;
1460
+ char * devstr = NULL ;
1461
+
1462
+ struct stat stat_buf ;
1463
+ char * sizestr = NULL ;
1464
+ u64 new_size = 0 , old_size = 0 , diff = 0 ;
1465
+ int mod = 0 ;
1466
+
1467
+ if (check_mounted (path )) {
1468
+ error ("%s must not be mounted to use --offline" , path );
1469
+ ret = 1 ;
1470
+ goto out ;
1471
+ }
1472
+
1473
+ if (!fs_info -> fs_devices -> num_devices ) {
1474
+ error ("no devices found" );
1475
+ ret = 1 ;
1476
+ goto out ;
1477
+ }
1478
+
1479
+ ret = snprintf (amount_dup , BTRFS_VOL_NAME_MAX , "%s" , amount );
1480
+ if (strlen (amount ) != ret ) {
1481
+ error ("newsize argument is too long" );
1482
+ ret = 1 ;
1483
+ goto out ;
1484
+ }
1485
+ ret = 0 ;
1486
+
1487
+ if (strcmp (amount , "cancel" ) == 0 ) {
1488
+ error ("cannot cancel offline resize since the operation is synchronous" );
1489
+ ret = 1 ;
1490
+ goto out ;
1491
+ }
1492
+
1493
+ /* Parse device id. */
1494
+ sizestr = amount_dup ;
1495
+ devstr = strchr (sizestr , ':' );
1496
+ if (devstr ) {
1497
+ sizestr = devstr + 1 ;
1498
+ * devstr = 0 ;
1499
+ devstr = amount_dup ;
1500
+
1501
+ errno = 0 ;
1502
+ devid = strtoull (devstr , NULL , 10 );
1503
+
1504
+ if (errno ) {
1505
+ error ("failed to parse devid %s: %m" , devstr );
1506
+ ret = 1 ;
1507
+ goto out ;
1508
+ }
1509
+ }
1510
+
1511
+ /* Find device matching device id */
1512
+ list_for_each_entry (device , & fs_info -> fs_devices -> devices , dev_list ) {
1513
+ if (device -> devid < mindevid ) {
1514
+ mindevid = device -> devid ;
1515
+ mindev = device ;
1516
+ }
1517
+ if (device -> devid == devid ) {
1518
+ dev_found = true;
1519
+ break ;
1520
+ }
1521
+ }
1522
+
1523
+ if (devstr && !dev_found ) {
1524
+ /* Devid specified but not found. */
1525
+ error ("cannot find devid: %lld" , devid );
1526
+ ret = 1 ;
1527
+ goto out ;
1528
+ } else if (!devstr && devid == 1 && !dev_found ) {
1529
+ /*
1530
+ * No device specified, assuming implicit 1 but it does not
1531
+ * exist. Use minimum device as fallback.
1532
+ */
1533
+ warning ("no devid specified means devid 1 which does not exist, using\n"
1534
+ "\t lowest devid %llu as a fallback" ,
1535
+ mindevid );
1536
+ devid = mindevid ;
1537
+ device = mindev ;
1538
+ }
1539
+ if (!device ) {
1540
+ error ("unable to find device" );
1541
+ ret = 1 ;
1542
+ goto out ;
1543
+ }
1544
+ * device_ret = device ;
1545
+ old_size = device -> total_bytes ;
1546
+
1547
+ if (strcmp (sizestr , "max" ) == 0 ) {
1548
+ if (path_is_block_device (device -> name )) {
1549
+ new_size = device_get_partition_size (device -> name );
1550
+ } else if (path_is_reg_file (device -> name )) {
1551
+ stat (device -> name , & stat_buf );
1552
+ new_size = stat_buf .st_size ;
1553
+ }
1554
+
1555
+ if (new_size == 0 ) {
1556
+ error ("unable to get size for device: %s" ,
1557
+ device -> name );
1558
+ ret = 1 ;
1559
+ goto out ;
1560
+ }
1561
+ } else {
1562
+ if (sizestr [0 ] == '-' ) {
1563
+ error ("offline resize does not support shrinking" );
1564
+ ret = 1 ;
1565
+ goto out ;
1566
+ } else if (sizestr [0 ] == '+' ) {
1567
+ mod = 1 ;
1568
+ sizestr ++ ;
1569
+ }
1570
+ ret = parse_u64_with_suffix (sizestr , & diff );
1571
+ if (ret < 0 ) {
1572
+ error ("failed to parse size %s" , sizestr );
1573
+ ret = 1 ;
1574
+ goto out ;
1575
+ }
1576
+
1577
+ /* For target sizes without +/- sign prefix (e.g. 1:150g) */
1578
+ if (mod == 0 ) {
1579
+ new_size = diff ;
1580
+ } else if (mod > 0 ) {
1581
+ if (diff > ULLONG_MAX - old_size ) {
1582
+ error ("increasing %s is out of range" ,
1583
+ pretty_size_mode (diff , UNITS_DEFAULT ));
1584
+ ret = 1 ;
1585
+ goto out ;
1586
+ }
1587
+ new_size = old_size + diff ;
1588
+ }
1589
+ }
1590
+ new_size = round_down (new_size , fs_info -> sectorsize );
1591
+ if (new_size < old_size ) {
1592
+ error ("offline resize does not support shrinking" );
1593
+ ret = 1 ;
1594
+ goto out ;
1595
+ }
1596
+ * new_size_ret = new_size ;
1597
+
1598
+ if (path_is_block_device (device -> name ) &&
1599
+ new_size > device_get_partition_size (device -> name )) {
1600
+ error ("unable to resize '%s': not enough free space" ,
1601
+ device -> name );
1602
+ ret = 1 ;
1603
+ goto out ;
1604
+ }
1605
+
1606
+ if (new_size < 256 * SZ_1M )
1607
+ warning ("the new size %lld (%s) is < 256MiB, this may be rejected by kernel" ,
1608
+ new_size , pretty_size_mode (new_size , UNITS_DEFAULT ));
1609
+
1610
+ pr_verbose (LOG_DEFAULT , "Resize device id %lld from %s to %s\n" , devid ,
1611
+ pretty_size_mode (old_size , UNITS_DEFAULT ),
1612
+ pretty_size_mode (new_size , UNITS_DEFAULT ));
1613
+ out :
1614
+ return ret ;
1615
+ }
1616
+
1617
+ static int offline_resize (const char * amount , const char * path )
1618
+ {
1619
+ int ret = 0 , ret2 = 0 ;
1620
+ struct btrfs_root * root ;
1621
+ struct btrfs_fs_info * fs_info ;
1622
+ struct btrfs_device * device ;
1623
+ struct btrfs_super_block * super_copy ;
1624
+ struct btrfs_trans_handle * trans ;
1625
+ u64 new_size ;
1626
+
1627
+ u64 old_total ;
1628
+ u64 diff ;
1629
+
1630
+ char dev_name [BTRFS_VOL_NAME_MAX ];
1631
+
1632
+ root = open_ctree (path , 0 , OPEN_CTREE_WRITES );
1633
+ if (!root ) {
1634
+ error ("could not open file at %s\n"
1635
+ "offline resize works on a file containing a btrfs image." ,
1636
+ path );
1637
+ return 1 ;
1638
+ }
1639
+ fs_info = root -> fs_info ;
1640
+ super_copy = fs_info -> super_copy ;
1641
+
1642
+ ret = check_offline_resize_args (fs_info , amount , path , & device ,
1643
+ & new_size );
1644
+ if (ret ) {
1645
+ ret = 1 ;
1646
+ goto close ;
1647
+ }
1648
+
1649
+ ret = snprintf (dev_name , BTRFS_VOL_NAME_MAX , "%s" , device -> name );
1650
+ if (strlen (device -> name ) != ret ) {
1651
+ error ("device name too long %s" , device -> name );
1652
+ ret = 1 ;
1653
+ goto close ;
1654
+ }
1655
+ ret = 0 ;
1656
+
1657
+ trans = btrfs_start_transaction (root , 0 );
1658
+ old_total = btrfs_super_total_bytes (super_copy );
1659
+ diff = round_down (new_size - device -> total_bytes , fs_info -> sectorsize );
1660
+ btrfs_set_super_total_bytes (
1661
+ super_copy , round_down (old_total + diff , fs_info -> sectorsize ));
1662
+ device -> total_bytes = new_size ;
1663
+
1664
+ ret = btrfs_update_device (trans , device );
1665
+ ret2 = btrfs_commit_transaction (trans , root );
1666
+ if (ret2 )
1667
+ ret = 1 ;
1668
+ close :
1669
+ ret2 = close_ctree (root );
1670
+ if (ret2 )
1671
+ ret = 1 ;
1672
+
1673
+ if (ret )
1674
+ return ret ;
1675
+
1676
+ if (path_is_reg_file (dev_name )) {
1677
+ ret = truncate (dev_name , new_size );
1678
+ if (ret ) {
1679
+ error ("failed to truncate %s" , device -> name );
1680
+ ret = 1 ;
1681
+ }
1682
+ }
1683
+ return ret ;
1684
+ }
1685
+
1443
1686
static int cmd_filesystem_resize (const struct cmd_struct * cmd ,
1444
1687
int argc , char * * argv )
1445
1688
{
@@ -1449,6 +1692,7 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1449
1692
u64 devid ;
1450
1693
int ret ;
1451
1694
bool enqueue = false;
1695
+ bool offline = false;
1452
1696
bool cancel = false;
1453
1697
1454
1698
/*
@@ -1458,6 +1702,8 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1458
1702
for (optind = 1 ; optind < argc ; optind ++ ) {
1459
1703
if (strcmp (argv [optind ], "--enqueue" ) == 0 ) {
1460
1704
enqueue = true;
1705
+ } else if (strcmp (argv [optind ], "--offline" ) == 0 ) {
1706
+ offline = true;
1461
1707
} else if (strcmp (argv [optind ], "--" ) == 0 ) {
1462
1708
/* Separator: options -- non-options */
1463
1709
} else if (strncmp (argv [optind ], "--" , 2 ) == 0 ) {
@@ -1472,6 +1718,12 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1472
1718
if (check_argc_exact (argc - optind , 2 ))
1473
1719
return 1 ;
1474
1720
1721
+ if (offline && enqueue ) {
1722
+ error ("--enqueue is not compatible with --offline\n"
1723
+ "since offline resizing is synchronous" );
1724
+ return 1 ;
1725
+ }
1726
+
1475
1727
amount = argv [optind ];
1476
1728
path = argv [optind + 1 ];
1477
1729
@@ -1481,6 +1733,9 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1481
1733
return 1 ;
1482
1734
}
1483
1735
1736
+ if (offline )
1737
+ return offline_resize (amount , path );
1738
+
1484
1739
cancel = (strcmp ("cancel" , amount ) == 0 );
1485
1740
1486
1741
fd = btrfs_open_dir (path );
@@ -1490,7 +1745,8 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1490
1745
error (
1491
1746
"resize works on mounted filesystems and accepts only\n"
1492
1747
"directories as argument. Passing file containing a btrfs image\n"
1493
- "would resize the underlying filesystem instead of the image.\n" );
1748
+ "would resize the underlying filesystem instead of the image.\n"
1749
+ "To resize a file containing a btrfs image please use the --offline flag.\n" );
1494
1750
}
1495
1751
return 1 ;
1496
1752
}
0 commit comments