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,231 @@ 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 ;
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
+ new_size = device_get_partition_size (device -> name );
1389
+ } else if (path_is_reg_file (device -> name )) {
1390
+ stat (device -> name , & stat_buf );
1391
+ new_size = stat_buf .st_size ;
1392
+ }
1393
+
1394
+ if (new_size == 0 ) {
1395
+ error ("unable to get size for device: %s" ,
1396
+ device -> name );
1397
+ return 1 ;
1398
+ }
1399
+ } else {
1400
+ if (sizestr [0 ] == '-' ) {
1401
+ error ("offline resize does not support shrinking" );
1402
+ return 1 ;
1403
+ } else if (sizestr [0 ] == '+' ) {
1404
+ mod = 1 ;
1405
+ sizestr ++ ;
1406
+ }
1407
+ ret = parse_u64_with_suffix (sizestr , & diff );
1408
+ if (ret < 0 ) {
1409
+ error ("failed to parse size %s" , sizestr );
1410
+ return 1 ;
1411
+ }
1412
+
1413
+ /* For target sizes without +/- sign prefix (e.g. 1:150g) */
1414
+ if (mod == 0 ) {
1415
+ new_size = diff ;
1416
+ } else if (mod > 0 ) {
1417
+ if (diff > ULLONG_MAX - old_size ) {
1418
+ error ("increasing %s is out of range" ,
1419
+ pretty_size_mode (diff , UNITS_DEFAULT ));
1420
+ return 1 ;
1421
+ }
1422
+ new_size = old_size + diff ;
1423
+ }
1424
+ }
1425
+ new_size = round_down (new_size , fs_info -> sectorsize );
1426
+ if (new_size < old_size ) {
1427
+ error ("offline resize does not support shrinking" );
1428
+ return 1 ;
1429
+ }
1430
+ * new_size_ret = new_size ;
1431
+
1432
+ if (path_is_block_device (device -> name ) &&
1433
+ new_size > device_get_partition_size (device -> name )) {
1434
+ error ("unable to resize '%s': not enough free space" ,
1435
+ device -> name );
1436
+ return 1 ;
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 device id %lld from %s to %s\n" , devid ,
1444
+ pretty_size_mode (old_size , UNITS_DEFAULT ),
1445
+ pretty_size_mode (new_size , UNITS_DEFAULT ));
1446
+ return 0 ;
1447
+ }
1448
+
1449
+ static int offline_resize (const char * path , const char * amount )
1450
+ {
1451
+ int ret = 0 ;
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
+ char dev_name [BTRFS_VOL_NAME_MAX ];
1461
+
1462
+ root = open_ctree (path , 0 , OPEN_CTREE_WRITES | OPEN_CTREE_CHUNK_ROOT_ONLY );
1463
+ if (!root ) {
1464
+ error ("could not open file at %s\n"
1465
+ "offline resize works on a file containing a btrfs image." ,
1466
+ path );
1467
+ return 1 ;
1468
+ }
1469
+ fs_info = root -> fs_info ;
1470
+ super = fs_info -> super_copy ;
1471
+
1472
+ ret = check_offline_resize_args (path , amount , fs_info , & device ,
1473
+ & new_size );
1474
+ if (ret ) {
1475
+ ret = 1 ;
1476
+ goto close ;
1477
+ }
1478
+
1479
+ ret = snprintf (dev_name , BTRFS_VOL_NAME_MAX , "%s" , device -> name );
1480
+ if (strlen (device -> name ) != ret ) {
1481
+ error ("device name too long %s" , device -> name );
1482
+ ret = 1 ;
1483
+ goto close ;
1484
+ }
1485
+ ret = 0 ;
1486
+
1487
+ trans = btrfs_start_transaction (root , 1 );
1488
+ if (IS_ERR (trans )) {
1489
+ ret = PTR_ERR (trans );
1490
+ errno = - ret ;
1491
+ error_msg (ERROR_MSG_START_TRANS , "%m" );
1492
+ return ret ;
1493
+ }
1494
+ old_total = btrfs_super_total_bytes (super );
1495
+ diff = round_down (new_size - device -> total_bytes , fs_info -> sectorsize );
1496
+ btrfs_set_super_total_bytes (super , round_down (old_total + diff ,
1497
+ fs_info -> sectorsize ));
1498
+ device -> total_bytes = new_size ;
1499
+ ret = btrfs_update_device (trans , device );
1500
+ if (ret ) {
1501
+ btrfs_abort_transaction (trans , ret );
1502
+ goto close ;
1503
+ }
1504
+ ret |= btrfs_commit_transaction (trans , root );
1505
+ close :
1506
+ ret |= close_ctree (root );
1507
+ if (ret )
1508
+ return ret ;
1509
+
1510
+ if (path_is_reg_file (dev_name ))
1511
+ ret = truncate (dev_name , new_size );
1512
+ if (ret )
1513
+ error ("failed to truncate %s" , device -> name );
1514
+ return ret ;
1515
+ }
1516
+
1293
1517
static int check_resize_args (const char * amount , const char * path , u64 * devid_ret )
1294
1518
{
1295
1519
struct btrfs_ioctl_fs_info_args fi_args ;
@@ -1449,6 +1673,7 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1449
1673
u64 devid ;
1450
1674
int ret ;
1451
1675
bool enqueue = false;
1676
+ bool offline = false;
1452
1677
bool cancel = false;
1453
1678
1454
1679
/*
@@ -1458,6 +1683,8 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1458
1683
for (optind = 1 ; optind < argc ; optind ++ ) {
1459
1684
if (strcmp (argv [optind ], "--enqueue" ) == 0 ) {
1460
1685
enqueue = true;
1686
+ } else if (strcmp (argv [optind ], "--offline" ) == 0 ) {
1687
+ offline = true;
1461
1688
} else if (strcmp (argv [optind ], "--" ) == 0 ) {
1462
1689
/* Separator: options -- non-options */
1463
1690
} else if (strncmp (argv [optind ], "--" , 2 ) == 0 ) {
@@ -1472,6 +1699,12 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1472
1699
if (check_argc_exact (argc - optind , 2 ))
1473
1700
return 1 ;
1474
1701
1702
+ if (offline && enqueue ) {
1703
+ error ("--enqueue is not compatible with --offline\n"
1704
+ "since offline resizing is synchronous" );
1705
+ return 1 ;
1706
+ }
1707
+
1475
1708
amount = argv [optind ];
1476
1709
path = argv [optind + 1 ];
1477
1710
@@ -1481,6 +1714,9 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1481
1714
return 1 ;
1482
1715
}
1483
1716
1717
+ if (offline )
1718
+ return offline_resize (path , amount );
1719
+
1484
1720
cancel = (strcmp ("cancel" , amount ) == 0 );
1485
1721
1486
1722
fd = btrfs_open_dir (path );
@@ -1490,7 +1726,8 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
1490
1726
error (
1491
1727
"resize works on mounted filesystems and accepts only\n"
1492
1728
"directories as argument. Passing file containing a btrfs image\n"
1493
- "would resize the underlying filesystem instead of the image.\n" );
1729
+ "would resize the underlying filesystem instead of the image.\n"
1730
+ "To resize a file containing a btrfs image please use the --offline flag.\n" );
1494
1731
}
1495
1732
return 1 ;
1496
1733
}
0 commit comments