generic: test MADV_POPULATE_READ with IO errors master v2024.03.31
authorDarrick J. Wong <djwong@kernel.org>
Wed, 27 Mar 2024 02:43:41 +0000 (19:43 -0700)
committerZorro Lang <zlang@kernel.org>
Sat, 30 Mar 2024 07:48:12 +0000 (15:48 +0800)
This is a regression test for "mm/madvise: make
MADV_POPULATE_(READ|WRITE) handle VM_FAULT_RETRY properly".

Cc: David Hildenbrand <david@redhat.com>
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Zorro Lang <zlang@redhat.com>
Signed-off-by: Zorro Lang <zlang@kernel.org>
1392 files changed:
.gitignore
MAINTAINERS [new file with mode: 0644]
README
README.config-sections
README.fuse [new file with mode: 0644]
README.overlay
README.selftest [new file with mode: 0644]
acinclude.m4
build/rpm/xfstests.spec.in
check
common/attr
common/btrfs
common/config
common/dmerror
common/dmlogwrites
common/dmthin
common/dump
common/encrypt
common/ext4 [new file with mode: 0644]
common/fail_make_request [new file with mode: 0644]
common/filter
common/filter.btrfs
common/fuzzy
common/gcov [new file with mode: 0644]
common/metadump [new file with mode: 0644]
common/module
common/overlay
common/populate
common/punch
common/quota
common/rc
common/reflink
common/repair
common/report
common/tracing [new file with mode: 0644]
common/ubifs [new file with mode: 0644]
common/verity
common/xfs
common/zoned [new file with mode: 0644]
configure.ac
doc/group-names.txt
doc/requirement-checking.txt
doc/testing-afs.txt [new file with mode: 0644]
doc/xunit.xsd [new file with mode: 0644]
include/builddefs.in
lsqa.pl
ltp/Makefile
ltp/doio.c
ltp/fsstress.c
ltp/fsx.c
m4/manual_format.m4 [deleted file]
m4/package_libcdev.m4
m4/package_ncurses.m4 [deleted file]
m4/package_pthread.m4 [deleted file]
m4/package_types.m4 [deleted file]
m4/package_xfslibs.m4
m4/visibility_hidden.m4 [deleted file]
src/Makefile
src/af_unix.c
src/aio-dio-regress/aio-dio-append-write-read-race.c
src/aio-dio-regress/aio-dio-write-verify.c
src/aio-dio-regress/aiodio_sparse2.c
src/attr_replace_test.c
src/btrfs_crc32c_forged_name.py
src/detached_mounts_propagation.c
src/dio-buf-fault.c [new file with mode: 0644]
src/ext4_resize.c
src/fake-dump-rootino.c [new file with mode: 0644]
src/feature.c
src/fiemap-fault.c [new file with mode: 0644]
src/fiemap-tester.c
src/fiexchange.h [new file with mode: 0644]
src/fscrypt-crypt-util.c
src/fstest.c
src/global.h
src/mmap-rw-fault.c
src/popattr.py [new file with mode: 0755]
src/popdir.pl [new file with mode: 0755]
src/readdir-while-renames.c [new file with mode: 0644]
src/rewinddir-test.c [new file with mode: 0644]
src/seek_sanity_test.c
src/soak_duration.awk [new file with mode: 0644]
src/splice-test.c
src/splice2pipe.c
src/t_getcwd.c
src/t_mmap_cow_memory_failure.c [new file with mode: 0644]
src/t_mtab.c
src/t_ofd_locks.c
src/t_readdir_3.c
src/t_reflink_read_race.c [new file with mode: 0644]
src/t_snapshot_deleted_subvolume.c [new file with mode: 0644]
src/uuid_ioctl.c [new file with mode: 0644]
src/vfs/Makefile
src/vfs/idmapped-mounts.c
src/vfs/idmapped-mounts.h
src/vfs/tmpfs-idmapped-mounts.c [new file with mode: 0644]
src/vfs/tmpfs-idmapped-mounts.h [new file with mode: 0644]
src/vfs/utils.c
src/vfs/utils.h
src/vfs/vfstest.c
src/vfs/vfstest.h [new file with mode: 0644]
src/xfsfind.c [new file with mode: 0644]
tests/btrfs/003
tests/btrfs/004
tests/btrfs/005
tests/btrfs/011
tests/btrfs/012
tests/btrfs/013
tests/btrfs/015
tests/btrfs/016
tests/btrfs/017
tests/btrfs/021
tests/btrfs/022
tests/btrfs/024
tests/btrfs/025
tests/btrfs/027
tests/btrfs/028
tests/btrfs/034
tests/btrfs/037
tests/btrfs/046
tests/btrfs/048
tests/btrfs/049
tests/btrfs/052
tests/btrfs/053
tests/btrfs/056
tests/btrfs/057
tests/btrfs/059
tests/btrfs/060
tests/btrfs/061
tests/btrfs/062
tests/btrfs/063
tests/btrfs/064
tests/btrfs/065
tests/btrfs/066
tests/btrfs/067
tests/btrfs/068
tests/btrfs/069
tests/btrfs/070
tests/btrfs/071
tests/btrfs/072
tests/btrfs/073
tests/btrfs/074
tests/btrfs/076
tests/btrfs/076.out
tests/btrfs/079
tests/btrfs/080
tests/btrfs/088
tests/btrfs/091
tests/btrfs/094
tests/btrfs/095
tests/btrfs/100
tests/btrfs/101
tests/btrfs/104
tests/btrfs/106
tests/btrfs/106.out
tests/btrfs/112
tests/btrfs/121
tests/btrfs/122
tests/btrfs/123
tests/btrfs/125
tests/btrfs/126
tests/btrfs/131
tests/btrfs/136
tests/btrfs/137
tests/btrfs/138
tests/btrfs/139
tests/btrfs/140
tests/btrfs/141
tests/btrfs/142
tests/btrfs/143
tests/btrfs/148
tests/btrfs/150
tests/btrfs/153
tests/btrfs/154
tests/btrfs/157
tests/btrfs/158
tests/btrfs/163
tests/btrfs/164
tests/btrfs/167
tests/btrfs/169
tests/btrfs/170
tests/btrfs/171
tests/btrfs/172
tests/btrfs/173
tests/btrfs/174
tests/btrfs/175
tests/btrfs/176
tests/btrfs/177
tests/btrfs/179
tests/btrfs/180
tests/btrfs/185
tests/btrfs/187
tests/btrfs/190
tests/btrfs/192
tests/btrfs/193
tests/btrfs/195
tests/btrfs/198
tests/btrfs/199
tests/btrfs/200
tests/btrfs/202
tests/btrfs/205
tests/btrfs/206
tests/btrfs/208
tests/btrfs/208.out
tests/btrfs/210
tests/btrfs/211
tests/btrfs/213
tests/btrfs/215
tests/btrfs/216
tests/btrfs/218
tests/btrfs/219
tests/btrfs/220
tests/btrfs/224
tests/btrfs/225
tests/btrfs/228
tests/btrfs/230
tests/btrfs/232
tests/btrfs/233
tests/btrfs/233.out
tests/btrfs/234
tests/btrfs/237
tests/btrfs/238
tests/btrfs/239
tests/btrfs/246
tests/btrfs/249
tests/btrfs/251
tests/btrfs/252
tests/btrfs/253
tests/btrfs/254
tests/btrfs/256
tests/btrfs/257
tests/btrfs/258
tests/btrfs/259
tests/btrfs/260
tests/btrfs/261 [new file with mode: 0755]
tests/btrfs/261.out [new file with mode: 0644]
tests/btrfs/263
tests/btrfs/265 [new file with mode: 0755]
tests/btrfs/265.out [new file with mode: 0644]
tests/btrfs/266 [new file with mode: 0755]
tests/btrfs/266.out [new file with mode: 0644]
tests/btrfs/267 [new file with mode: 0755]
tests/btrfs/267.out [new file with mode: 0644]
tests/btrfs/268 [new file with mode: 0755]
tests/btrfs/268.out [new file with mode: 0644]
tests/btrfs/269 [new file with mode: 0755]
tests/btrfs/269.out [new file with mode: 0644]
tests/btrfs/270 [new file with mode: 0755]
tests/btrfs/270.out [new file with mode: 0644]
tests/btrfs/271 [new file with mode: 0755]
tests/btrfs/271.out [new file with mode: 0644]
tests/btrfs/272 [new file with mode: 0755]
tests/btrfs/272.out [new file with mode: 0644]
tests/btrfs/273 [new file with mode: 0755]
tests/btrfs/273.out [new file with mode: 0644]
tests/btrfs/274 [new file with mode: 0755]
tests/btrfs/274.out [new file with mode: 0644]
tests/btrfs/275 [new file with mode: 0755]
tests/btrfs/275.out [new file with mode: 0644]
tests/btrfs/276 [new file with mode: 0755]
tests/btrfs/276.out [new file with mode: 0644]
tests/btrfs/277 [new file with mode: 0755]
tests/btrfs/277.out [new file with mode: 0644]
tests/btrfs/278 [new file with mode: 0755]
tests/btrfs/278.out [new file with mode: 0644]
tests/btrfs/279 [new file with mode: 0755]
tests/btrfs/279.out [new file with mode: 0644]
tests/btrfs/280 [new file with mode: 0755]
tests/btrfs/280.out [new file with mode: 0644]
tests/btrfs/281 [new file with mode: 0755]
tests/btrfs/281.out [new file with mode: 0644]
tests/btrfs/282 [new file with mode: 0755]
tests/btrfs/282.out [new file with mode: 0644]
tests/btrfs/283 [new file with mode: 0755]
tests/btrfs/283.out [new file with mode: 0644]
tests/btrfs/284 [new file with mode: 0755]
tests/btrfs/284.out [new file with mode: 0644]
tests/btrfs/285 [new file with mode: 0755]
tests/btrfs/285.out [new file with mode: 0644]
tests/btrfs/286 [new file with mode: 0755]
tests/btrfs/286.out [new file with mode: 0644]
tests/btrfs/287 [new file with mode: 0755]
tests/btrfs/287.out [new file with mode: 0644]
tests/btrfs/288 [new file with mode: 0755]
tests/btrfs/288.out [new file with mode: 0644]
tests/btrfs/289 [new file with mode: 0755]
tests/btrfs/289.out [new file with mode: 0644]
tests/btrfs/290 [new file with mode: 0755]
tests/btrfs/290.out [new file with mode: 0644]
tests/btrfs/291 [new file with mode: 0755]
tests/btrfs/291.out [new file with mode: 0644]
tests/btrfs/292 [new file with mode: 0755]
tests/btrfs/292.out [new file with mode: 0644]
tests/btrfs/293 [new file with mode: 0755]
tests/btrfs/293.out [new file with mode: 0644]
tests/btrfs/294 [new file with mode: 0755]
tests/btrfs/294.out [new file with mode: 0644]
tests/btrfs/295 [new file with mode: 0755]
tests/btrfs/295.out [new file with mode: 0644]
tests/btrfs/296 [new file with mode: 0755]
tests/btrfs/296.out [new file with mode: 0644]
tests/btrfs/297 [new file with mode: 0755]
tests/btrfs/297.out [new file with mode: 0644]
tests/btrfs/298 [new file with mode: 0755]
tests/btrfs/298.out [new file with mode: 0644]
tests/btrfs/299 [new file with mode: 0755]
tests/btrfs/299.out [new file with mode: 0644]
tests/btrfs/300 [new file with mode: 0755]
tests/btrfs/300.out [new file with mode: 0644]
tests/btrfs/301 [new file with mode: 0755]
tests/btrfs/301.out [new file with mode: 0644]
tests/btrfs/302 [new file with mode: 0755]
tests/btrfs/302.out [new file with mode: 0644]
tests/btrfs/303 [new file with mode: 0755]
tests/btrfs/303.out [new file with mode: 0644]
tests/btrfs/304 [new file with mode: 0755]
tests/btrfs/304.out [new file with mode: 0644]
tests/btrfs/305 [new file with mode: 0755]
tests/btrfs/305.out [new file with mode: 0644]
tests/btrfs/306 [new file with mode: 0755]
tests/btrfs/306.out [new file with mode: 0644]
tests/btrfs/307 [new file with mode: 0755]
tests/btrfs/307.out [new file with mode: 0644]
tests/btrfs/308 [new file with mode: 0755]
tests/btrfs/308.out [new file with mode: 0644]
tests/btrfs/309 [new file with mode: 0755]
tests/btrfs/309.out [new file with mode: 0644]
tests/btrfs/310 [new file with mode: 0755]
tests/btrfs/310.out [new file with mode: 0644]
tests/btrfs/311 [new file with mode: 0755]
tests/btrfs/311.out [new file with mode: 0644]
tests/btrfs/312 [new file with mode: 0755]
tests/btrfs/312.out [new file with mode: 0644]
tests/btrfs/313 [new file with mode: 0755]
tests/btrfs/313.out [new file with mode: 0644]
tests/btrfs/314 [new file with mode: 0755]
tests/btrfs/314.out [new file with mode: 0644]
tests/btrfs/315 [new file with mode: 0755]
tests/btrfs/315.out [new file with mode: 0644]
tests/btrfs/316 [new file with mode: 0755]
tests/btrfs/316.out [new file with mode: 0644]
tests/btrfs/317 [new file with mode: 0755]
tests/btrfs/317.out [new file with mode: 0644]
tests/btrfs/320 [new file with mode: 0755]
tests/btrfs/320.out [new file with mode: 0644]
tests/btrfs/330 [new file with mode: 0755]
tests/btrfs/330.out [new file with mode: 0644]
tests/btrfs/Makefile
tests/ceph/001
tests/ceph/002.out
tests/ceph/004
tests/ceph/005 [new file with mode: 0755]
tests/ceph/005.out [new file with mode: 0644]
tests/ceph/Makefile
tests/cifs/Makefile
tests/ext4/001
tests/ext4/003
tests/ext4/006
tests/ext4/009
tests/ext4/015
tests/ext4/022
tests/ext4/034
tests/ext4/035
tests/ext4/044
tests/ext4/044.out
tests/ext4/045
tests/ext4/050
tests/ext4/053
tests/ext4/054
tests/ext4/055
tests/ext4/057 [new file with mode: 0755]
tests/ext4/057.out [new file with mode: 0644]
tests/ext4/058 [new file with mode: 0755]
tests/ext4/058.out [new file with mode: 0644]
tests/ext4/059 [new file with mode: 0755]
tests/ext4/059.out [new file with mode: 0644]
tests/ext4/306
tests/ext4/307
tests/ext4/308
tests/ext4/Makefile
tests/f2fs/001
tests/f2fs/002
tests/f2fs/Makefile
tests/generic/009
tests/generic/012
tests/generic/016
tests/generic/017
tests/generic/019
tests/generic/020
tests/generic/021
tests/generic/022
tests/generic/031
tests/generic/032
tests/generic/038
tests/generic/042
tests/generic/043
tests/generic/044
tests/generic/045
tests/generic/046
tests/generic/047
tests/generic/048
tests/generic/049
tests/generic/058
tests/generic/060
tests/generic/061
tests/generic/063
tests/generic/064
tests/generic/068
tests/generic/081
tests/generic/085
tests/generic/092
tests/generic/094
tests/generic/099
tests/generic/103
tests/generic/105
tests/generic/108
tests/generic/110
tests/generic/111
tests/generic/115
tests/generic/116
tests/generic/118
tests/generic/119
tests/generic/121
tests/generic/122
tests/generic/123
tests/generic/125
tests/generic/128
tests/generic/134
tests/generic/136
tests/generic/137
tests/generic/139
tests/generic/144
tests/generic/145
tests/generic/149
tests/generic/153
tests/generic/156
tests/generic/158
tests/generic/162
tests/generic/163
tests/generic/164
tests/generic/165
tests/generic/168
tests/generic/170
tests/generic/175
tests/generic/177
tests/generic/181
tests/generic/183
tests/generic/185
tests/generic/186
tests/generic/187
tests/generic/188
tests/generic/189
tests/generic/190
tests/generic/191
tests/generic/192
tests/generic/193
tests/generic/194
tests/generic/195
tests/generic/196
tests/generic/197
tests/generic/199
tests/generic/200
tests/generic/201
tests/generic/216
tests/generic/217
tests/generic/218
tests/generic/219
tests/generic/220
tests/generic/222
tests/generic/223
tests/generic/225
tests/generic/227
tests/generic/229
tests/generic/237
tests/generic/238
tests/generic/245
tests/generic/245.out
tests/generic/251
tests/generic/255
tests/generic/256
tests/generic/260
tests/generic/264
tests/generic/269
tests/generic/270
tests/generic/273
tests/generic/275
tests/generic/280
tests/generic/284
tests/generic/285
tests/generic/286
tests/generic/287
tests/generic/289
tests/generic/290
tests/generic/291
tests/generic/292
tests/generic/293
tests/generic/294
tests/generic/295
tests/generic/297
tests/generic/298
tests/generic/299
tests/generic/301
tests/generic/302
tests/generic/305
tests/generic/311
tests/generic/314
tests/generic/316
tests/generic/317
tests/generic/318
tests/generic/319
tests/generic/324
tests/generic/326
tests/generic/327
tests/generic/328
tests/generic/342
tests/generic/351
tests/generic/352
tests/generic/352.out
tests/generic/353
tests/generic/353.out
tests/generic/355
tests/generic/357
tests/generic/358
tests/generic/359
tests/generic/362
tests/generic/363
tests/generic/364
tests/generic/365
tests/generic/366
tests/generic/367
tests/generic/368
tests/generic/369
tests/generic/370
tests/generic/372
tests/generic/375
tests/generic/388
tests/generic/390
tests/generic/391
tests/generic/392
tests/generic/394
tests/generic/404
tests/generic/409
tests/generic/410
tests/generic/411
tests/generic/413
tests/generic/414
tests/generic/416
tests/generic/422
tests/generic/425
tests/generic/436
tests/generic/441
tests/generic/444
tests/generic/445
tests/generic/446
tests/generic/448
tests/generic/454
tests/generic/455
tests/generic/457
tests/generic/459
tests/generic/465
tests/generic/468
tests/generic/469
tests/generic/470
tests/generic/471
tests/generic/471.out
tests/generic/472
tests/generic/473
tests/generic/475
tests/generic/476
tests/generic/478
tests/generic/482
tests/generic/483
tests/generic/484
tests/generic/485
tests/generic/486
tests/generic/487
tests/generic/491
tests/generic/495
tests/generic/496
tests/generic/497
tests/generic/499
tests/generic/500
tests/generic/501
tests/generic/502
tests/generic/503
tests/generic/506
tests/generic/511
tests/generic/513
tests/generic/515
tests/generic/516
tests/generic/519
tests/generic/521
tests/generic/522
tests/generic/526
tests/generic/527
tests/generic/531
tests/generic/540
tests/generic/541
tests/generic/542
tests/generic/543
tests/generic/544
tests/generic/546
tests/generic/551
tests/generic/558
tests/generic/569
tests/generic/570
tests/generic/572
tests/generic/572.out
tests/generic/573
tests/generic/574
tests/generic/574.out
tests/generic/575
tests/generic/575.out
tests/generic/576
tests/generic/577
tests/generic/577.out
tests/generic/578
tests/generic/587
tests/generic/588
tests/generic/589
tests/generic/591
tests/generic/591.out
tests/generic/597
tests/generic/598
tests/generic/604
tests/generic/605
tests/generic/610
tests/generic/614
tests/generic/615
tests/generic/616
tests/generic/617
tests/generic/619
tests/generic/623
tests/generic/624
tests/generic/624.out
tests/generic/627
tests/generic/628
tests/generic/629
tests/generic/631
tests/generic/636
tests/generic/641
tests/generic/642
tests/generic/646
tests/generic/648
tests/generic/649
tests/generic/650
tests/generic/652
tests/generic/653
tests/generic/654
tests/generic/655
tests/generic/658
tests/generic/659
tests/generic/660
tests/generic/661
tests/generic/662
tests/generic/663
tests/generic/664
tests/generic/665
tests/generic/666
tests/generic/667
tests/generic/668
tests/generic/669
tests/generic/670
tests/generic/673
tests/generic/673.out
tests/generic/674
tests/generic/675
tests/generic/677
tests/generic/679
tests/generic/681
tests/generic/682
tests/generic/682.out
tests/generic/683
tests/generic/683.out
tests/generic/684
tests/generic/684.out
tests/generic/685
tests/generic/685.out
tests/generic/686
tests/generic/686.out
tests/generic/687
tests/generic/687.out
tests/generic/688
tests/generic/692 [new file with mode: 0755]
tests/generic/692.out [new file with mode: 0644]
tests/generic/693 [new file with mode: 0755]
tests/generic/693.out [new file with mode: 0644]
tests/generic/694 [new file with mode: 0755]
tests/generic/694.out [new file with mode: 0644]
tests/generic/695 [new file with mode: 0755]
tests/generic/695.out [new file with mode: 0644]
tests/generic/696 [new file with mode: 0755]
tests/generic/696.out [new file with mode: 0644]
tests/generic/697 [new file with mode: 0755]
tests/generic/697.out [new file with mode: 0644]
tests/generic/698 [new file with mode: 0755]
tests/generic/698.out [new file with mode: 0644]
tests/generic/699 [new file with mode: 0755]
tests/generic/699.out [new file with mode: 0644]
tests/generic/700 [new file with mode: 0755]
tests/generic/700.out [new file with mode: 0644]
tests/generic/701 [new file with mode: 0755]
tests/generic/701.out [new file with mode: 0644]
tests/generic/702 [new file with mode: 0755]
tests/generic/702.out [new file with mode: 0644]
tests/generic/703 [new file with mode: 0755]
tests/generic/703.out [new file with mode: 0644]
tests/generic/704 [new file with mode: 0755]
tests/generic/704.out [new file with mode: 0644]
tests/generic/705 [new file with mode: 0755]
tests/generic/705.out [new file with mode: 0644]
tests/generic/706 [new file with mode: 0755]
tests/generic/706.out [new file with mode: 0644]
tests/generic/707 [new file with mode: 0755]
tests/generic/707.out [new file with mode: 0644]
tests/generic/708 [new file with mode: 0755]
tests/generic/708.out [new file with mode: 0644]
tests/generic/709 [new file with mode: 0755]
tests/generic/709.out [new file with mode: 0644]
tests/generic/710 [new file with mode: 0755]
tests/generic/710.out [new file with mode: 0644]
tests/generic/711 [new file with mode: 0755]
tests/generic/711.out [new file with mode: 0644]
tests/generic/712 [new file with mode: 0755]
tests/generic/712.out [new file with mode: 0644]
tests/generic/713 [new file with mode: 0755]
tests/generic/713.out [new file with mode: 0644]
tests/generic/714 [new file with mode: 0755]
tests/generic/714.out [new file with mode: 0644]
tests/generic/715 [new file with mode: 0755]
tests/generic/715.out [new file with mode: 0644]
tests/generic/716 [new file with mode: 0755]
tests/generic/716.out [new file with mode: 0644]
tests/generic/717 [new file with mode: 0755]
tests/generic/717.out [new file with mode: 0644]
tests/generic/718 [new file with mode: 0755]
tests/generic/718.out [new file with mode: 0644]
tests/generic/719 [new file with mode: 0755]
tests/generic/719.out [new file with mode: 0644]
tests/generic/720 [new file with mode: 0755]
tests/generic/720.out [new file with mode: 0644]
tests/generic/721 [new file with mode: 0755]
tests/generic/721.out [new file with mode: 0644]
tests/generic/722 [new file with mode: 0755]
tests/generic/722.out [new file with mode: 0644]
tests/generic/723 [new file with mode: 0755]
tests/generic/723.out [new file with mode: 0644]
tests/generic/724 [new file with mode: 0755]
tests/generic/724.out [new file with mode: 0644]
tests/generic/725 [new file with mode: 0755]
tests/generic/725.out [new file with mode: 0644]
tests/generic/726 [new file with mode: 0755]
tests/generic/726.out [new file with mode: 0644]
tests/generic/727 [new file with mode: 0755]
tests/generic/727.out [new file with mode: 0755]
tests/generic/728 [new file with mode: 0755]
tests/generic/728.out [new file with mode: 0644]
tests/generic/729 [new file with mode: 0755]
tests/generic/729.out [new file with mode: 0644]
tests/generic/730 [new file with mode: 0755]
tests/generic/730.out [new file with mode: 0644]
tests/generic/731 [new file with mode: 0755]
tests/generic/731.out [new file with mode: 0644]
tests/generic/732 [new file with mode: 0755]
tests/generic/732.out [new file with mode: 0644]
tests/generic/733 [new file with mode: 0755]
tests/generic/733.out [new file with mode: 0644]
tests/generic/734 [new file with mode: 0755]
tests/generic/734.out [new file with mode: 0644]
tests/generic/735 [new file with mode: 0755]
tests/generic/735.out [new file with mode: 0644]
tests/generic/736 [new file with mode: 0755]
tests/generic/736.out [new file with mode: 0644]
tests/generic/737 [new file with mode: 0755]
tests/generic/737.out [new file with mode: 0644]
tests/generic/738 [new file with mode: 0755]
tests/generic/738.out [new file with mode: 0644]
tests/generic/739 [new file with mode: 0755]
tests/generic/739.out [new file with mode: 0644]
tests/generic/741 [new file with mode: 0755]
tests/generic/741.out [new file with mode: 0644]
tests/generic/742 [new file with mode: 0755]
tests/generic/742.out [new file with mode: 0644]
tests/generic/743 [new file with mode: 0755]
tests/generic/743.out [new file with mode: 0644]
tests/generic/Makefile
tests/nfs/Makefile
tests/ocfs2/Makefile
tests/overlay/004
tests/overlay/008
tests/overlay/011
tests/overlay/015
tests/overlay/020
tests/overlay/023
tests/overlay/026
tests/overlay/026.out
tests/overlay/035
tests/overlay/052
tests/overlay/053
tests/overlay/060
tests/overlay/060.out
tests/overlay/062
tests/overlay/066
tests/overlay/079 [new file with mode: 0755]
tests/overlay/079.out [new file with mode: 0644]
tests/overlay/080 [new file with mode: 0755]
tests/overlay/080.out [new file with mode: 0644]
tests/overlay/081 [new file with mode: 0755]
tests/overlay/081.out [new file with mode: 0644]
tests/overlay/082 [new file with mode: 0755]
tests/overlay/082.out [new file with mode: 0644]
tests/overlay/083 [new file with mode: 0755]
tests/overlay/083.out [new file with mode: 0644]
tests/overlay/084 [new file with mode: 0755]
tests/overlay/084.out [new file with mode: 0644]
tests/overlay/085 [new file with mode: 0755]
tests/overlay/085.out [new file with mode: 0644]
tests/overlay/086 [new file with mode: 0755]
tests/overlay/086.out [new file with mode: 0644]
tests/overlay/Makefile
tests/perf/Makefile
tests/selftest/001 [new file with mode: 0755]
tests/selftest/001.out [new file with mode: 0644]
tests/selftest/002 [new file with mode: 0755]
tests/selftest/002.out [new file with mode: 0644]
tests/selftest/003 [new file with mode: 0755]
tests/selftest/003.out [new file with mode: 0644]
tests/selftest/004 [new file with mode: 0755]
tests/selftest/004.out [new file with mode: 0644]
tests/selftest/005 [new file with mode: 0755]
tests/selftest/005.out [new file with mode: 0644]
tests/selftest/006 [new file with mode: 0755]
tests/selftest/006.out [new file with mode: 0644]
tests/selftest/Makefile [new file with mode: 0644]
tests/shared/298
tests/shared/Makefile
tests/tmpfs/001 [new file with mode: 0755]
tests/tmpfs/001.out [new file with mode: 0644]
tests/tmpfs/Makefile [new file with mode: 0644]
tests/udf/Makefile
tests/xfs/006.out
tests/xfs/008
tests/xfs/011
tests/xfs/012
tests/xfs/014
tests/xfs/015
tests/xfs/018 [new file with mode: 0755]
tests/xfs/018.out [new file with mode: 0644]
tests/xfs/033
tests/xfs/041
tests/xfs/042
tests/xfs/042.out
tests/xfs/051
tests/xfs/057
tests/xfs/064
tests/xfs/065
tests/xfs/069
tests/xfs/076
tests/xfs/080
tests/xfs/081 [new file with mode: 0755]
tests/xfs/081.out [new file with mode: 0644]
tests/xfs/082 [new file with mode: 0755]
tests/xfs/082.out [new file with mode: 0644]
tests/xfs/083
tests/xfs/084
tests/xfs/097
tests/xfs/099
tests/xfs/100
tests/xfs/101
tests/xfs/102
tests/xfs/105
tests/xfs/106
tests/xfs/108
tests/xfs/109
tests/xfs/112
tests/xfs/113
tests/xfs/114
tests/xfs/118
tests/xfs/119
tests/xfs/122
tests/xfs/122.out
tests/xfs/128
tests/xfs/129
tests/xfs/129.out
tests/xfs/138
tests/xfs/144 [new file with mode: 0755]
tests/xfs/144.out [new file with mode: 0644]
tests/xfs/145
tests/xfs/146
tests/xfs/147
tests/xfs/151
tests/xfs/152
tests/xfs/154
tests/xfs/154.out
tests/xfs/155
tests/xfs/158
tests/xfs/158.out
tests/xfs/166
tests/xfs/167
tests/xfs/176
tests/xfs/177
tests/xfs/178
tests/xfs/178.out
tests/xfs/179
tests/xfs/180
tests/xfs/182
tests/xfs/182.out
tests/xfs/184
tests/xfs/185
tests/xfs/186
tests/xfs/187
tests/xfs/189
tests/xfs/191 [new file with mode: 0755]
tests/xfs/191.out [new file with mode: 0644]
tests/xfs/192
tests/xfs/193
tests/xfs/198
tests/xfs/200
tests/xfs/203
tests/xfs/204
tests/xfs/207
tests/xfs/208
tests/xfs/209
tests/xfs/210
tests/xfs/211
tests/xfs/212
tests/xfs/213
tests/xfs/214
tests/xfs/215
tests/xfs/218
tests/xfs/219
tests/xfs/221
tests/xfs/223
tests/xfs/224
tests/xfs/225
tests/xfs/226
tests/xfs/228
tests/xfs/229
tests/xfs/230
tests/xfs/231
tests/xfs/232
tests/xfs/234
tests/xfs/234.out
tests/xfs/237
tests/xfs/239
tests/xfs/240
tests/xfs/241
tests/xfs/243
tests/xfs/245
tests/xfs/248
tests/xfs/249
tests/xfs/251
tests/xfs/252
tests/xfs/253
tests/xfs/254
tests/xfs/255
tests/xfs/256
tests/xfs/257
tests/xfs/258
tests/xfs/262
tests/xfs/263
tests/xfs/264.out
tests/xfs/270
tests/xfs/270.out
tests/xfs/271
tests/xfs/272
tests/xfs/274
tests/xfs/279
tests/xfs/280
tests/xfs/284
tests/xfs/285
tests/xfs/285.out
tests/xfs/286
tests/xfs/286.out
tests/xfs/288
tests/xfs/291
tests/xfs/293
tests/xfs/294
tests/xfs/297
tests/xfs/305
tests/xfs/307
tests/xfs/308
tests/xfs/310
tests/xfs/312
tests/xfs/313
tests/xfs/315
tests/xfs/316
tests/xfs/318
tests/xfs/322
tests/xfs/324
tests/xfs/325
tests/xfs/326
tests/xfs/328
tests/xfs/329
tests/xfs/330
tests/xfs/331
tests/xfs/332
tests/xfs/335
tests/xfs/336
tests/xfs/337
tests/xfs/341
tests/xfs/342
tests/xfs/343
tests/xfs/344
tests/xfs/345
tests/xfs/346
tests/xfs/347
tests/xfs/348
tests/xfs/354
tests/xfs/355
tests/xfs/357
tests/xfs/358
tests/xfs/359
tests/xfs/360
tests/xfs/361
tests/xfs/362
tests/xfs/363
tests/xfs/364
tests/xfs/365
tests/xfs/366
tests/xfs/367
tests/xfs/368
tests/xfs/369
tests/xfs/370
tests/xfs/371
tests/xfs/372
tests/xfs/373
tests/xfs/410
tests/xfs/411
tests/xfs/422
tests/xfs/422.out
tests/xfs/423
tests/xfs/432
tests/xfs/432.out
tests/xfs/434
tests/xfs/435
tests/xfs/436
tests/xfs/438
tests/xfs/439
tests/xfs/440
tests/xfs/443
tests/xfs/444
tests/xfs/445
tests/xfs/450
tests/xfs/455
tests/xfs/457
tests/xfs/458
tests/xfs/459
tests/xfs/460
tests/xfs/461
tests/xfs/462
tests/xfs/463
tests/xfs/464
tests/xfs/483
tests/xfs/499
tests/xfs/503
tests/xfs/503.out
tests/xfs/505
tests/xfs/506
tests/xfs/507
tests/xfs/513
tests/xfs/514
tests/xfs/515
tests/xfs/516
tests/xfs/517
tests/xfs/517.out
tests/xfs/520
tests/xfs/528
tests/xfs/529
tests/xfs/530
tests/xfs/533 [new file with mode: 0755]
tests/xfs/533.out [new file with mode: 0644]
tests/xfs/534
tests/xfs/535
tests/xfs/537
tests/xfs/538
tests/xfs/538.out
tests/xfs/539
tests/xfs/542
tests/xfs/545
tests/xfs/547 [new file with mode: 0755]
tests/xfs/547.out [new file with mode: 0644]
tests/xfs/548 [new file with mode: 0755]
tests/xfs/548.out [new file with mode: 0644]
tests/xfs/549 [new file with mode: 0755]
tests/xfs/549.out [new file with mode: 0644]
tests/xfs/550 [new file with mode: 0755]
tests/xfs/550.out [new file with mode: 0644]
tests/xfs/551 [new file with mode: 0755]
tests/xfs/551.out [new file with mode: 0644]
tests/xfs/552 [new file with mode: 0755]
tests/xfs/552.out [new file with mode: 0644]
tests/xfs/553 [new file with mode: 0755]
tests/xfs/553.out [new file with mode: 0644]
tests/xfs/554 [new file with mode: 0755]
tests/xfs/554.out [new file with mode: 0644]
tests/xfs/555 [new file with mode: 0755]
tests/xfs/555.out [new file with mode: 0644]
tests/xfs/556 [new file with mode: 0755]
tests/xfs/556.out [new file with mode: 0644]
tests/xfs/557 [new file with mode: 0755]
tests/xfs/557.out [new file with mode: 0644]
tests/xfs/558 [new file with mode: 0755]
tests/xfs/558.out [new file with mode: 0644]
tests/xfs/559 [new file with mode: 0755]
tests/xfs/559.out [new file with mode: 0644]
tests/xfs/560 [new file with mode: 0755]
tests/xfs/560.out [new file with mode: 0644]
tests/xfs/561 [new file with mode: 0755]
tests/xfs/561.out [new file with mode: 0644]
tests/xfs/562 [new file with mode: 0755]
tests/xfs/562.out [new file with mode: 0644]
tests/xfs/563 [new file with mode: 0755]
tests/xfs/563.out [new file with mode: 0644]
tests/xfs/564 [new file with mode: 0755]
tests/xfs/564.out [new file with mode: 0644]
tests/xfs/565 [new file with mode: 0755]
tests/xfs/565.out [new file with mode: 0644]
tests/xfs/566 [new file with mode: 0755]
tests/xfs/566.out [new file with mode: 0644]
tests/xfs/567 [new file with mode: 0755]
tests/xfs/567.out [new file with mode: 0644]
tests/xfs/568 [new file with mode: 0755]
tests/xfs/568.out [new file with mode: 0644]
tests/xfs/569 [new file with mode: 0755]
tests/xfs/569.out [new file with mode: 0644]
tests/xfs/570 [new file with mode: 0755]
tests/xfs/570.out [new file with mode: 0644]
tests/xfs/571 [new file with mode: 0755]
tests/xfs/571.out [new file with mode: 0644]
tests/xfs/572 [new file with mode: 0755]
tests/xfs/572.out [new file with mode: 0644]
tests/xfs/573 [new file with mode: 0755]
tests/xfs/573.out [new file with mode: 0644]
tests/xfs/574 [new file with mode: 0755]
tests/xfs/574.out [new file with mode: 0644]
tests/xfs/575 [new file with mode: 0755]
tests/xfs/575.out [new file with mode: 0644]
tests/xfs/576 [new file with mode: 0755]
tests/xfs/576.out [new file with mode: 0644]
tests/xfs/577 [new file with mode: 0755]
tests/xfs/577.out [new file with mode: 0644]
tests/xfs/578 [new file with mode: 0755]
tests/xfs/578.out [new file with mode: 0644]
tests/xfs/579 [new file with mode: 0755]
tests/xfs/579.out [new file with mode: 0644]
tests/xfs/580 [new file with mode: 0755]
tests/xfs/580.out [new file with mode: 0644]
tests/xfs/581 [new file with mode: 0755]
tests/xfs/581.out [new file with mode: 0644]
tests/xfs/582 [new file with mode: 0755]
tests/xfs/582.out [new file with mode: 0644]
tests/xfs/583 [new file with mode: 0755]
tests/xfs/583.out [new file with mode: 0644]
tests/xfs/584 [new file with mode: 0755]
tests/xfs/584.out [new file with mode: 0644]
tests/xfs/585 [new file with mode: 0755]
tests/xfs/585.out [new file with mode: 0644]
tests/xfs/586 [new file with mode: 0755]
tests/xfs/586.out [new file with mode: 0644]
tests/xfs/587 [new file with mode: 0755]
tests/xfs/587.out [new file with mode: 0644]
tests/xfs/588 [new file with mode: 0755]
tests/xfs/588.out [new file with mode: 0644]
tests/xfs/589 [new file with mode: 0755]
tests/xfs/589.out [new file with mode: 0644]
tests/xfs/590 [new file with mode: 0755]
tests/xfs/590.out [new file with mode: 0644]
tests/xfs/591 [new file with mode: 0755]
tests/xfs/591.out [new file with mode: 0644]
tests/xfs/592 [new file with mode: 0755]
tests/xfs/592.out [new file with mode: 0644]
tests/xfs/593 [new file with mode: 0755]
tests/xfs/593.out [new file with mode: 0644]
tests/xfs/594 [new file with mode: 0755]
tests/xfs/594.out [new file with mode: 0644]
tests/xfs/595 [new file with mode: 0755]
tests/xfs/595.out [new file with mode: 0644]
tests/xfs/596 [new file with mode: 0755]
tests/xfs/596.out [new file with mode: 0644]
tests/xfs/597 [new file with mode: 0755]
tests/xfs/597.out [new file with mode: 0644]
tests/xfs/598 [new file with mode: 0755]
tests/xfs/598.out [new file with mode: 0644]
tests/xfs/599 [new file with mode: 0755]
tests/xfs/599.out [new file with mode: 0644]
tests/xfs/600 [new file with mode: 0755]
tests/xfs/600.out [new file with mode: 0644]
tests/xfs/601 [new file with mode: 0755]
tests/xfs/601.out [new file with mode: 0755]
tests/xfs/602 [new file with mode: 0755]
tests/xfs/602.out [new file with mode: 0644]
tests/xfs/603 [new file with mode: 0755]
tests/xfs/603.out [new file with mode: 0644]
tests/xfs/604 [new file with mode: 0755]
tests/xfs/604.out [new file with mode: 0644]
tests/xfs/605 [new file with mode: 0755]
tests/xfs/605.out [new file with mode: 0644]
tests/xfs/606 [new file with mode: 0755]
tests/xfs/606.out [new file with mode: 0644]
tests/xfs/607 [new file with mode: 0755]
tests/xfs/607.out [new file with mode: 0644]
tests/xfs/708 [new file with mode: 0755]
tests/xfs/708.out [new file with mode: 0644]
tests/xfs/709 [new file with mode: 0755]
tests/xfs/709.out [new file with mode: 0644]
tests/xfs/710 [new file with mode: 0755]
tests/xfs/710.out [new file with mode: 0644]
tests/xfs/711 [new file with mode: 0755]
tests/xfs/711.out [new file with mode: 0644]
tests/xfs/712 [new file with mode: 0755]
tests/xfs/712.out [new file with mode: 0644]
tests/xfs/713 [new file with mode: 0755]
tests/xfs/713.out [new file with mode: 0644]
tests/xfs/714 [new file with mode: 0755]
tests/xfs/714.out [new file with mode: 0644]
tests/xfs/715 [new file with mode: 0755]
tests/xfs/715.out [new file with mode: 0644]
tests/xfs/716 [new file with mode: 0755]
tests/xfs/716.out [new file with mode: 0644]
tests/xfs/717 [new file with mode: 0755]
tests/xfs/717.out [new file with mode: 0644]
tests/xfs/718 [new file with mode: 0755]
tests/xfs/718.out [new file with mode: 0644]
tests/xfs/719 [new file with mode: 0755]
tests/xfs/719.out [new file with mode: 0644]
tests/xfs/720 [new file with mode: 0755]
tests/xfs/720.out [new file with mode: 0644]
tests/xfs/721 [new file with mode: 0755]
tests/xfs/721.out [new file with mode: 0644]
tests/xfs/722 [new file with mode: 0755]
tests/xfs/722.out [new file with mode: 0644]
tests/xfs/723 [new file with mode: 0755]
tests/xfs/723.out [new file with mode: 0644]
tests/xfs/724 [new file with mode: 0755]
tests/xfs/724.out [new file with mode: 0644]
tests/xfs/725 [new file with mode: 0755]
tests/xfs/725.out [new file with mode: 0644]
tests/xfs/726 [new file with mode: 0755]
tests/xfs/726.out [new file with mode: 0644]
tests/xfs/727 [new file with mode: 0755]
tests/xfs/727.out [new file with mode: 0644]
tests/xfs/728 [new file with mode: 0755]
tests/xfs/728.out [new file with mode: 0644]
tests/xfs/729 [new file with mode: 0755]
tests/xfs/729.out [new file with mode: 0644]
tests/xfs/730 [new file with mode: 0755]
tests/xfs/730.out [new file with mode: 0644]
tests/xfs/731 [new file with mode: 0755]
tests/xfs/731.out [new file with mode: 0644]
tests/xfs/732 [new file with mode: 0755]
tests/xfs/732.out [new file with mode: 0644]
tests/xfs/733 [new file with mode: 0755]
tests/xfs/733.out [new file with mode: 0644]
tests/xfs/734 [new file with mode: 0755]
tests/xfs/734.out [new file with mode: 0644]
tests/xfs/735 [new file with mode: 0755]
tests/xfs/735.out [new file with mode: 0644]
tests/xfs/736 [new file with mode: 0755]
tests/xfs/736.out [new file with mode: 0644]
tests/xfs/737 [new file with mode: 0755]
tests/xfs/737.out [new file with mode: 0644]
tests/xfs/738 [new file with mode: 0755]
tests/xfs/738.out [new file with mode: 0644]
tests/xfs/739 [new file with mode: 0755]
tests/xfs/739.out [new file with mode: 0644]
tests/xfs/740 [new file with mode: 0755]
tests/xfs/740.out [new file with mode: 0644]
tests/xfs/741 [new file with mode: 0755]
tests/xfs/741.out [new file with mode: 0644]
tests/xfs/742 [new file with mode: 0755]
tests/xfs/742.out [new file with mode: 0644]
tests/xfs/743 [new file with mode: 0755]
tests/xfs/743.out [new file with mode: 0644]
tests/xfs/744 [new file with mode: 0755]
tests/xfs/744.out [new file with mode: 0644]
tests/xfs/745 [new file with mode: 0755]
tests/xfs/745.out [new file with mode: 0644]
tests/xfs/746 [new file with mode: 0755]
tests/xfs/746.out [new file with mode: 0644]
tests/xfs/747 [new file with mode: 0755]
tests/xfs/747.out [new file with mode: 0644]
tests/xfs/748 [new file with mode: 0755]
tests/xfs/748.out [new file with mode: 0644]
tests/xfs/749 [new file with mode: 0755]
tests/xfs/749.out [new file with mode: 0644]
tests/xfs/750 [new file with mode: 0755]
tests/xfs/750.out [new file with mode: 0644]
tests/xfs/751 [new file with mode: 0755]
tests/xfs/751.out [new file with mode: 0644]
tests/xfs/752 [new file with mode: 0755]
tests/xfs/752.out [new file with mode: 0644]
tests/xfs/753 [new file with mode: 0755]
tests/xfs/753.out [new file with mode: 0644]
tests/xfs/754 [new file with mode: 0755]
tests/xfs/754.out [new file with mode: 0644]
tests/xfs/755 [new file with mode: 0755]
tests/xfs/755.out [new file with mode: 0644]
tests/xfs/756 [new file with mode: 0755]
tests/xfs/756.out [new file with mode: 0644]
tests/xfs/757 [new file with mode: 0755]
tests/xfs/757.out [new file with mode: 0644]
tests/xfs/758 [new file with mode: 0755]
tests/xfs/758.out [new file with mode: 0644]
tests/xfs/759 [new file with mode: 0755]
tests/xfs/759.out [new file with mode: 0644]
tests/xfs/760 [new file with mode: 0755]
tests/xfs/760.out [new file with mode: 0644]
tests/xfs/761 [new file with mode: 0755]
tests/xfs/761.out [new file with mode: 0644]
tests/xfs/762 [new file with mode: 0755]
tests/xfs/762.out [new file with mode: 0644]
tests/xfs/763 [new file with mode: 0755]
tests/xfs/763.out [new file with mode: 0644]
tests/xfs/764 [new file with mode: 0755]
tests/xfs/764.out [new file with mode: 0644]
tests/xfs/765 [new file with mode: 0755]
tests/xfs/765.out [new file with mode: 0644]
tests/xfs/766 [new file with mode: 0755]
tests/xfs/766.out [new file with mode: 0644]
tests/xfs/767 [new file with mode: 0755]
tests/xfs/767.out [new file with mode: 0644]
tests/xfs/768 [new file with mode: 0755]
tests/xfs/768.out [new file with mode: 0644]
tests/xfs/769 [new file with mode: 0755]
tests/xfs/769.out [new file with mode: 0644]
tests/xfs/770 [new file with mode: 0755]
tests/xfs/770.out [new file with mode: 0644]
tests/xfs/771 [new file with mode: 0755]
tests/xfs/771.out [new file with mode: 0644]
tests/xfs/772 [new file with mode: 0755]
tests/xfs/772.out [new file with mode: 0644]
tests/xfs/773 [new file with mode: 0755]
tests/xfs/773.out [new file with mode: 0644]
tests/xfs/774 [new file with mode: 0755]
tests/xfs/774.out [new file with mode: 0644]
tests/xfs/775 [new file with mode: 0755]
tests/xfs/775.out [new file with mode: 0644]
tests/xfs/776 [new file with mode: 0755]
tests/xfs/776.out [new file with mode: 0644]
tests/xfs/777 [new file with mode: 0755]
tests/xfs/777.out [new file with mode: 0644]
tests/xfs/778 [new file with mode: 0755]
tests/xfs/778.out [new file with mode: 0644]
tests/xfs/779 [new file with mode: 0755]
tests/xfs/779.out [new file with mode: 0644]
tests/xfs/780 [new file with mode: 0755]
tests/xfs/780.out [new file with mode: 0644]
tests/xfs/781 [new file with mode: 0755]
tests/xfs/781.out [new file with mode: 0644]
tests/xfs/782 [new file with mode: 0755]
tests/xfs/782.out [new file with mode: 0644]
tests/xfs/783 [new file with mode: 0755]
tests/xfs/783.out [new file with mode: 0644]
tests/xfs/784 [new file with mode: 0755]
tests/xfs/784.out [new file with mode: 0644]
tests/xfs/785 [new file with mode: 0755]
tests/xfs/785.out [new file with mode: 0644]
tests/xfs/786 [new file with mode: 0755]
tests/xfs/786.out [new file with mode: 0644]
tests/xfs/787 [new file with mode: 0755]
tests/xfs/787.out [new file with mode: 0755]
tests/xfs/788 [new file with mode: 0755]
tests/xfs/788.out [new file with mode: 0644]
tests/xfs/789 [new file with mode: 0755]
tests/xfs/789.out [new file with mode: 0644]
tests/xfs/790 [new file with mode: 0755]
tests/xfs/790.out [new file with mode: 0644]
tests/xfs/791 [new file with mode: 0755]
tests/xfs/791.out [new file with mode: 0644]
tests/xfs/792 [new file with mode: 0755]
tests/xfs/792.out [new file with mode: 0644]
tests/xfs/793 [new file with mode: 0755]
tests/xfs/793.out [new file with mode: 0644]
tests/xfs/794 [new file with mode: 0755]
tests/xfs/794.out [new file with mode: 0644]
tests/xfs/795 [new file with mode: 0755]
tests/xfs/795.out [new file with mode: 0644]
tests/xfs/796 [new file with mode: 0755]
tests/xfs/796.out [new file with mode: 0644]
tests/xfs/797 [new file with mode: 0755]
tests/xfs/797.out [new file with mode: 0644]
tests/xfs/798 [new file with mode: 0755]
tests/xfs/798.out [new file with mode: 0644]
tests/xfs/799 [new file with mode: 0755]
tests/xfs/799.out [new file with mode: 0644]
tests/xfs/800 [new file with mode: 0755]
tests/xfs/800.out [new file with mode: 0644]
tests/xfs/Makefile
tools/get_maintainer.pl [new file with mode: 0755]
tools/mkgroupfile
tools/mvtest

index 88c7941249572e3c0a5aea10387bcbae8b1df20c..18815b35fd7e1ee9170fb6ec19602d49c844d544 100644 (file)
@@ -72,6 +72,7 @@ tags
 /src/deduperace
 /src/detached_mounts_propagation
 /src/devzero
+/src/dio-buf-fault
 /src/dio-interleaved
 /src/dio-invalidate-cache
 /src/dirhash_collide
@@ -79,6 +80,7 @@ tags
 /src/dirstress
 /src/e4compact
 /src/ext4_resize
+/src/fake-dump-rootino
 /src/fault
 /src/feature
 /src/fiemap-tester
@@ -119,9 +121,11 @@ tags
 /src/punch-alternating
 /src/pwrite_mmap_blocked
 /src/randholes
+/src/readdir-while-renames
 /src/rename
 /src/renameat2
 /src/resvtest
+/src/rewinddir-test
 /src/runas
 /src/seek_copy_test
 /src/seek_sanity_test
@@ -147,6 +151,7 @@ tags
 /src/t_holes
 /src/t_immutable
 /src/t_mmap_collision
+/src/t_mmap_cow_memory_failure
 /src/t_mmap_cow_race
 /src/t_mmap_dio
 /src/t_mmap_fallocate
@@ -160,7 +165,9 @@ tags
 /src/t_readdir_1
 /src/t_readdir_2
 /src/t_readdir_3
+/src/t_reflink_read_race
 /src/t_rename_overwrite
+/src/t_snapshot_deleted_subvolume
 /src/t_stripealign
 /src/t_truncate_cmtime
 /src/t_truncate_self
@@ -172,9 +179,11 @@ tags
 /src/unwritten_sync
 /src/uring_read_fault
 /src/usemem
+/src/uuid_ioctl
 /src/writemod
 /src/writev_on_pagefault
 /src/xfsctl
+/src/xfsfind
 /src/aio-dio-regress/aio-dio-append-write-fallocate-race
 /src/aio-dio-regress/aio-dio-append-write-read-race
 /src/aio-dio-regress/aio-dio-cow-race
@@ -196,6 +205,7 @@ tags
 /src/vfs/mount-idmapped
 /src/log-writes/replay-log
 /src/perf/*.pyc
+/src/fiemap-fault
 
 # Symlinked files
 /tests/generic/035.out
@@ -203,6 +213,7 @@ tags
 /tests/xfs/033.out
 /tests/xfs/071.out
 /tests/xfs/096.out
+/tests/xfs/216.out
 
 # cscope files
 cscope.*
diff --git a/MAINTAINERS b/MAINTAINERS
new file mode 100644 (file)
index 0000000..bc31572
--- /dev/null
@@ -0,0 +1,200 @@
+List of reviewers, co-maintainers and how to submit fstests changes
+====================================================
+
+Please try to follow the guidelines below.  This will make things
+easier on the maintainers.  Not all of these guidelines matter for every
+trivial patch so apply some common sense.
+
+Tips for patch submitters
+-------------------------
+
+1.     Always *test* your changes, however small, on at least 4 or
+       5 people, preferably many more.
+
+2.     Make sure your changes compile correctly in multiple
+       configurations. In particular check that changes don't break
+       fstests basic running.
+
+3.     When you are happy with a change make it generally available for
+       testing and await feedback.
+
+4.     Make a patch available to fstests@ list directly, that's the only
+       one mailing list which maintain the whole fstests project.
+
+       PLEASE CC: the relevant reviewers, co-maintainers and mailing lists
+       that are generated by ``tools/get_maintainer.pl.``
+
+       PLEASE try to include any credit lines you want added with the
+       patch. It avoids people being missed off by mistake and makes
+       it easier to know who wants adding and who doesn't.
+
+       PLEASE document known bugs. If it doesn't work for everything
+       or does something very odd once a month document it.
+
+5.     Make sure you have the right to send any changes you make. If you
+       do changes at work you may find your employer owns the patch
+       not you.
+
+6.     Happy hacking.
+
+Descriptions of section entries and preferred order
+---------------------------------------------------
+
+       M: *Mail* patches to: FullName <address@domain>
+          These people might be a co-maintainer (with Supported status) or
+          maintainer (with Maintained status).
+       R: Designated *Reviewer*: FullName <address@domain>
+          These reviewers should be CCed on patches.
+       L: Besides fstests@ list itself, this *Mailing list* is relevant to
+          this area, should be CCed.
+       S: *Status*, one of the following (note: all things are maintained by
+          fstests@vger.kernel.org):
+          Supported:   Someone is actually paid to look after this.
+          Maintained:  Someone actually looks after it, has the privilege to
+                       merge & push.
+          Odd Fixes:   It has a maintainer but they don't have time to do
+                       much other than throw the odd patch in. See below..
+          Orphan:      No current maintainer [but maybe you could take the
+                       role as you write your new code].
+          Obsolete:    Old code. Something tagged obsolete generally means
+                       it has been replaced by a better system and you
+                       should be using that.
+       W: *Web-page* with status/info
+       Q: *Patchwork* web based patch tracking system site
+       B: URI for where to file *bugs*. A web-page with detailed bug
+          filing info, a direct bug tracker link, or a mailto: URI.
+       C: URI for *chat* protocol, server and channel where developers
+          usually hang out, for example irc://server/channel.
+       P: Subsystem Profile document for more details submitting
+          patches to the given subsystem. This is either an in-tree file,
+          or a URI.
+       T: *SCM* tree type and location.
+          Type is one of: git, hg, quilt, stgit, topgit
+       F: *Files* and directories wildcard patterns.
+          A trailing slash includes all files and subdirectory files.
+          F:   tests/xfs/      all files in and below tests/xfs
+          F:   tests/generic/* all files in tests/generic, but not below
+          F:   */ext4/*        all files in "any top level directory"/ext4
+          One pattern per line.  Multiple F: lines acceptable.
+       X: *Excluded* files and directories that are NOT maintained, same
+          rules as F:. Files exclusions are tested before file matches.
+          Can be useful for excluding a specific subdirectory, for instance:
+          F:   src/
+          X:   src/vfs
+          matches all files in and below net excluding net/ipv6/
+       N: Files and directories *Regex* patterns.
+          N:   [^a-z]tegra     all files whose path contains tegra
+                               (not including files like integrator)
+          One pattern per line.  Multiple N: lines acceptable.
+          tools/get_maintainer.pl has different behavior for files that
+          match F: pattern and matches of N: patterns.  By default,
+          get_maintainer will not look at git log history when an F: pattern
+          match occurs.  When an N: match occurs, git log history is used
+          to also notify the people that have git commit signatures.
+       K: *Content regex* (perl extended) pattern match in a patch or file.
+          For instance:
+          K: of_get_profile
+             matches patches or files that contain "of_get_profile"
+          K: \b(printk|pr_(info|err))\b
+             matches patches or files that contain one or more of the words
+             printk, pr_info or pr_err
+          One regex pattern per line.  Multiple K: lines acceptable.
+
+Maintainers List
+----------------
+
+.. note:: The whole fstests are maintained by fstests@vger.kernel.org, so you
+         should send patch to fstests@ at least. Other relevant mailing list
+         or reviewer or co-maintainer can be in cc list.
+
+BTRFS
+M:     Anand Jain <anand.jain@oracle.com>
+R:     Filipe Manana <fdmanana@suse.com>
+L:     linux-btrfs@vger.kernel.org
+S:     Supported
+F:     tests/btrfs/
+F:     common/btrfs
+
+CEPH
+L:     ceph-devel@vger.kernel.org
+S:     Supported
+F:     tests/ceph/
+F:     common/ceph
+
+CIFS
+L:     linux-cifs@vger.kernel.org
+S:     Supported
+F:     tests/cifs
+
+EXT4
+L:     linux-ext4@vger.kernel.org
+S:     Supported
+F:     tests/ext4/
+F:     common/ext4
+
+F2FS
+L:     linux-f2fs-devel@lists.sourceforge.net
+S:     Supported
+F:     tests/f2fs/
+F:     common/f2fs
+
+FSVERITY
+R:     Eric Biggers <ebiggers@google.com>
+L:     fsverity@lists.linux.dev
+S:     Supported
+F:     common/verity
+
+FSCRYPT
+R:     Eric Biggers <ebiggers@google.com>
+L:      linux-fscrypt@vger.kernel.org
+S:     Supported
+F:     common/encrypt
+
+NFS
+L:     linux-nfs@vger.kernel.org
+S:     Supported
+F:     tests/nfs/
+F:     common/nfs
+
+OCFS2
+L:     ocfs2-devel@oss.oracle.com
+S:     Supported
+F:     tests/ocfs2/
+
+OVERLAYFS
+R:     Amir Goldstein <amir73il@gmail.com>
+L:     linux-unionfs@vger.kernel.org
+S:     Supported
+F:     tests/overlay
+F:     common/overlay
+
+UDF
+R:     Jan Kara <jack@suse.com>
+S:     Supported
+F:     tests/udf/
+
+VFS
+R:     Christian Brauner <brauner@kernel.org>
+L:     linux-fsdevel@vger.kernel.org
+S:     Supported
+F:     src/vfs/
+
+XFS
+R:     Darrick J. Wong <djwong@kernel.org>
+L:     linux-xfs@vger.kernel.org
+S:     Supported
+F:     common/dump
+F:     common/fuzzy
+F:     common/inject
+F:     common/populate
+F:     common/repair
+F:     common/xfs
+F:     tests/xfs/
+
+ALL
+M:     Zorro Lang <zlang@kernel.org>
+L:     fstests@vger.kernel.org
+S:     Maintained
+T:     git git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git
+F:     *
+F:     */
diff --git a/README b/README
index 7da66cb66a109fd8ebd1d0ea5edb633240e03438..477136deb0d01a2fb5993d20699c776e0103a228 100644 (file)
--- a/README
+++ b/README
@@ -11,16 +11,14 @@ Ubuntu or Debian
    $ sudo apt-get install acl attr automake bc dbench dump e2fsprogs fio gawk \
         gcc git indent libacl1-dev libaio-dev libcap-dev libgdbm-dev libtool \
         libtool-bin liburing-dev libuuid1 lvm2 make psmisc python3 quota sed \
-        uuid-dev uuid-runtime xfsprogs linux-headers-$(uname -r) sqlite3
+        uuid-dev uuid-runtime xfsprogs linux-headers-$(uname -r) sqlite3 \
+        libgdbm-compat-dev
 
 2. Install packages for the filesystem(s) being tested:
 
    $ sudo apt-get install exfatprogs f2fs-tools ocfs2-tools udftools xfsdump \
         xfslibs-dev
 
-   For OverlayFS install:
-    - see https://github.com/hisilicon/overlayfs-progs
-
 Fedora
 ------
 
@@ -36,9 +34,6 @@ Fedora
     $ sudo yum install btrfs-progs exfatprogs f2fs-tools ocfs2-tools xfsdump \
         xfsprogs-devel
 
-   For OverlayFS build and install:
-    - see https://github.com/hisilicon/overlayfs-progs
-
 RHEL or CentOS
 --------------
 
@@ -74,8 +69,22 @@ RHEL or CentOS
     For ocfs2 build and install:
      - see https://github.com/markfasheh/ocfs2-tools
 
-    For OverlayFS build and install:
-     - see https://github.com/hisilicon/overlayfs-progs
+SUSE Linux Enterprise or openSUSE
+---------------------------------
+
+1. Install all necessary packages from standard repositories:
+
+   $ sudo zypper install acct automake bc dbench duperemove dump fio gcc git \
+        indent libacl-devel libaio-devel libattr-devel libcap libcap-devel \
+        libtool liburing-devel libuuid-devel lvm2 make quota sqlite3 xfsprogs
+
+2. Install packages for the filesystem(s) being tested:
+
+    For btrfs install:
+     $ sudo zypper install btrfsprogs libbtrfs-devel
+
+    For XFS install:
+     $ sudo zypper install xfsdump xfsprogs-devel
 
 Build and install test, libs and utils
 --------------------------------------
@@ -133,6 +142,12 @@ Setup Environment
    https://www.lscdweb.com/registered/udf_verifier.html, then copy the udf_test
    binary to xfstests/src/.
 
+8. (optional) To do io_uring related testing, please make sure below 3 things:
+     1) kernel is built with CONFIG_IO_URING=y
+     2) sysctl -w kernel.io_uring_disabled=0 (or set it to 2 to disable io_uring
+        testing dynamically if kernel supports)
+     3) install liburing development package contains liburing.h before building
+        fstests
 
 For example, to run the tests with loopback partitions:
 
@@ -191,6 +206,9 @@ Extra XFS specification:
    to check the filesystem. As of August 2021, xfs_repair finds all
    filesystem corruptions found by xfs_check, and more, which means that
    xfs_check is no longer run by default.
+ - Set TEST_XFS_SCRUB_REBUILD=1 to have _check_xfs_filesystem run xfs_scrub in
+   "force_repair" mode to rebuild the filesystem; and xfs_repair -n to check
+   the results of the rebuilding.
  - xfs_scrub, if present, will always check the test and scratch
    filesystems if they are still online at the end of the test. It is no
    longer necessary to set TEST_XFS_SCRUB.
@@ -212,6 +230,10 @@ Tools specification:
     - Set FSSTRESS_AVOID and/or FSX_AVOID, which contain options added to
       the end of fsstresss and fsx invocations, respectively, in case you wish
       to exclude certain operational modes from these tests.
+ - core dumps:
+    - Set COREDUMP_COMPRESSOR to a compression program to compress crash dumps.
+      This program must accept '-f' and the name of a file to compress.  In
+      other words, it must emulate gzip.
 
 Kernel/Modules related configuration:
  - Set TEST_FS_MODULE_RELOAD=1 to unload the module and reload it between
@@ -222,6 +244,20 @@ Kernel/Modules related configuration:
    to "forever" and we'll wait forever until the module is gone.
  - Set KCONFIG_PATH to specify your preferred location of kernel config
    file. The config is used by tests to check if kernel feature is enabled.
+ - Set REPORT_GCOV to a directory path to make lcov and genhtml generate
+   html reports from any gcov code coverage data collected by the kernel.
+   If REPORT_GCOV is set to 1, the report will be written to $REPORT_DIR/gcov/.
+
+Test control:
+ - Set LOAD_FACTOR to a nonzero positive integer to increase the amount of
+   load applied to the system during a test by the specified multiple.
+ - Set TIME_FACTOR to a nonzero positive integer to increase the amount of
+   time that a test runs by the specified multiple.
+ - For tests that are a member of the "soak" group, setting SOAK_DURATION
+   allows the test runner to specify exactly how long the test should continue
+   running.  This setting overrides TIME_FACTOR.  Floating point numbers are
+   allowed, and the unit suffixes m(inutes), h(ours), d(ays), and w(eeks) are
+   supported.
 
 Misc:
  - If you wish to disable UDF verification test set the environment variable
@@ -241,6 +277,12 @@ Misc:
    this option is supported for all filesystems currently only -overlay is
    expected to run without issues. For other filesystems additional patches
    and fixes to the test suite might be needed.
+ - Set REPORT_VARS_FILE to a file containing colon-separated name-value pairs
+   that will be recorded in the test section report.  Names must be unique.
+   Whitespace surrounding the colon will be removed.
+ - set CANON_DEVS=yes to canonicalize device symlinks. This will let you
+   for example use something like TEST_DEV/dev/disk/by-id/nvme-* so the
+   device remains persistent between reboots. This is disabled by default.
 
 ______________________
 USING THE FSQA SUITE
@@ -261,9 +303,10 @@ Running tests:
     - If you want to run all tests regardless of what group they are in
       (including dangerous tests), use the "all" group: ./check -g all
     - To randomize test order: ./check -r [test(s)]
-    - You can explicitly specify NFS/CIFS/OVERLAY, otherwise
+    - You can explicitly specify NFS/AFS/CIFS/OVERLAY, otherwise
       the filesystem type will be autodetected from $TEST_DEV:
         - for running nfs tests: ./check -nfs [test(s)]
+        - for running afs tests: ./check -afs [test(s)]
         - for running cifs/smb3 tests: ./check -cifs [test(s)]
         - for overlay tests: ./check -overlay [test(s)]
           The TEST and SCRATCH partitions should be pre-formatted
@@ -368,19 +411,41 @@ Test script environment:
 
      6. Test group membership: Each test can be associated with any number
        of groups for convenient selection of subsets of tests.  Group names
-       can be any sequence of non-whitespace characters.  Test authors
-       associate a test with groups by passing the names of those groups as
-       arguments to the _begin_fstest function.  For example, the code:
+       must  be human readable using only characters in the set [:alnum:_-].
+
+       Test authors associate a test with groups by passing the names of those
+       groups as arguments to the _begin_fstest function. While _begin_fstests
+       is a shell function that must be called at the start of a test to
+       initialise the test environment correctly, the the build infrastructure
+       also scans the test files for _begin_fstests invocations. It does this
+       to compile the group lists that are used to determine which tests to run
+       when `check` is executed. In other words, test files files must call
+       _begin_fstest with their intended groups or they will not be run.
+
+       However, because the build infrastructure also uses _begin_fstests as
+       a defined keyword, addition restrictions are placed on how it must be
+       formatted:
+
+       (a) It must be a single line with no multi-line continuations.
 
-       _begin_fstest auto quick subvol snapshot
+       (b) group names should be separated by spaces and not other whitespace
+
+       (c) A '#' placed anywhere in the list, even in the middle of a group
+           name, will cause everything from the # to the end of the line to be
+           ignored.
+
+       For example, the code:
+
+       _begin_fstest auto quick subvol snapshot # metadata
 
        associates the current test with the "auto", "quick", "subvol", and
-       "snapshot" groups.  It is not necessary to specify the "all" group
-       in the list because that group is computed at run time.
+       "snapshot" groups. Because "metadata" is after the "#" comment
+       delimiter, it is ignored by the build infrastructure and so it will not
+       be associated with that group.
+
+       It is not necessary to specify the "all" group in the list because that
+       group is always computed at run time from the group lists.
 
-       The build process scans test files for _begin_fstest invocations and
-       compiles the group list from that information.  In other words, test
-       files must call _begin_fstest or they will not be run.
 
 Verified output:
 
index 4f1a4dc6df7a3ae9c24545f971d3104d6f84302a..a42d9d7bd0880b4e1d374e9c2d4baea1d2f7c155 100644 (file)
@@ -128,3 +128,10 @@ TEST_DIR=/mnt/gluster/test
 TEST_DEV=192.168.1.1:testvol
 SCRATCH_MNT=/mnt/gluster/scratch
 SCRATCH_DEV=192.168.1.1:scratchvol
+
+[afs]
+FSTYP=afs
+TEST_DEV=%example.com:xfstest.test
+TEST_DIR=/mnt/xfstest.test
+SCRATCH_DEV=%example.com:xfstest.scratch
+SCRATCH_MNT=/mnt/xfstest.scratch
diff --git a/README.fuse b/README.fuse
new file mode 100644 (file)
index 0000000..969dbd5
--- /dev/null
@@ -0,0 +1,26 @@
+Here are instructions for testing fuse using the passthrough_ll example
+filesystem provided in the libfuse source tree:
+
+git clone git://github.com/libfuse/libfuse.git
+cd libfuse
+meson build
+cd build
+ninja
+cat << EOF | sudo tee /sbin/mount.fuse.passthrough_ll
+#!/bin/bash
+ulimit -n 1048576
+exec $(pwd)/example/passthrough_ll -ofsname="\$@"
+EOF
+sudo chmod +x /sbin/mount.fuse.passthrough_ll
+mkdir -p /mnt/test /mnt/scratch /home/test/test /home/test/scratch
+
+Use the following local.config file:
+
+export TEST_DEV=non1
+export TEST_DIR=/mnt/test
+export SCRATCH_DEV=non2
+export SCRATCH_MNT=/mnt/scratch
+export FSTYP=fuse
+export FUSE_SUBTYP=.passthrough_ll
+export MOUNT_OPTIONS="-osource=/home/test/scratch,allow_other,default_permissions"
+export TEST_FS_MOUNT_OPTS="-osource=/home/test/test,allow_other,default_permissions"
index ec4671c39698e48640132023183bccf426e213f5..3093bf8c2bf7150ff1b0b7434858b4cac242d672 100644 (file)
@@ -1,4 +1,3 @@
-
 To run xfstest on overlayfs, configure the variables of TEST and SCRATCH
 partitions to be used as the "base fs" and run './check -overlay'.
 
@@ -69,3 +68,11 @@ UNIONMOUNT_TESTSUITE to the local path where the repository was cloned.
 
 Run './check -overlay -g overlay/union' to execute all the unionmount testsuite
 test cases.
+
+
+Overlayfs Tools
+===============
+
+A few tests require additional tools. For fsck.overlay [optional],
+build and install:
+  https://github.com/kmxz/overlayfs-tools
diff --git a/README.selftest b/README.selftest
new file mode 100644 (file)
index 0000000..ee77931
--- /dev/null
@@ -0,0 +1,29 @@
+Tests with consistent results are provided in the selftest folder.
+Since many people develop testing infrastructure around xfstests,
+these tests are helpful to confirm the testing setup is working as
+expected.
+
+The provided tests include:
+selftest/001 - pass
+selftest/002 - fail from output mismatch
+selftest/003 - fail via _fail
+selftest/004 - skip
+selftest/005 - crash
+selftest/006 - hang
+
+Two groups are used for these tests: selftest and dangerous_selftest.
+selftest/00[1-4] are in the selftest group and selftest/00[5-6] are
+in the dangerous_selftest group.
+
+The selftest will only be run if explicitly speficied. To run the
+selftest, you can specify individual tests, e.g.
+
+# ./check selftest/001
+
+or use the groups under selftest/, e.g.
+
+# ./check -g selftest/selftest
+# ./check -g selftest/dangerous_selftest
+
+Note, you cannot use the group names without including the folder name
+(ie. "-g selftest").
index fd92f0d53e9533adecfcb46909e51c9e33b57a33..7632e5cc882e9bb82af7aa8d67af165e32f075d8 100644 (file)
@@ -7,11 +7,6 @@ AC_DEFUN([AC_PACKAGE_WANT_LINUX_FIEMAP_H],
     AC_SUBST(have_fiemap)
   ])
 
-AC_DEFUN([AC_PACKAGE_WANT_LINUX_PRCTL_H],
-  [ AC_CHECK_HEADERS([sys/prctl.h], [ have_prctl=true ], [ have_prctl=false ])
-    AC_SUBST(have_prctl)
-  ])
-
 AC_DEFUN([AC_PACKAGE_WANT_LINUX_FS_H],
   [ AC_CHECK_HEADER([linux/fs.h])
   ])
index e0f7c5f92dedfd3809dd54d787de33a4ec7759be..3dce41efd97234fa60d6f03b6402010277383311 100644 (file)
@@ -17,7 +17,7 @@ Group: System Environment/Base
 
 %description
 The XFS regression test suite.  Also includes some support for
-acl, attr, udf, and nfs testing.  Contains around 200 specific tests
+acl, attr, udf, nfs and afs testing.  Contains around 200 specific tests
 for userspace & kernelspace.
 
 %prep
diff --git a/check b/check
index de11b37e1346806d80158521b28a1752aeb63092..c6dba89b5b506efc1b0cc13bbdf79c019292ff7f 100755 (executable)
--- a/check
+++ b/check
@@ -8,13 +8,10 @@ tmp=/tmp/$$
 status=0
 needwrap=true
 needsum=true
-n_try=0
-try=""
-n_bad=0
+try=()
 sum_bad=0
-bad=""
-n_notrun=0
-notrun=""
+bad=()
+notrun=()
 interrupt=true
 diff="diff -u"
 showme=false
@@ -29,6 +26,8 @@ do_report=false
 DUMP_OUTPUT=false
 iterations=1
 istop=false
+loop_on_fail=0
+exclude_tests=()
 
 # This is a global variable used to pass test failure text to reporting gunk
 _err_msg=""
@@ -36,6 +35,9 @@ _err_msg=""
 # start the initialisation work now
 iam=check
 
+# mkfs.xfs uses the presence of both of these variables to enable formerly
+# supported tiny filesystem configurations that fstests use for fuzz testing
+# in a controlled environment
 export MSGVERB="text:action"
 export QA_CHECK_FS=${QA_CHECK_FS:=true}
 
@@ -45,7 +47,7 @@ export DIFF_LENGTH=${DIFF_LENGTH:=10}
 # by default don't output timestamps
 timestamp=${TIMESTAMP:=false}
 
-rm -f $tmp.list $tmp.tmp $tmp.grep $here/$iam.out $tmp.xlist $tmp.report.*
+rm -f $tmp.list $tmp.tmp $tmp.grep $here/$iam.out $tmp.report.* $tmp.arglist
 
 SRC_GROUPS="generic shared"
 export SRC_DIR="tests"
@@ -56,9 +58,11 @@ usage()
 
 check options
     -nfs               test NFS
+    -afs               test AFS
     -glusterfs         test GlusterFS
     -cifs              test CIFS
     -9p                        test 9p
+    -fuse              test fuse
     -virtiofs          test virtiofs
     -overlay           test overlay
     -pvfs2             test PVFS2
@@ -74,10 +78,11 @@ check options
     -I <n>             iterate the test list <n> times, but stops iterating further in case of any test failure
     -d                 dump test output to stdout
     -b                 brief test summary
-    -R fmt[,fmt]       generate report in formats specified. Supported format: [xunit]
+    -R fmt[,fmt]       generate report in formats specified. Supported formats: xunit, xunit-quiet
     --large-fs         optimise scratch device for large filesystems
     -s section         run only specified section from config file
     -S section         exclude the specified section from the config file
+    -L <n>             loop tests <n> times following a failure, measuring aggregate pass/fail metrics
 
 testlist options
     -g group[,group...]        include tests from these groups
@@ -179,10 +184,10 @@ get_all_tests()
 # the function from that list.
 trim_test_list()
 {
-       test_list="$*"
+       local test_list="$*"
 
        rm -f $tmp.grep
-       numsed=0
+       local numsed=0
        for t in $test_list
        do
            if [ $numsed -gt 100 ]; then
@@ -199,15 +204,9 @@ trim_test_list()
        rm -f $tmp.grep
 }
 
-
-_wallclock()
-{
-    date "+%s"
-}
-
 _timestamp()
 {
-    now=`date "+%T"`
+    local now=`date "+%T"`
     echo -n " [$now]"
 }
 
@@ -277,15 +276,14 @@ while [ $# -gt 0 ]; do
        case "$1" in
        -\? | -h | --help) usage ;;
 
-       -nfs)           FSTYP=nfs ;;
-       -glusterfs)     FSTYP=glusterfs ;;
-       -cifs)          FSTYP=cifs ;;
-       -9p)            FSTYP=9p ;;
-       -virtiofs)      FSTYP=virtiofs ;;
-       -overlay)       FSTYP=overlay; export OVERLAY=true ;;
-       -pvfs2)         FSTYP=pvfs2 ;;
-       -tmpfs)         FSTYP=tmpfs ;;
-       -ubifs)         FSTYP=ubifs ;;
+       -nfs|-afs|-glusterfs|-cifs|-9p|-fuse|-virtiofs|-pvfs2|-tmpfs|-ubifs)
+               FSTYP="${1:1}"
+               ;;
+       -overlay)
+               [ "$FSTYP" == overlay ] || export OVL_BASE_FSTYP="$FSTYP"
+               FSTYP=overlay
+               export OVERLAY=true
+               ;;
 
        -g)     group=$2 ; shift ;
                GROUP_LIST="$GROUP_LIST ${group//,/ }"
@@ -299,13 +297,15 @@ while [ $# -gt 0 ]; do
                ;;
        -e)
                xfile=$2; shift ;
-               echo "$xfile" | tr ', ' '\n\n' >> $tmp.xlist
+               readarray -t -O "${#exclude_tests[@]}" exclude_tests < \
+                       <(echo "$xfile" | tr ', ' '\n\n')
                ;;
 
        -E)     xfile=$2; shift ;
                if [ -f $xfile ]; then
-                       sed "s/#.*$//" "$xfile" >> $tmp.xlist
-               fi
+                       readarray -t -O ${#exclude_tests[@]} exclude_tests < \
+                               <(sed "s/#.*$//" $xfile)
+               fi
                ;;
        -s)     RUN_SECTION="$RUN_SECTION $2"; shift ;;
        -S)     EXCLUDE_SECTION="$EXCLUDE_SECTION $2"; shift ;;
@@ -338,6 +338,9 @@ while [ $# -gt 0 ]; do
                ;;
        --large-fs) export LARGE_SCRATCH_DEV=yes ;;
        --extra-space=*) export SCRATCH_DEV_EMPTY_SPACE=${r#*=} ;;
+       -L)     [[ $2 =~ ^[0-9]+$ ]] || usage
+               loop_on_fail=$2; shift
+               ;;
 
        -*)     usage ;;
        *)      # not an argument, we've got tests now.
@@ -361,11 +364,23 @@ if ! . ./common/rc; then
        exit 1
 fi
 
+# If the test config specified a soak test duration, see if there are any
+# unit suffixes that need converting to an integer seconds count.
+if [ -n "$SOAK_DURATION" ]; then
+       SOAK_DURATION="$(echo "$SOAK_DURATION" | \
+               sed -e 's/^\([.0-9]*\)\([a-z]\)*/\1 \2/g' | \
+               $AWK_PROG -f $here/src/soak_duration.awk)"
+       if [ $? -ne 0 ]; then
+               status=1
+               exit 1
+       fi
+fi
+
 if [ -n "$subdir_xfile" ]; then
        for d in $SRC_GROUPS $FSTYP; do
                [ -f $SRC_DIR/$d/$subdir_xfile ] || continue
                for f in `sed "s/#.*$//" $SRC_DIR/$d/$subdir_xfile`; do
-                       echo $d/$f >> $tmp.xlist
+                       exclude_tests+=($d/$f)
                done
        done
 fi
@@ -381,12 +396,12 @@ if $have_test_arg; then
                *)      # Expand test pattern (e.g. xfs/???, *fs/001)
                        list=$(cd $SRC_DIR; echo $1)
                        for t in $list; do
-                               test_dir=`dirname $t`
-                               test_dir=${test_dir#$SRC_DIR/*}
-                               test_name=`basename $t`
+                               t=${t#$SRC_DIR/}
+                               test_dir=${t%%/*}
+                               test_name=${t##*/}
                                group_file=$SRC_DIR/$test_dir/group.list
 
-                               if egrep -q "^$test_name" $group_file; then
+                               if grep -Eq "^$test_name" $group_file; then
                                        # in group file ... OK
                                        echo $SRC_DIR/$test_dir/$test_name \
                                                >>$tmp.arglist
@@ -414,10 +429,9 @@ fi
 
 _wipe_counters()
 {
-       n_try="0"
-       n_bad="0"
-       n_notrun="0"
-       unset try notrun bad
+       try=()
+       notrun=()
+       bad=()
 }
 
 _global_log() {
@@ -427,18 +441,25 @@ _global_log() {
        fi
 }
 
+if [ -n "$REPORT_GCOV" ]; then
+       . ./common/gcov
+       _gcov_check_report_gcov
+fi
+
 _wrapup()
 {
        seq="check"
        check="$RESULT_BASE/check"
+       $interrupt && sect_stop=`_wallclock`
 
-       if $showme; then
-               if $needwrap; then
-                       if $do_report; then
-                               _make_section_report
-                       fi
-                       needwrap=false
+       if $showme && $needwrap; then
+               if $do_report; then
+                       # $showme = all selected tests are notrun (no tries)
+                       _make_section_report "$section" "${#notrun[*]}" "0" \
+                                            "${#notrun[*]}" \
+                                            "$((sect_stop - sect_start))"
                fi
+               needwrap=false
        elif $needwrap; then
                if [ -f $check.time -a -f $tmp.time ]; then
                        cat $check.time $tmp.time  \
@@ -461,12 +482,12 @@ _wrapup()
 
                echo "SECTION       -- $section" >>$tmp.summary
                echo "=========================" >>$tmp.summary
-               if [ ! -z "$n_try" -a $n_try != 0 ]; then
+               if ((${#try[*]} > 0)); then
                        if [ $brief_test_summary == "false" ]; then
-                               echo "Ran:$try"
-                               echo "Ran:$try" >>$tmp.summary
+                               echo "Ran: ${try[*]}"
+                               echo "Ran: ${try[*]}" >>$tmp.summary
                        fi
-                       _global_log "Ran:$try"
+                       _global_log "Ran: ${try[*]}"
                fi
 
                $interrupt && echo "Interrupted!" | tee -a $check.log
@@ -475,34 +496,48 @@ _wrapup()
                                ${REPORT_DIR}/check.log
                fi
 
-               if [ ! -z "$notrun" ]; then
+               if ((${#notrun[*]} > 0)); then
                        if [ $brief_test_summary == "false" ]; then
-                               echo "Not run:$notrun"
-                               echo "Not run:$notrun" >>$tmp.summary
+                               echo "Not run: ${notrun[*]}"
+                               echo "Not run: ${notrun[*]}" >>$tmp.summary
                        fi
-                       _global_log "Not run:$notrun"
+                       _global_log "Not run: ${notrun[*]}"
                fi
 
-               if [ ! -z "$n_bad" -a $n_bad != 0 ]; then
-                       echo "Failures:$bad"
-                       echo "Failed $n_bad of $n_try tests"
-                       _global_log "Failures:$bad"
-                       _global_log "Failed $n_bad of $n_try tests"
-                       echo "Failures:$bad" >>$tmp.summary
-                       echo "Failed $n_bad of $n_try tests" >>$tmp.summary
+               if ((${#bad[*]} > 0)); then
+                       echo "Failures: ${bad[*]}"
+                       echo "Failed ${#bad[*]} of ${#try[*]} tests"
+                       _global_log "Failures: ${bad[*]}"
+                       _global_log "Failed ${#bad[*]} of ${#try[*]} tests"
+                       echo "Failures: ${bad[*]}" >>$tmp.summary
+                       echo "Failed ${#bad[*]} of ${#try[*]} tests" >>$tmp.summary
                else
-                       echo "Passed all $n_try tests"
-                       _global_log "Passed all $n_try tests"
-                       echo "Passed all $n_try tests" >>$tmp.summary
+                       echo "Passed all ${#try[*]} tests"
+                       _global_log "Passed all ${#try[*]} tests"
+                       echo "Passed all ${#try[*]} tests" >>$tmp.summary
                fi
                echo "" >>$tmp.summary
                if $do_report; then
-                       _make_section_report
+                       _make_section_report "$section" "${#try[*]}" \
+                                            "${#bad[*]}" "${#notrun[*]}" \
+                                            "$((sect_stop - sect_start))"
+               fi
+
+               # Generate code coverage report
+               if [ -n "$REPORT_GCOV" ]; then
+                       # don't trigger multiple times if caller hits ^C
+                       local gcov_report_dir="$REPORT_GCOV"
+                       test "$gcov_report_dir" = "1" && \
+                               gcov_report_dir="$REPORT_DIR/gcov"
+                       unset REPORT_GCOV
+
+                       _gcov_generate_report "$gcov_report_dir"
                fi
+
                needwrap=false
        fi
 
-       sum_bad=`expr $sum_bad + $n_bad`
+       sum_bad=`expr $sum_bad + ${#bad[*]}`
        _wipe_counters
        rm -f /tmp/*.rawout /tmp/*.out /tmp/*.err /tmp/*.time
        if ! $OPTIONS_HAVE_SECTIONS; then
@@ -528,7 +563,12 @@ _check_filesystems()
        local ret=0
 
        if [ -f ${RESULT_DIR}/require_test ]; then
-               _check_test_fs || ret=1
+               if ! _check_test_fs ; then
+                       ret=1
+                       echo "Trying to repair broken TEST_DEV file system"
+                       _repair_test_fs
+                       _test_mount
+               fi
                rm -f ${RESULT_DIR}/require_test*
        else
                _test_unmount 2> /dev/null
@@ -544,13 +584,82 @@ _check_filesystems()
 _expunge_test()
 {
        local TEST_ID="$1"
-       if [ -s $tmp.xlist ]; then
-               if grep -q $TEST_ID $tmp.xlist; then
+
+       for f in "${exclude_tests[@]}"; do
+               # $f may contain traling spaces and comments
+               local id_regex="^${TEST_ID}\b"
+               if [[ "$f" =~ ${id_regex} ]]; then
                        echo "       [expunged]"
-                       return 1
+                       return 0
+               fi
+       done
+       return 1
+}
+
+# retain files which would be overwritten in subsequent reruns of the same test
+_stash_fail_loop_files() {
+       local seq_prefix="${REPORT_DIR}/${1}"
+       local cp_suffix="$2"
+
+       for i in ".full" ".dmesg" ".out.bad" ".notrun" ".core" ".hints"; do
+               rm -f "${seq_prefix}${i}${cp_suffix}"
+               if [ -f "${seq_prefix}${i}" ]; then
+                       cp "${seq_prefix}${i}" "${seq_prefix}${i}${cp_suffix}"
+               fi
+       done
+}
+
+# Retain in @bad / @notrun the result of the just-run @test_seq. @try array
+# entries are added prior to execution.
+_stash_test_status() {
+       local test_seq="$1"
+       local test_status="$2"
+
+       if $do_report && [[ $test_status != "expunge" ]]; then
+               _make_testcase_report "$section" "$test_seq" \
+                                     "$test_status" "$((stop - start))"
+       fi
+
+       if ((${#loop_status[*]} > 0)); then
+               # continuing or completing rerun-on-failure loop
+               _stash_fail_loop_files "$test_seq" ".rerun${#loop_status[*]}"
+               loop_status+=("$test_status")
+               if ((${#loop_status[*]} > loop_on_fail)); then
+                       printf "%s aggregate results across %d runs: " \
+                               "$test_seq" "${#loop_status[*]}"
+                       awk "BEGIN {
+                               n=split(\"${loop_status[*]}\", arr);"'
+                               for (i = 1; i <= n; i++)
+                                       stats[arr[i]]++;
+                               for (x in stats)
+                                       printf("%s=%d (%.1f%%)",
+                                              (i-- > n ? x : ", " x),
+                                              stats[x], 100 * stats[x] / n);
+                               }'
+                       echo
+                       loop_status=()
                fi
+               return  # only stash @bad result for initial failure in loop
        fi
-       return 0
+
+       case "$test_status" in
+       fail)
+               if ((loop_on_fail > 0)); then
+                       # initial failure, start rerun-on-failure loop
+                       _stash_fail_loop_files "$test_seq" ".rerun0"
+                       loop_status+=("$test_status")
+               fi
+               bad+=("$test_seq")
+               ;;
+       list|notrun)
+               notrun+=("$test_seq")
+               ;;
+       pass|expunge)
+               ;;
+       *)
+               echo "Unexpected test $test_seq status: $test_status"
+               ;;
+       esac
 }
 
 # Can we run systemd scopes?
@@ -594,6 +703,7 @@ _run_seq() {
 
 _detect_kmemleak
 _prepare_test_list
+fstests_start_time="$(date +"%F %T")"
 
 if $OPTIONS_HAVE_SECTIONS; then
        trap "_summary; exit \$status" 0 1 2 3 15
@@ -603,11 +713,10 @@ fi
 
 function run_section()
 {
-       local section=$1
+       local section=$1 skip
 
        OLD_FSTYP=$FSTYP
        OLD_TEST_FS_MOUNT_OPTS=$TEST_FS_MOUNT_OPTS
-       get_next_config $section
 
        # Do we need to run only some sections ?
        if [ ! -z "$RUN_SECTION" ]; then
@@ -637,6 +746,9 @@ function run_section()
                fi
        fi
 
+       get_next_config $section
+       _canonicalize_devices
+
        mkdir -p $RESULT_BASE
        if [ ! -d $RESULT_BASE ]; then
                echo "failed to create results directory $RESULT_BASE"
@@ -699,6 +811,7 @@ function run_section()
          echo "MOUNT_OPTIONS -- `_scratch_mount_options`"
        fi
        echo
+       test -n "$REPORT_GCOV" && _gcov_reset
        needwrap=true
 
        if [ ! -z "$SCRATCH_DEV" ]; then
@@ -732,25 +845,12 @@ function run_section()
        seqres="$check"
        _check_test_fs
 
-       err=false
-       first_test=true
-       prev_seq=""
-       for seq in $list ; do
-               # Run report for previous test!
-               if $err ; then
-                       bad="$bad $seqnum"
-                       n_bad=`expr $n_bad + 1`
-                       tc_status="fail"
-               fi
-               if $do_report && ! $first_test ; then
-                       if [ $tc_status != "expunge" ] ; then
-                               _make_testcase_report "$prev_seq" "$tc_status"
-                       fi
-               fi
-               first_test=false
+       loop_status=()  # track rerun-on-failure state
+       local tc_status ix
+       local -a _list=( $list )
+       for ((ix = 0; ix < ${#_list[*]}; !${#loop_status[*]} && ix++)); do
+               seq="${_list[$ix]}"
 
-               err=false
-               prev_seq="$seq"
                if [ ! -f $seq ]; then
                        # Try to get full name in case the user supplied only
                        # seq id and the test has a name. A bit of hassle to
@@ -768,70 +868,72 @@ function run_section()
 
                # the filename for the test and the name output are different.
                # we don't include the tests/ directory in the name output.
-               export seqnum=`echo $seq | sed -e "s;$SRC_DIR/;;"`
-
-               # Similarly, the result directory needs to replace the tests/
-               # part of the test location.
-               group=`dirname $seq`
+               export seqnum=${seq#$SRC_DIR/}
+               group=${seqnum%%/*}
                if $OPTIONS_HAVE_SECTIONS; then
-                       export RESULT_DIR=`echo $group | sed -e "s;$SRC_DIR;${RESULT_BASE}/$section;"`
                        REPORT_DIR="$RESULT_BASE/$section"
                else
-                       export RESULT_DIR=`echo $group | sed -e "s;$SRC_DIR;$RESULT_BASE;"`
                        REPORT_DIR="$RESULT_BASE"
                fi
+               export RESULT_DIR="$REPORT_DIR/$group"
                seqres="$REPORT_DIR/$seqnum"
 
-               mkdir -p $RESULT_DIR
-               rm -f ${RESULT_DIR}/require_scratch*
-               rm -f ${RESULT_DIR}/require_test*
+               # Generate the entire section report with whatever test results
+               # we have so far.  Leave the $sect_time parameter empty so that
+               # it's a little more obvious that this test run is incomplete.
+               if $do_report; then
+                       _make_section_report "$section" "${#try[*]}" \
+                                            "${#bad[*]}" "${#notrun[*]}" \
+                                            "" &> /dev/null
+               fi
+
                echo -n "$seqnum"
 
                if $showme; then
-                       _expunge_test $seqnum
-                       if [ $? -eq 1 ]; then
-                           tc_status="expunge"
-                           continue
+                       if _expunge_test $seqnum; then
+                               tc_status="expunge"
+                       else
+                               echo
+                               start=0
+                               stop=0
+                               tc_status="list"
                        fi
-                       echo
-                       start=0
-                       stop=0
-                       tc_status="list"
-                       n_notrun=`expr $n_notrun + 1`
+                       _stash_test_status "$seqnum" "$tc_status"
                        continue
                fi
 
                tc_status="pass"
                if [ ! -f $seq ]; then
                        echo " - no such test?"
+                       _stash_test_status "$seqnum" "$tc_status"
                        continue
                fi
 
                # really going to try and run this one
+               mkdir -p $RESULT_DIR
+               rm -f ${RESULT_DIR}/require_scratch*
+               rm -f ${RESULT_DIR}/require_test*
                rm -f $seqres.out.bad $seqres.hints
 
                # check if we really should run it
-               _expunge_test $seqnum
-               if [ $? -eq 1 ]; then
+               if _expunge_test $seqnum; then
                        tc_status="expunge"
+                       _stash_test_status "$seqnum" "$tc_status"
                        continue
                fi
 
                # record that we really tried to run this test.
-               try="$try $seqnum"
-               n_try=`expr $n_try + 1`
-
-               # slashes now in names, sed barfs on them so use grep
-               lasttime=`grep -w ^$seqnum $check.time | awk '// {print $2}'`
-               if [ "X$lasttime" != X ]; then
-                       echo -n " ${lasttime}s ... "
-               else
-                       echo -n "       " # prettier output with timestamps.
+               if ((!${#loop_status[*]})); then
+                       try+=("$seqnum")
                fi
+
+               awk 'BEGIN {lasttime="       "} \
+                    $1 == "'$seqnum'" {lasttime=" " $2 "s ... "; exit} \
+                    END {printf "%s", lasttime}' "$check.time"
                rm -f core $seqres.notrun
 
                start=`_wallclock`
-               $timestamp && echo -n " ["`date "+%T"`"]"
+               $timestamp && _timestamp
                [ ! -x $seq ] && chmod u+x $seq # ensure we can run it
                $LOGGER_PROG "run xfstest $seqnum"
                if [ -w /dev/kmsg ]; then
@@ -839,6 +941,7 @@ function run_section()
                        echo "run fstests $seqnum at $date_time" > /dev/kmsg
                        # _check_dmesg depends on this log in dmesg
                        touch ${RESULT_DIR}/check_dmesg
+                       rm -f ${RESULT_DIR}/dmesg_filter
                fi
                _try_wipe_scratch_devs > /dev/null 2>&1
 
@@ -846,6 +949,7 @@ function run_section()
                # to be reported for each test
                (echo 1 > $DEBUGFS_MNT/clear_warn_once) > /dev/null 2>&1
 
+               test_start_time="$(date +"%F %T")"
                if [ "$DUMP_OUTPUT" = true ]; then
                        _run_seq 2>&1 | tee $tmp.out
                        # Because $? would get tee's return code
@@ -855,11 +959,19 @@ function run_section()
                        sts=$?
                fi
 
-               if [ -f core ]; then
-                       _dump_err_cont "[dumped core]"
-                       mv core $RESULT_BASE/$seqnum.core
-                       err=true
-               fi
+               # If someone sets kernel.core_pattern or kernel.core_uses_pid,
+               # coredumps generated by fstests might have a longer name than
+               # just "core".  Use globbing to find the most common patterns,
+               # assuming there are no other coredump capture packages set up.
+               local cores=0
+               for i in core core.*; do
+                       test -f "$i" || continue
+                       if ((cores++ == 0)); then
+                               _dump_err_cont "[dumped core]"
+                       fi
+                       (_adjust_oom_score 250; _save_coredump "$i")
+                       tc_status="fail"
+               done
 
                if [ -f $seqres.notrun ]; then
                        $timestamp && _timestamp
@@ -868,9 +980,8 @@ function run_section()
                        $timestamp && echo " [not run]" && \
                                      echo -n " $seqnum -- "
                        cat $seqres.notrun
-                       notrun="$notrun $seqnum"
-                       n_notrun=`expr $n_notrun + 1`
                        tc_status="notrun"
+                       _stash_test_status "$seqnum" "$tc_status"
 
                        # Unmount the scratch fs so that we can wipe the scratch
                        # dev state prior to the next test run.
@@ -884,15 +995,29 @@ function run_section()
                        _scratch_unmount 2> /dev/null
                        rm -f ${RESULT_DIR}/require_test*
                        rm -f ${RESULT_DIR}/require_scratch*
-                       err=true
+                       # Even though we failed, there may be something interesting in
+                       # dmesg which can help debugging.
+                       _check_dmesg
+                       (_adjust_oom_score 250; _check_filesystems)
+                       tc_status="fail"
                else
                        # The test apparently passed, so check for corruption
                        # and log messages that shouldn't be there.  Run the
                        # checking tools from a subshell with adjusted OOM
                        # score so that the OOM killer will target them instead
                        # of the check script itself.
-                       (_adjust_oom_score 250; _check_filesystems) || err=true
-                       _check_dmesg || err=true
+                       (_adjust_oom_score 250; _check_filesystems) || tc_status="fail"
+                       _check_dmesg || tc_status="fail"
+
+                       # Save any coredumps from the post-test fs checks
+                       for i in core core.*; do
+                               test -f "$i" || continue
+                               if ((cores++ == 0)); then
+                                       _dump_err_cont "[dumped core]"
+                               fi
+                               (_adjust_oom_score 250; _save_coredump "$i")
+                               tc_status="fail"
+                       done
                fi
 
                # Reload the module after each test to check for leaks or
@@ -906,7 +1031,7 @@ function run_section()
 
                # Scan for memory leaks after every test so that associating
                # a leak to a particular test will be as accurate as possible.
-               _check_kmemleak || err=true
+               _check_kmemleak || tc_status="fail"
 
                # test ends after all checks are done.
                $timestamp && _timestamp
@@ -914,7 +1039,8 @@ function run_section()
 
                if [ ! -f $seq.out ]; then
                        _dump_err "no qualified output"
-                       err=true
+                       tc_status="fail"
+                       _stash_test_status "$seqnum" "$tc_status"
                        continue;
                fi
 
@@ -923,7 +1049,7 @@ function run_section()
                # version.
                sed -i "s/\`/\'/g" $tmp.out
                if diff $seq.out $tmp.out >/dev/null 2>&1 ; then
-                       if ! $err ; then
+                       if [ "$tc_status" != "fail" ]; then
                                echo "$seqnum `expr $stop - $start`" >>$tmp.time
                                echo -n " `expr $stop - $start`s"
                        fi
@@ -940,30 +1066,19 @@ function run_section()
                                echo "(Run '$diff $here/$seq.out $seqres.out.bad'" \
                                        " to see the entire diff)"
                        fi; } | sed -e 's/^\(.\)/    \1/'
-                       err=true
+                       tc_status="fail"
                fi
                if [ -f $seqres.hints ]; then
-                       if $err; then
+                       if [ "$tc_status" == "fail" ]; then
                                echo
                                cat $seqres.hints
                        else
                                rm -f $seqres.hints
                        fi
                fi
+               _stash_test_status "$seqnum" "$tc_status"
        done
 
-       # make sure we record the status of the last test we ran.
-       if $err ; then
-               bad="$bad $seqnum"
-               n_bad=`expr $n_bad + 1`
-               tc_status="fail"
-       fi
-       if $do_report && ! $first_test ; then
-               if [ $tc_status != "expunge" ] ; then
-                       _make_testcase_report "$prev_seq" "$tc_status"
-               fi
-       fi
-
        sect_stop=`_wallclock`
        interrupt=false
        _wrapup
index cce4d1b201b2790a8cf2250b234ddfe16634c5e4..3ebba682c894f6f85f12189f675c61c5ec24e45f 100644 (file)
@@ -163,13 +163,12 @@ _require_acls()
     [ -n "$CHACL_PROG" ] || _notrun "chacl command not found"
 
     #
-    # Test if chacl is able to list ACLs on the target filesystems.  On really
-    # old kernels the system calls might not be implemented at all, but the
-    # more common case is that the tested filesystem simply doesn't support
-    # ACLs.
+    # Test if chacl is able to set an ACL on a file.  On really old kernels
+    # the system calls might not be implemented at all, but the more common
+    # case is that the tested filesystem simply doesn't support ACLs.
     #
     touch $TEST_DIR/syscalltest
-    chacl -l $TEST_DIR/syscalltest > $TEST_DIR/syscalltest.out 2>&1
+    chacl 'u::rw-,g::---,o::---' $TEST_DIR/syscalltest > $TEST_DIR/syscalltest.out 2>&1
     cat $TEST_DIR/syscalltest.out >> $seqres.full
 
     if grep -q 'Function not implemented' $TEST_DIR/syscalltest.out; then
index ac597ca465a1c3d39240b7e330284c42b7471c47..99bd5ace674fbee94769322b3d23576d49e0ad7c 100644 (file)
@@ -6,10 +6,10 @@
 
 _btrfs_get_subvolid()
 {
-       mnt=$1
-       name=$2
+       local mnt=$1
+       local name=$2
 
-       $BTRFS_UTIL_PROG sub list $mnt | egrep "\s$name$" | $AWK_PROG '{ print $2 }'
+       $BTRFS_UTIL_PROG subvolume list $mnt | grep -E "\s$name$" | $AWK_PROG '{ print $2 }'
 }
 
 # _require_btrfs_command <command> [<subcommand>|<option>]
@@ -88,6 +88,17 @@ _require_btrfs_mkfs_feature()
                _notrun "Feature $feat not supported in the available version of mkfs.btrfs"
 }
 
+_require_btrfs_mkfs_uuid_option()
+{
+       local cnt
+
+       cnt=$($MKFS_BTRFS_PROG --help 2>&1 | \
+                               grep -E --count -- "--uuid|--device-uuid")
+       if [ $cnt != 2 ]; then
+               _notrun "Require $MKFS_BTRFS_PROG with --uuid and --device-uuid options"
+       fi
+}
+
 _require_btrfs_fs_feature()
 {
        if [ -z $1 ]; then
@@ -120,6 +131,33 @@ _require_btrfs_no_compress()
        fi
 }
 
+_require_btrfs_no_nodatacow()
+{
+       if _normalize_mount_options "$MOUNT_OPTIONS" | grep -q "nodatacow"; then
+               _notrun "This test requires no nodatacow enabled"
+       fi
+}
+
+_require_btrfs_free_space_tree()
+{
+       _scratch_mkfs > /dev/null 2>&1
+       if ! $BTRFS_UTIL_PROG inspect-internal dump-super $SCRATCH_DEV | \
+               grep -q "FREE_SPACE_TREE"
+       then
+               _notrun "This test requires a free-space-tree"
+       fi
+}
+
+_require_btrfs_no_block_group_tree()
+{
+       _scratch_mkfs > /dev/null 2>&1
+       if $BTRFS_UTIL_PROG inspect-internal dump-super $SCRATCH_DEV | \
+               grep -q "BLOCK_GROUP_TREE"
+       then
+               _notrun "This test requires no block-group-tree"
+       fi
+}
+
 _check_btrfs_filesystem()
 {
        device=$1
@@ -183,7 +221,7 @@ _check_btrfs_filesystem()
 
 _require_btrfs_dev_del_by_devid()
 {
-       $BTRFS_UTIL_PROG device delete --help | egrep devid > /dev/null 2>&1
+       $BTRFS_UTIL_PROG device delete --help | grep -E devid > /dev/null 2>&1
        [ $? -eq 0 ] || _notrun "$BTRFS_UTIL_PROG too old "\
                        "(must support 'btrfs device delete <devid> /<mnt>')"
 }
@@ -201,6 +239,39 @@ _btrfs_get_profile_configs()
                return
        fi
 
+       local unsupported=()
+       if [ "$1" == "replace" ]; then
+               # We can't do replace with these profiles because they
+               # imply only one device ($SCRATCH_DEV), and we need to
+               # keep $SCRATCH_DEV around for _scratch_mount
+               # and _check_scratch_fs.
+               unsupported+=(
+                       "dup"
+               )
+       elif [ "$1" == "replace-missing" ]; then
+               # We can't replace missing devices with these profiles
+               # because there isn't enough redundancy.
+               unsupported+=(
+                       "single"
+                       "dup"
+                       "raid0"
+               )
+       fi
+
+       if _scratch_btrfs_is_zoned; then
+               # Zoned btrfs only supports SINGLE profile
+               unsupported+=(
+                       "dup"
+                       "raid0"
+                       "raid1"
+                       "raid1c3"
+                       "raid1c4"
+                       "raid10"
+                       "raid5"
+                       "raid6"
+               )
+       fi
+
        if [ -z "$BTRFS_PROFILE_CONFIGS" ]; then
                # Default configurations to test.
                local configs=(
@@ -213,6 +284,9 @@ _btrfs_get_profile_configs()
                        "raid5:raid5"
                        "raid6:raid6"
                )
+               if [ "$1" == "dup" ]; then
+                       configs+=("dup:dup")
+               fi
        else
                # User-provided configurations.
                local configs=(${BTRFS_PROFILE_CONFIGS[@]})
@@ -222,39 +296,6 @@ _btrfs_get_profile_configs()
        for cfg in "${configs[@]}"; do
                local supported=true
                local profiles=(${cfg/:/ })
-               if [ "$1" == "replace" ]; then
-                       # We can't do replace with these profiles because they
-                       # imply only one device ($SCRATCH_DEV), and we need to
-                       # keep $SCRATCH_DEV around for _scratch_mount
-                       # and _check_scratch_fs.
-                       local unsupported=(
-                               "dup"
-                       )
-               elif [ "$1" == "replace-missing" ]; then
-                       # We can't replace missing devices with these profiles
-                       # because there isn't enough redundancy.
-                       local unsupported=(
-                               "single"
-                               "dup"
-                               "raid0"
-                       )
-               else
-                       local unsupported=()
-               fi
-
-               if _scratch_btrfs_is_zoned; then
-                       # Zoned btrfs only supports SINGLE profile
-                       unsupported+=(
-                               "dup"
-                               "raid0"
-                               "raid1"
-                               "raid1c3"
-                               "raid1c4"
-                               "raid10"
-                               "raid5"
-                               "raid6"
-                       )
-               fi
 
                for unsupp in "${unsupported[@]}"; do
                        if [ "${profiles[0]}" == "$unsupp" -o "${profiles[1]}" == "$unsupp" ]; then
@@ -454,13 +495,23 @@ _scratch_btrfs_is_zoned()
        return 1
 }
 
-_require_btrfs_sysfs_fsid()
+_btrfs_get_fsid()
 {
        local fsid
+       local mnt=$1
 
-       fsid=$($BTRFS_UTIL_PROG filesystem show $TEST_DIR |grep uuid: |\
+       fsid=$($BTRFS_UTIL_PROG filesystem show $mnt |grep uuid: |\
               $AWK_PROG '{print $NF}')
 
+       echo $fsid
+}
+
+_require_btrfs_sysfs_fsid()
+{
+       local fsid
+
+       fsid=$(_btrfs_get_fsid $TEST_DIR)
+
        # Check if the kernel has sysfs fsid support.
        # Following kernel patch adds it:
        #   btrfs: sysfs add devinfo/fsid to retrieve fsid from the device
@@ -486,7 +537,7 @@ _require_btrfs_support_sectorsize()
        local sectorsize=$1
 
        # PAGE_SIZE as sectorsize is always supported
-       if [ $sectorsize -eq $(get_page_size) ]; then
+       if [ $sectorsize -eq $(_get_page_size) ]; then
                return
        fi
 
@@ -496,6 +547,28 @@ _require_btrfs_support_sectorsize()
                _notrun "sectorsize $sectorsize is not supported"
 }
 
+_require_btrfs_inline_extents_creation()
+{
+       local ino
+
+       _require_xfs_io_command fiemap
+       _require_scratch
+
+       _scratch_mkfs &> /dev/null
+       _scratch_mount -o max_inline=2048,compress=none
+       _pwrite_byte 0x00 0 1024 $SCRATCH_MNT/inline &> /dev/null
+       sync
+       $XFS_IO_PROG -c "fiemap -v" $SCRATCH_MNT/inline | tail -n 1 > $tmp.fiemap
+       _scratch_unmount
+       # 0x200 means inlined, 0x100 means not block aligned, 0x1 means
+       # the last extent.
+       if ! grep -q "0x301" $tmp.fiemap; then
+               rm -f -- $tmp.fiemap
+               _notrun "No inline extent creation support, maybe subpage?"
+       fi
+       rm -f -- $tmp.fiemap
+}
+
 _btrfs_metadump()
 {
        local device="$1"
@@ -505,3 +578,303 @@ _btrfs_metadump()
        $BTRFS_IMAGE_PROG "$device" "$dumpfile"
        [ -n "$DUMP_COMPRESSOR" ] && $DUMP_COMPRESSOR -f "$dumpfile" &> /dev/null
 }
+
+# Return the btrfs logical address for the first block in a file
+_btrfs_get_first_logical()
+{
+       local file=$1
+       _require_command "$FILEFRAG_PROG" filefrag
+
+       ${FILEFRAG_PROG} -v $file >> $seqres.full
+       ${FILEFRAG_PROG} -v $file | _filter_filefrag | cut -d '#' -f 1
+}
+
+# Find the device path for a btrfs logical offset
+_btrfs_get_device_path()
+{
+       local logical=$1
+       local stripe=$2
+
+       _require_command "$BTRFS_MAP_LOGICAL_PROG" btrfs-map-logical
+
+       $BTRFS_MAP_LOGICAL_PROG -l $logical $SCRATCH_DEV | \
+               $AWK_PROG "(\$1 ~ /mirror/ && \$2 ~ /$stripe/) { print \$8 }"
+}
+
+
+# Find the device physical sector for a btrfs logical offset
+_btrfs_get_physical()
+{
+       local logical=$1
+       local stripe=$2
+
+       _require_command "$BTRFS_MAP_LOGICAL_PROG" btrfs-map-logical
+
+       $BTRFS_MAP_LOGICAL_PROG -l $logical $SCRATCH_DEV >> $seqres.full 2>&1
+       $BTRFS_MAP_LOGICAL_PROG -l $logical $SCRATCH_DEV | \
+               $AWK_PROG "(\$1 ~ /mirror/ && \$2 ~ /$stripe/) { print \$6 }"
+}
+
+# Read from a specific stripe to test read recovery that corrupted a specific
+# stripe.  Btrfs uses the PID to select the mirror, so keep reading until the
+# xfs_io process that performed the read was executed with a PID that ends up
+# on the intended mirror.
+_btrfs_direct_read_on_mirror()
+{
+       local mirror=$1
+       local nr_mirrors=$2
+       local file=$3
+       local offset=$4
+       local size=$5
+
+       while [[ -z $( (( BASHPID % nr_mirrors == mirror )) &&
+               exec $XFS_IO_PROG -d \
+                       -c "pread -b $size $offset $size" $file) ]]; do
+               :
+       done
+}
+
+# Read from a specific stripe to test read recovery that corrupted a specific
+# stripe.  Btrfs uses the PID to select the mirror, so keep reading until the
+# xfs_io process that performed the read was executed with a PID that ends up
+# on the intended mirror.
+_btrfs_buffered_read_on_mirror()
+{
+       local mirror=$1
+       local nr_mirrors=$2
+       local file=$3
+       local offset=$4
+       local size=$5
+
+       # The drop_caches doesn't seem to drop every pages on aarch64 with
+       # 64K page size.
+       # So here as another workaround, cycle mount the SCRATCH_MNT to ensure
+       # the cache are dropped, but we can not use _scratch_cycle_mount, as
+       # we may mount whatever dm device at SCRATCH_MNT.
+       # So here we grab the mounted block device and its mount options, then
+       # unmount and re-mount with the same device and options.
+       local dev=$(findmnt -n -T $SCRATCH_MNT -o SOURCE)
+       local opts=$(findmnt -n -T $SCRATCH_MNT -o OPTIONS)
+       if [ -z "$dev" -o -z "$opts" ]; then
+               _fail "failed to grab mount info of $SCRATCH_MNT"
+       fi
+       _scratch_unmount
+       _mount $dev -o $opts $SCRATCH_MNT
+       while [[ -z $( (( BASHPID % nr_mirrors == mirror )) &&
+               exec $XFS_IO_PROG \
+                       -c "pread -b $size $offset $size" $file) ]]; do
+               :
+       done
+}
+
+_require_btrfs_corrupt_block()
+{
+       _require_command "$BTRFS_CORRUPT_BLOCK_PROG" btrfs-corrupt-block
+}
+
+_require_btrfs_send_version()
+{
+       local version=$1
+
+       # Check first if btrfs-progs supports the v2 stream.
+       _require_btrfs_command send --compressed-data
+
+       # Now check the kernel support. If send_stream_version does not exists,
+       # then it's a kernel that only supports v1.
+       [ -f /sys/fs/btrfs/features/send_stream_version ] || \
+               _notrun "kernel does not support send stream $version"
+
+       [ $(cat /sys/fs/btrfs/features/send_stream_version) -ge $version ] || \
+               _notrun "kernel does not support send stream $version"
+}
+
+# Get the bytenr associated to a file extent item at a given file offset.
+#
+# NOTE: At the moment this only works if the file is on a filesystem on top of
+#       the scratch device and the file is in the default subvolume (tree id 5).
+_btrfs_get_file_extent_item_bytenr()
+{
+       local file="$1"
+       local offset="$2"
+       local ino=$(stat -c "%i" "$file")
+       local file_extent_key="($ino EXTENT_DATA $offset)"
+
+       _require_btrfs_command inspect-internal dump-tree
+
+       # The tree dump command below works on committed roots, by reading from
+       # a device directly, so we have to sync the filesystem to commit any
+       # open transaction.
+       $BTRFS_UTIL_PROG filesystem sync $SCRATCH_MNT
+
+       $BTRFS_UTIL_PROG inspect-internal dump-tree -t 5 $SCRATCH_DEV | \
+               grep -A4 "$file_extent_key" | grep "disk byte" | \
+               $AWK_PROG '{ print $5 }'
+}
+
+# Check that btrfs-progs has support for the logical-resolve command, with the
+# -o option, and that the kernel supports the logical to ino ioctl v2 (which
+# adds the ignore offset parameter).
+_require_btrfs_scratch_logical_resolve_v2()
+{
+       local bytenr
+
+       # First check if we have support for calling the v2 logical resolve
+       # ioctl in btrfs-progs. Check if the -o options exists, which makes
+       # btrfs-progs call v2 of the ioctl (because the flag
+       # BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET is only supported in v2).
+       _require_btrfs_command inspect-internal logical-resolve -o
+       _require_scratch
+
+       _scratch_mkfs > /dev/null || \
+               _fail "_require_btrfs_scratch_logical_resolve_v2: mkfs failed"
+       _scratch_mount
+
+       $XFS_IO_PROG -f -c "pwrite -q 0 128K" "$SCRATCH_MNT/file1"
+       bytenr=$(_btrfs_get_file_extent_item_bytenr "$SCRATCH_MNT/file1" 0)
+       $BTRFS_UTIL_PROG inspect-internal logical-resolve -o $bytenr \
+                        $SCRATCH_MNT > /dev/null
+       if [ $? -ne 0 ]; then
+               _scratch_unmount
+               _notrun "Logical resolve ioctl v2 not supported in the kernel"
+       fi
+       _scratch_unmount
+}
+
+_qgroup_mode()
+{
+       local dev=$1
+
+       if [ ! -b "$dev" ]; then
+               _fail "Usage: _qgroup_mode <mounted_device>"
+       fi
+
+       if _has_fs_sysfs_attr $dev /qgroups/mode; then
+               _get_fs_sysfs_attr $dev qgroups/mode
+       else
+               echo "qgroup"
+       fi
+}
+
+_check_regular_qgroup()
+{
+       _qgroup_mode "$@" | grep -q 'qgroup'
+}
+
+_qgroup_rescan()
+{
+       local mnt=$1
+       local dev=$(findmnt -n -o SOURCE $mnt)
+
+       _check_regular_qgroup $dev || return 1
+       _run_btrfs_util_prog quota rescan -w $mnt
+}
+
+_require_qgroup_rescan()
+{
+       _scratch_mkfs >>$seqres.full 2>&1
+       _scratch_mount
+       _run_btrfs_util_prog quota enable $SCRATCH_MNT
+       # Wait for the first rescan.
+       $BTRFS_UTIL_PROG quota rescan -W $SCRATCH_MNT || \
+                               _notrun "not able to wait on a quota rescan"
+       # Make sure we can start a rescan.
+       $BTRFS_UTIL_PROG quota rescan -w $SCRATCH_MNT >> $seqres.full || \
+                                       _notrun "not able to run quota rescan"
+       _scratch_unmount
+}
+
+_require_scratch_qgroup()
+{
+       _scratch_mkfs >>$seqres.full 2>&1
+       _scratch_mount
+       $BTRFS_UTIL_PROG quota enable $SCRATCH_MNT
+       _check_regular_qgroup $SCRATCH_DEV || \
+                                       _notrun "not running normal qgroups"
+       _scratch_unmount
+}
+
+_require_scratch_enable_simple_quota()
+{
+       _scratch_mkfs >>$seqres.full 2>&1
+       _scratch_mount
+       _qgroup_mode $SCRATCH_DEV | grep 'squota' && \
+                       _notrun "cannot enable simple quota; on by default"
+       $BTRFS_UTIL_PROG quota enable --simple $SCRATCH_MNT || \
+                                       _notrun "simple quotas not available"
+       _scratch_unmount
+}
+
+_has_btrfs_sysfs_feature_attr()
+{
+       local feature_attr=$1
+
+       [ -z $feature_attr ] && \
+               _fail "Missing feature name argument for _has_btrfs_sysfs_attr"
+
+       modprobe btrfs &> /dev/null
+
+       test -e /sys/fs/btrfs/features/$feature_attr
+}
+
+# Print the fsid and metadata uuid replaced with constant strings FSID and
+# METADATA_UUID. Compare temp_fsid with fsid and metadata_uuid, then echo what
+# it matches to or TEMP_FSID. This helps in comparing with the golden output.
+check_fsid()
+{
+       local dev1=$1
+       local fsid
+       local metadata_uuid
+
+       _require_btrfs_fs_sysfs
+       _require_btrfs_fs_feature temp_fsid
+       _require_btrfs_fs_feature metadata_uuid
+       _require_btrfs_command inspect-internal dump-super
+
+       # on disk fsid
+       fsid=$($BTRFS_UTIL_PROG inspect-internal dump-super $dev1 | \
+                               grep ^fsid | $AWK_PROG -d" " '{print $2}')
+       echo -e "On disk fsid:\t\t$fsid" | sed -e "s/$fsid/FSID/g"
+
+       # Print FSID even if it is not the same as metadata_uuid because it has
+       # to match in the golden output.
+       metadata_uuid=$(cat /sys/fs/btrfs/$fsid/metadata_uuid)
+       echo -e "Metadata uuid:\t\tFSID"
+
+       # This returns the temp_fsid if set
+       tempfsid=$(_btrfs_get_fsid $dev1)
+       if [[ $tempfsid == $fsid ]]; then
+               echo -e "Temp fsid:\t\tFSID"
+       elif [[ $tempfsid == $metadata_uuid ]]; then
+               # If we are here, it means there is a bug; let it not match with
+               # the golden output.
+               echo -e "Temp fsid:\t\t$metadata_uuid"
+       else
+               echo -e "Temp fsid:\t\tTEMPFSID"
+       fi
+
+       echo -e -n "Tempfsid status:\t"
+       cat /sys/fs/btrfs/$tempfsid/temp_fsid
+}
+
+mkfs_clone()
+{
+       local fsid
+       local uuid
+       local dev1=$1
+       local dev2=$2
+
+       _require_btrfs_command inspect-internal dump-super
+       _require_btrfs_mkfs_uuid_option
+
+       [[ -z $dev1 || -z $dev2 ]] && \
+               _fail "mkfs_clone requires two devices as arguments"
+
+       _mkfs_dev -fq $dev1
+
+       fsid=$($BTRFS_UTIL_PROG inspect-internal dump-super $dev1 | \
+                                       grep -E ^fsid | $AWK_PROG '{print $2}')
+       uuid=$($BTRFS_UTIL_PROG inspect-internal dump-super $dev1 | \
+                               grep -E ^dev_item.uuid | $AWK_PROG '{print $2}')
+
+       _mkfs_dev -fq --uuid $fsid --device-uuid $uuid $dev2
+}
index c6428f90c7c7253aa1a2046c2f458190b3bbdd0d..2a1434bb11b9cfb3a25f1079649476b7cd31e90d 100644 (file)
@@ -25,6 +25,9 @@
 # KEEP_DMESG -      whether to keep all dmesg for each test case.
 #                   yes: keep all dmesg
 #                   no: only keep dmesg with error/warning (default)
+# CANON_DEVS -      whether or not to canonicalize device symlinks
+#                   yes: canonicalize device symlinks
+#                   no (default) do not canonicalize device if they are symlinks
 #
 # - These can be added to $HOST_CONFIG_DIR (witch default to ./config)
 #   below or a separate local configuration file can be used (using
@@ -57,11 +60,13 @@ export SOAK_PROC=3             # -p option to fsstress
 export SOAK_STRESS=10000       # -n option to fsstress
 export SOAK_PASSES=-1          # count of repetitions of fsstress (while soaking)
 export EMAIL=root@localhost    # where auto-qa will send its status messages
+
 export HOST_OPTIONS=${HOST_OPTIONS:=local.config}
 export CHECK_OPTIONS=${CHECK_OPTIONS:="-g auto"}
 export BENCH_PASSES=${BENCH_PASSES:=5}
 export TIME_FACTOR=${TIME_FACTOR:=1}
 export LOAD_FACTOR=${LOAD_FACTOR:=1}
+export SOAK_DURATION=${SOAK_DURATION:=}
 export DEBUGFS_MNT=${DEBUGFS_MNT:="/sys/kernel/debug"}
 
 # some constants for overlayfs setup
@@ -100,7 +105,7 @@ set_mkfs_prog_path_with_opts()
        # Note: mkfs.f2fs doesn't support the --help option yet, but it doesn't
        # matter since it also prints the help when an invalid option is given.
        if [ "$p" != "" ] && \
-               $p --help |& grep -q "[[:space:]]-f[[:space:]|]"; then
+               $p --help |& grep -q "[[:space:]]-f[[:space:]|,]"; then
                echo "$p -f"
        else
                echo $p
@@ -156,6 +161,7 @@ export XFS_LOGPRINT_PROG="$(type -P xfs_logprint)"
 export XFS_REPAIR_PROG="$(type -P xfs_repair)"
 export XFS_DB_PROG="$(type -P xfs_db)"
 export XFS_METADUMP_PROG="$(type -P xfs_metadump)"
+export XFS_MDRESTORE_PROG="$(type -P xfs_mdrestore)"
 export XFS_ADMIN_PROG="$(type -P xfs_admin)"
 export XFS_GROWFS_PROG=$(type -P xfs_growfs)
 export XFS_SPACEMAN_PROG="$(type -P xfs_spaceman)"
@@ -211,7 +217,7 @@ export NFS4_SETFACL_PROG="$(type -P nfs4_setfacl)"
 export NFS4_GETFACL_PROG="$(type -P nfs4_getfacl)"
 export UBIUPDATEVOL_PROG="$(type -P ubiupdatevol)"
 export THIN_CHECK_PROG="$(type -P thin_check)"
-export PYTHON2_PROG="$(type -P python2)"
+export PYTHON3_PROG="$(type -P python3)"
 export SQLITE3_PROG="$(type -P sqlite3)"
 export TIMEOUT_PROG="$(type -P timeout)"
 export SETCAP_PROG="$(type -P setcap)"
@@ -228,6 +234,7 @@ export E2IMAGE_PROG="$(type -P e2image)"
 export BLKZONE_PROG="$(type -P blkzone)"
 export GZIP_PROG="$(type -P gzip)"
 export BTRFS_IMAGE_PROG="$(type -P btrfs-image)"
+export BTRFS_MAP_LOGICAL_PROG=$(type -P btrfs-map-logical)
 
 # use 'udevadm settle' or 'udevsettle' to wait for lv to be settled.
 # newer systems have udevadm command but older systems like RHEL5 don't.
@@ -257,7 +264,7 @@ export UDEV_SETTLE_PROG
 # Set MODPROBE_PATIENT_RM_TIMEOUT_SECONDS to "forever" if you want the patient
 # modprobe removal to run forever trying to remove a module.
 MODPROBE_REMOVE_PATIENT=""
-modprobe --help >& /dev/null && modprobe --help | grep -q -1 "remove-patiently"
+modprobe --help >& /dev/null && modprobe --help 2>&1 | grep -q -1 "remove-patiently"
 if [[ $? -ne 0 ]]; then
        if [[ -z "$MODPROBE_PATIENT_RM_TIMEOUT_SECONDS" ]]; then
                # We will open code our own implementation of patient module
@@ -296,14 +303,17 @@ export BTRFS_UTIL_PROG=$(type -P btrfs)
 export BTRFS_SHOW_SUPER_PROG=$(type -P btrfs-show-super)
 export BTRFS_CONVERT_PROG=$(type -P btrfs-convert)
 export BTRFS_TUNE_PROG=$(type -P btrfstune)
+export BTRFS_CORRUPT_BLOCK_PROG=$(type -P btrfs-corrupt-block)
 export XFS_FSR_PROG=$(type -P xfs_fsr)
 export MKFS_NFS_PROG="false"
+export MKFS_AFS_PROG="false"
 export MKFS_CIFS_PROG="false"
 export MKFS_OVERLAY_PROG="false"
 export MKFS_REISER4_PROG=$(type -P mkfs.reiser4)
 export E2FSCK_PROG=$(type -P e2fsck)
 export TUNE2FS_PROG=$(type -P tune2fs)
 export FSCK_OVERLAY_PROG=$(type -P fsck.overlay)
+export MKFS_BCACHEFS_PROG=$(set_mkfs_prog_path_with_opts bcachefs)
 
 # SELinux adds extra xattrs which can mess up our expected output.
 # So, mount with a context, and they won't be created.
@@ -332,89 +342,78 @@ if [ "$FSTYP" == "xfs" ]; then
        export XFS_MKFS_HAS_NO_META_SUPPORT
 fi
 
-_mount_opts()
+_common_mount_opts()
 {
        case $FSTYP in
        9p)
-               export MOUNT_OPTIONS=$PLAN9_MOUNT_OPTIONS
+               echo $PLAN9_MOUNT_OPTIONS
+               ;;
+       fuse)
+               echo $FUSE_MOUNT_OPTIONS
                ;;
        xfs)
-               export MOUNT_OPTIONS=$XFS_MOUNT_OPTIONS
+               echo $XFS_MOUNT_OPTIONS
                ;;
        udf)
-               export MOUNT_OPTIONS=$UDF_MOUNT_OPTIONS
+               echo $UDF_MOUNT_OPTIONS
                ;;
        nfs)
-               export MOUNT_OPTIONS=$NFS_MOUNT_OPTIONS
+               echo $NFS_MOUNT_OPTIONS
+               ;;
+       afs)
+               echo $AFS_MOUNT_OPTIONS
                ;;
        cifs)
-               export MOUNT_OPTIONS=$CIFS_MOUNT_OPTIONS
+               echo $CIFS_MOUNT_OPTIONS
                ;;
        ceph)
-               export MOUNT_OPTIONS=$CEPHFS_MOUNT_OPTIONS
+               echo $CEPHFS_MOUNT_OPTIONS
                ;;
        glusterfs)
-               export MOUNT_OPTIONS=$GLUSTERFS_MOUNT_OPTIONS
+               echo $GLUSTERFS_MOUNT_OPTIONS
                ;;
        overlay)
-               export MOUNT_OPTIONS=$OVERLAY_MOUNT_OPTIONS
+               echo $OVERLAY_MOUNT_OPTIONS
                ;;
        ext2|ext3|ext4|ext4dev)
                # acls & xattrs aren't turned on by default on ext$FOO
-               export MOUNT_OPTIONS="-o acl,user_xattr $EXT_MOUNT_OPTIONS"
+               echo "-o acl,user_xattr $EXT_MOUNT_OPTIONS"
                ;;
        f2fs)
-               export MOUNT_OPTIONS="-o acl,user_xattr $F2FS_MOUNT_OPTIONS"
+               echo "-o acl,user_xattr $F2FS_MOUNT_OPTIONS"
                ;;
        reiserfs)
                # acls & xattrs aren't turned on by default on reiserfs
-               export MOUNT_OPTIONS="-o acl,user_xattr $REISERFS_MOUNT_OPTIONS"
+               echo "-o acl,user_xattr $REISERFS_MOUNT_OPTIONS"
                ;;
        reiser4)
-               # acls & xattrs aren't supported by reiser4
-               export MOUNT_OPTIONS=$REISER4_MOUNT_OPTIONS
-               ;;
+               # acls & xattrs aren't supported by reiser4
+               echo $REISER4_MOUNT_OPTIONS
+               ;;
        gfs2)
                # acls aren't turned on by default on gfs2
-               export MOUNT_OPTIONS="-o acl $GFS2_MOUNT_OPTIONS"
+               echo "-o acl $GFS2_MOUNT_OPTIONS"
                ;;
        tmpfs)
                # We need to specify the size at mount, use 1G by default
-               export MOUNT_OPTIONS="-o size=1G $TMPFS_MOUNT_OPTIONS"
+               echo "-o size=1G $TMPFS_MOUNT_OPTIONS"
                ;;
        ubifs)
-               export MOUNT_OPTIONS=$UBIFS_MOUNT_OPTIONS
+               echo $UBIFS_MOUNT_OPTIONS
                ;;
        *)
                ;;
        esac
 }
 
+_mount_opts()
+{
+       export MOUNT_OPTIONS=$(_common_mount_opts)
+}
+
 _test_mount_opts()
 {
-       case $FSTYP in
-       9p)
-               export TEST_FS_MOUNT_OPTS=$PLAN9_MOUNT_OPTIONS
-               ;;
-       cifs)
-               export TEST_FS_MOUNT_OPTS=$CIFS_MOUNT_OPTIONS
-               ;;
-       ceph)
-               export TEST_FS_MOUNT_OPTS=$CEPHFS_MOUNT_OPTIONS
-               ;;
-       nfs)
-               export TEST_FS_MOUNT_OPTS=$NFS_MOUNT_OPTIONS
-               ;;
-       glusterfs)
-               export TEST_FS_MOUNT_OPTS=$GLUSTERFS_MOUNT_OPTIONS
-               ;;
-       ext2|ext3|ext4|ext4dev)
-               # acls & xattrs aren't turned on by default on older ext$FOO
-               export TEST_FS_MOUNT_OPTS="-o acl,user_xattr $EXT_MOUNT_OPTIONS"
-               ;;
-       *)
-               ;;
-       esac
+       export TEST_FS_MOUNT_OPTS=$(_common_mount_opts)
 }
 
 _mkfs_opts()
@@ -431,6 +430,9 @@ _mkfs_opts()
        nfs)
                export MKFS_OPTIONS=$NFS_MKFS_OPTIONS
                ;;
+       afs)
+               export MKFS_OPTIONS=$AFS_MKFS_OPTIONS
+               ;;
        cifs)
                export MKFS_OPTIONS=$CIFS_MKFS_OPTIONS
                ;;
@@ -481,6 +483,74 @@ _fsck_opts()
        esac
 }
 
+# check necessary running dependences then source sepcific fs helpers
+_source_specific_fs()
+{
+       local fs=$1
+
+       if [ -z "$fs" ];then
+               fs=$FSTYP
+       fi
+
+       case "$fs" in
+       xfs)
+               [ "$XFS_LOGPRINT_PROG" = "" ] && _fatal "xfs_logprint not found"
+               [ "$XFS_REPAIR_PROG" = "" ] && _fatal "xfs_repair not found"
+               [ "$XFS_DB_PROG" = "" ] && _fatal "xfs_db not found"
+               [ "$MKFS_XFS_PROG" = "" ] && _fatal "mkfs_xfs not found"
+               [ "$XFS_INFO_PROG" = "" ] && _fatal "xfs_info not found"
+
+               . ./common/xfs
+               ;;
+       udf)
+               [ "$MKFS_UDF_PROG" = "" ] && _fatal "mkfs_udf/mkudffs not found"
+               ;;
+       btrfs)
+               [ "$MKFS_BTRFS_PROG" = "" ] && _fatal "mkfs.btrfs not found"
+
+               . ./common/btrfs
+               ;;
+       ext4)
+               [ "$MKFS_EXT4_PROG" = "" ] && _fatal "mkfs.ext4 not found"
+               . ./common/ext4
+               ;;
+       ext2|ext3|ext4dev)
+               . ./common/ext4
+               ;;
+       f2fs)
+               [ "$MKFS_F2FS_PROG" = "" ] && _fatal "mkfs.f2fs not found"
+               ;;
+       nfs)
+               . ./common/nfs
+               ;;
+       afs)
+               ;;
+       cifs)
+               ;;
+       9p)
+               ;;
+       fuse)
+               ;;
+       ceph)
+               . ./common/ceph
+               ;;
+       glusterfs)
+               ;;
+       overlay)
+               . ./common/overlay
+               ;;
+       reiser4)
+               [ "$MKFS_REISER4_PROG" = "" ] && _fatal "mkfs.reiser4 not found"
+               ;;
+       pvfs2)
+               ;;
+       ubifs)
+               [ "$UBIUPDATEVOL_PROG" = "" ] && _fatal "ubiupdatevol not found"
+               . ./common/ubifs
+               ;;
+       esac
+}
+
 known_hosts()
 {
        [ "$HOST_CONFIG_DIR" ] || HOST_CONFIG_DIR=`pwd`/configs
@@ -534,9 +604,9 @@ _check_device()
        fi
 
        case "$FSTYP" in
-       9p|tmpfs|virtiofs)
-               # 9p and virtiofs mount tags are just plain strings, so anything is allowed
-               # tmpfs doesn't use mount source, ignore
+       9p|fuse|tmpfs|virtiofs|afs)
+               # 9p, fuse, virtiofs and afs mount tags are just plain strings,
+               # so anything is allowed tmpfs doesn't use mount source, ignore
                ;;
        ceph)
                # ceph has two different possible syntaxes for mount devices. The
@@ -590,6 +660,32 @@ _canonicalize_mountpoint()
        echo "$parent/$base"
 }
 
+# Enables usage of /dev/disk/by-id/ symlinks to persist target devices
+# over reboots
+_canonicalize_devices()
+{
+       if [ "$CANON_DEVS" != "yes" ]; then
+               return
+       fi
+       [ -L "$TEST_DEV" ]      && TEST_DEV=$(readlink -e "$TEST_DEV")
+       [ -L "$SCRATCH_DEV" ]   && SCRATCH_DEV=$(readlink -e "$SCRATCH_DEV")
+       [ -L "$TEST_LOGDEV" ]   && TEST_LOGDEV=$(readlink -e "$TEST_LOGDEV")
+       [ -L "$TEST_RTDEV" ]    && TEST_RTDEV=$(readlink -e "$TEST_RTDEV")
+       [ -L "$SCRATCH_RTDEV" ] && SCRATCH_RTDEV=$(readlink -e "$SCRATCH_RTDEV")
+       [ -L "$LOGWRITES_DEV" ] && LOGWRITES_DEV=$(readlink -e "$LOGWRITES_DEV")
+       if [ ! -z "$SCRATCH_DEV_POOL" ]; then
+               local NEW_SCRATCH_POOL=""
+               for i in $SCRATCH_DEV_POOL; do
+                       if [ -L $i ]; then
+                               NEW_SCRATCH_POOL="$NEW_SCRATCH_POOL $(readlink -e $i)"
+                       else
+                               NEW_SCRATCH_POOL="$NEW_SCRATCH_POOL $i"
+                       fi
+               done
+               SCRATCH_DEV_POOL="$NEW_SCRATCH_POOL"
+       fi
+}
+
 # On check -overlay, for the non multi section config case, this
 # function is called on every test, before init_rc().
 # When SCRATCH/TEST_* vars are defined in config file, config file
@@ -720,7 +816,6 @@ get_next_config() {
        fi
 
        parse_config_section $1
-
        if [ ! -z "$OLD_FSTYP" ] && [ $OLD_FSTYP != $FSTYP ]; then
                [ -z "$MOUNT_OPTIONS" ] && _mount_opts
                [ -z "$TEST_FS_MOUNT_OPTS" ] && _test_mount_opts
@@ -836,5 +931,7 @@ else
        fi
 fi
 
+_canonicalize_devices
+
 # make sure this script returns success
 /bin/true
index 01a4c8b5e52d3cd4469bfd911504577a7dd25c5a..3494b6dd3b9479e6120ca32717931044ccb1d847 100644 (file)
@@ -4,25 +4,88 @@
 #
 # common functions for setting up and tearing down a dmerror device
 
+_dmerror_setup_vars()
+{
+       local backing_dev="$1"
+       local tag="$2"
+       local target="$3"
+
+       test -z "$target" && target=error
+       local blk_dev_size=$(blockdev --getsz "$backing_dev")
+
+       eval export "DMLINEAR_${tag}TABLE=\"0 $blk_dev_size linear $backing_dev 0\""
+       eval export "DMERROR_${tag}TABLE=\"0 $blk_dev_size $target $backing_dev 0\""
+}
+
 _dmerror_setup()
 {
-       local dm_backing_dev=$SCRATCH_DEV
+       local rt_target=
+       local log_target=
 
-       local blk_dev_size=`blockdev --getsz $dm_backing_dev`
+       for arg in "$@"; do
+               case "${arg}" in
+               no_rt)          rt_target=linear;;
+               no_log)         log_target=linear;;
+               *)              echo "${arg}: Unknown _dmerror_setup arg.";;
+               esac
+       done
 
+       # Scratch device
        export DMERROR_DEV='/dev/mapper/error-test'
+       _dmerror_setup_vars $SCRATCH_DEV
 
-       export DMLINEAR_TABLE="0 $blk_dev_size linear $dm_backing_dev 0"
+       # Realtime device.  We reassign SCRATCH_RTDEV so that all the scratch
+       # helpers continue to work unmodified.
+       if [ -n "$SCRATCH_RTDEV" ]; then
+               if [ -z "$NON_ERROR_RTDEV" ]; then
+                       # Set up the device switch
+                       local dm_backing_dev=$SCRATCH_RTDEV
+                       export NON_ERROR_RTDEV="$SCRATCH_RTDEV"
+                       SCRATCH_RTDEV='/dev/mapper/error-rttest'
+               else
+                       # Already set up; recreate tables
+                       local dm_backing_dev="$NON_ERROR_RTDEV"
+               fi
 
-       export DMERROR_TABLE="0 $blk_dev_size error $dm_backing_dev 0"
+               _dmerror_setup_vars $dm_backing_dev RT $rt_target
+       fi
+
+       # External log device.  We reassign SCRATCH_LOGDEV so that all the
+       # scratch helpers continue to work unmodified.
+       if [ -n "$SCRATCH_LOGDEV" ]; then
+               if [ -z "$NON_ERROR_LOGDEV" ]; then
+                       # Set up the device switch
+                       local dm_backing_dev=$SCRATCH_LOGDEV
+                       export NON_ERROR_LOGDEV="$SCRATCH_LOGDEV"
+                       SCRATCH_LOGDEV='/dev/mapper/error-logtest'
+               else
+                       # Already set up; recreate tables
+                       local dm_backing_dev="$NON_ERROR_LOGDEV"
+               fi
+
+               _dmerror_setup_vars $dm_backing_dev LOG $log_target
+       fi
 }
 
 _dmerror_init()
 {
-       _dmerror_setup
+       _dmerror_setup "$@"
+
        _dmsetup_remove error-test
        _dmsetup_create error-test --table "$DMLINEAR_TABLE" || \
                _fatal "failed to create dm linear device"
+
+       if [ -n "$NON_ERROR_RTDEV" ]; then
+               _dmsetup_remove error-rttest
+               _dmsetup_create error-rttest --table "$DMLINEAR_RTTABLE" || \
+                       _fatal "failed to create dm linear rt device"
+       fi
+
+       if [ -n "$NON_ERROR_LOGDEV" ]; then
+               _dmsetup_remove error-logtest
+               _dmsetup_create error-logtest --table "$DMLINEAR_LOGTABLE" || \
+                       _fatal "failed to create dm linear log device"
+       fi
 }
 
 _dmerror_mount()
@@ -39,11 +102,27 @@ _dmerror_unmount()
 
 _dmerror_cleanup()
 {
+       test -n "$NON_ERROR_LOGDEV" && $DMSETUP_PROG resume error-logtest &>/dev/null
+       test -n "$NON_ERROR_RTDEV" && $DMSETUP_PROG resume error-rttest &>/dev/null
        $DMSETUP_PROG resume error-test > /dev/null 2>&1
+
        $UMOUNT_PROG $SCRATCH_MNT > /dev/null 2>&1
+
+       test -n "$NON_ERROR_LOGDEV" && _dmsetup_remove error-logtest
+       test -n "$NON_ERROR_RTDEV" && _dmsetup_remove error-rttest
        _dmsetup_remove error-test
 
        unset DMERROR_DEV DMLINEAR_TABLE DMERROR_TABLE
+
+       if [ -n "$NON_ERROR_LOGDEV" ]; then
+               SCRATCH_LOGDEV="$NON_ERROR_LOGDEV"
+               unset NON_ERROR_LOGDEV DMLINEAR_LOGTABLE DMERROR_LOGTABLE
+       fi
+
+       if [ -n "$NON_ERROR_RTDEV" ]; then
+               SCRATCH_RTDEV="$NON_ERROR_RTDEV"
+               unset NON_ERROR_RTDEV DMLINEAR_RTTABLE DMERROR_RTTABLE
+       fi
 }
 
 _dmerror_load_error_table()
@@ -59,12 +138,51 @@ _dmerror_load_error_table()
                suspend_opt="$*"
        fi
 
+       # If the full environment is set up, configure ourselves for shutdown
+       type _prepare_for_eio_shutdown &>/dev/null && \
+               _prepare_for_eio_shutdown $DMERROR_DEV
+
+       # Suspend the scratch device before the log and realtime devices so
+       # that the kernel can freeze and flush the filesystem if the caller
+       # wanted a freeze.
        $DMSETUP_PROG suspend $suspend_opt error-test
        [ $? -ne 0 ] && _fail  "dmsetup suspend failed"
 
+       if [ -n "$NON_ERROR_RTDEV" ]; then
+               $DMSETUP_PROG suspend $suspend_opt error-rttest
+               [ $? -ne 0 ] && _fail "failed to suspend error-rttest"
+       fi
+
+       if [ -n "$NON_ERROR_LOGDEV" ]; then
+               $DMSETUP_PROG suspend $suspend_opt error-logtest
+               [ $? -ne 0 ] && _fail "failed to suspend error-logtest"
+       fi
+
+       # Load new table
        $DMSETUP_PROG load error-test --table "$DMERROR_TABLE"
        load_res=$?
 
+       if [ -n "$NON_ERROR_RTDEV" ]; then
+               $DMSETUP_PROG load error-rttest --table "$DMERROR_RTTABLE"
+               [ $? -ne 0 ] && _fail "failed to load error table into error-rttest"
+       fi
+
+       if [ -n "$NON_ERROR_LOGDEV" ]; then
+               $DMSETUP_PROG load error-logtest --table "$DMERROR_LOGTABLE"
+               [ $? -ne 0 ] && _fail "failed to load error table into error-logtest"
+       fi
+
+       # Resume devices in the opposite order that we suspended them.
+       if [ -n "$NON_ERROR_LOGDEV" ]; then
+               $DMSETUP_PROG resume error-logtest
+               [ $? -ne 0 ] && _fail  "failed to resume error-logtest"
+       fi
+
+       if [ -n "$NON_ERROR_RTDEV" ]; then
+               $DMSETUP_PROG resume error-rttest
+               [ $? -ne 0 ] && _fail  "failed to resume error-rttest"
+       fi
+
        $DMSETUP_PROG resume error-test
        resume_res=$?
 
@@ -85,15 +203,197 @@ _dmerror_load_working_table()
                suspend_opt="$*"
        fi
 
+       # Suspend the scratch device before the log and realtime devices so
+       # that the kernel can freeze and flush the filesystem if the caller
+       # wanted a freeze.
        $DMSETUP_PROG suspend $suspend_opt error-test
        [ $? -ne 0 ] && _fail  "dmsetup suspend failed"
 
+       if [ -n "$NON_ERROR_RTDEV" ]; then
+               $DMSETUP_PROG suspend $suspend_opt error-rttest
+               [ $? -ne 0 ] && _fail "failed to suspend error-rttest"
+       fi
+
+       if [ -n "$NON_ERROR_LOGDEV" ]; then
+               $DMSETUP_PROG suspend $suspend_opt error-logtest
+               [ $? -ne 0 ] && _fail "failed to suspend error-logtest"
+       fi
+
+       # Load new table
        $DMSETUP_PROG load error-test --table "$DMLINEAR_TABLE"
        load_res=$?
 
+       if [ -n "$NON_ERROR_RTDEV" ]; then
+               $DMSETUP_PROG load error-rttest --table "$DMLINEAR_RTTABLE"
+               [ $? -ne 0 ] && _fail "failed to load working table into error-rttest"
+       fi
+
+       if [ -n "$NON_ERROR_LOGDEV" ]; then
+               $DMSETUP_PROG load error-logtest --table "$DMLINEAR_LOGTABLE"
+               [ $? -ne 0 ] && _fail "failed to load working table into error-logtest"
+       fi
+
+       # Resume devices in the opposite order that we suspended them.
+       if [ -n "$NON_ERROR_LOGDEV" ]; then
+               $DMSETUP_PROG resume error-logtest
+               [ $? -ne 0 ] && _fail  "failed to resume error-logtest"
+       fi
+
+       if [ -n "$NON_ERROR_RTDEV" ]; then
+               $DMSETUP_PROG resume error-rttest
+               [ $? -ne 0 ] && _fail  "failed to resume error-rttest"
+       fi
+
        $DMSETUP_PROG resume error-test
        resume_res=$?
 
        [ $load_res -ne 0 ] && _fail "dmsetup failed to load error table"
        [ $resume_res -ne 0 ] && _fail  "dmsetup resume failed"
 }
+
+# Given a list of (start, length) tuples on stdin, combine adjacent tuples into
+# larger ones and write the new list to stdout.
+__dmerror_combine_extents()
+{
+       local awk_program='
+       BEGIN {
+               start = 0; len = 0;
+       }
+       {
+               if (start + len == $1) {
+                       len += $2;
+               } else {
+                       if (len > 0)
+                               printf("%d %d\n", start, len);
+                       start = $1;
+                       len = $2;
+               }
+       }
+       END {
+               if (len > 0)
+                       printf("%d %d\n", start, len);
+       }'
+
+       awk "$awk_program"
+}
+
+# Given a block device, the name of a preferred dm target, the name of an
+# implied dm target, and a list of (start, len) tuples on stdin, create a new
+# dm table which maps each of the tuples to the preferred target and all other
+# areas to the implied dm target.
+__dmerror_recreate_map()
+{
+       local device="$1"
+       local preferred_tgt="$2"
+       local implied_tgt="$3"
+       local size=$(blockdev --getsz "$device")
+
+       local awk_program='
+       BEGIN {
+               implied_start = 0;
+       }
+       {
+               extent_start = $1;
+               extent_len = $2;
+
+               if (extent_start > size) {
+                       extent_start = size;
+                       extent_len = 0;
+               } else if (extent_start + extent_len > size) {
+                       extent_len = size - extent_start;
+               }
+
+               if (implied_start < extent_start)
+                       printf("%d %d %s %s %d\n", implied_start,
+                                       extent_start - implied_start,
+                                       implied_tgt, device, implied_start);
+               printf("%d %d %s %s %d\n", extent_start, extent_len,
+                               preferred_tgt, device, extent_start);
+               implied_start = extent_start + extent_len;
+       }
+       END {
+               if (implied_start < size)
+                       printf("%d %d %s %s %d\n", implied_start,
+                                       size - implied_start, implied_tgt,
+                                       device, implied_start);
+       }'
+
+       awk -v device="$device" -v size=$size -v implied_tgt="$implied_tgt" \
+               -v preferred_tgt="$preferred_tgt" "$awk_program"
+}
+
+# Update the dm error table so that the range (start, len) maps to the
+# preferred dm target, overriding anything that maps to the implied dm target.
+# This assumes that the only desired targets for this dm device are the
+# preferred and and implied targets.  The fifth argument is the scratch device
+# that we want to change the table for.
+__dmerror_change()
+{
+       local start="$1"
+       local len="$2"
+       local preferred_tgt="$3"
+       local implied_tgt="$4"
+       local whichdev="$5"
+       local old_table
+       local new_table
+
+       case "$whichdev" in
+       "SCRATCH_DEV"|"")       whichdev="$SCRATCH_DEV";;
+       "SCRATCH_LOGDEV"|"LOG") whichdev="$NON_ERROR_LOGDEV";;
+       "SCRATCH_RTDEV"|"RT")   whichdev="$NON_ERROR_RTDEV";;
+       esac
+
+       case "$whichdev" in
+       "$SCRATCH_DEV")         old_table="$DMERROR_TABLE";;
+       "$NON_ERROR_LOGDEV")    old_table="$DMERROR_LOGTABLE";;
+       "$NON_ERROR_RTDEV")     old_table="$DMERROR_RTTABLE";;
+       *)
+               echo "$whichdev: Unknown dmerror device."
+               return
+               ;;
+       esac
+
+       new_table="$( (echo "$old_table"; echo "$start $len $preferred_tgt") | \
+               awk -v type="$preferred_tgt" '{if ($3 == type) print $0;}' | \
+               sort -g | \
+               __dmerror_combine_extents | \
+               __dmerror_recreate_map "$whichdev" "$preferred_tgt" \
+                               "$implied_tgt" )"
+
+       case "$whichdev" in
+       "$SCRATCH_DEV")         DMERROR_TABLE="$new_table";;
+       "$NON_ERROR_LOGDEV")    DMERROR_LOGTABLE="$new_table";;
+       "$NON_ERROR_RTDEV")     DMERROR_RTTABLE="$new_table";;
+       esac
+}
+
+# Reset the dm error table to everything ok.  The dm device itself must be
+# remapped by calling _dmerror_load_error_table.
+_dmerror_reset_table()
+{
+       DMERROR_TABLE="$DMLINEAR_TABLE"
+       DMERROR_LOGTABLE="$DMLINEAR_LOGTABLE"
+       DMERROR_RTTABLE="$DMLINEAR_RTTABLE"
+}
+
+# Update the dm error table so that IOs to the given range will return EIO.
+# The dm device itself must be remapped by calling _dmerror_load_error_table.
+_dmerror_mark_range_bad()
+{
+       local start="$1"
+       local len="$2"
+       local dev="$3"
+
+       __dmerror_change "$start" "$len" error linear "$dev"
+}
+
+# Update the dm error table so that IOs to the given range will succeed.
+# The dm device itself must be remapped by calling _dmerror_load_error_table.
+_dmerror_mark_range_good()
+{
+       local start="$1"
+       local len="$2"
+       local dev="$3"
+
+       __dmerror_change "$start" "$len" linear error "$dev"
+}
index 66c57e2bdfe9da8614c97bbd20f9429917a3ac4f..c1c85de9dd43ac68deb71360e89746ac109af39d 100644 (file)
@@ -51,7 +51,7 @@ _require_log_writes_dax_mountopt()
        # Check options to be sure.
        # XFS ignores dax option(or changes it to dax=never)
        # and goes on if dev underneath does not support dax.
-       _fs_options $LOGWRITES_DMDEV | egrep -q "dax(=always|,|$)"
+       _fs_options $LOGWRITES_DMDEV | grep -Eq "dax(=always|,|$)"
        ret=$?
        _log_writes_cleanup
        if [ $ret -ne 0 ]; then
@@ -59,14 +59,28 @@ _require_log_writes_dax_mountopt()
        fi
 }
 
+# Set up a dm-log-writes device
+#
+# blkdev: the specified target device
+# length(optional): the mapped length in bytes
+# Note that the entire size of the target device will be used
+# if length is not specified.
 _log_writes_init()
 {
-       blkdev=$1
+       local blkdev=$1
+       local length=$2
+       local BLK_DEV_SIZE
 
        [ -z "$blkdev" ] && _fail \
        "block dev must be specified for _log_writes_init"
 
-       local BLK_DEV_SIZE=`blockdev --getsz $blkdev`
+       if [ -z "$length" ]; then
+               BLK_DEV_SIZE=`blockdev --getsz $blkdev`
+       else
+               local blksz=`blockdev --getss $blkdev`
+               BLK_DEV_SIZE=$((length / blksz))
+       fi
+
        LOGWRITES_NAME=logwrites-test
        LOGWRITES_DMDEV=/dev/mapper/$LOGWRITES_NAME
        LOGWRITES_TABLE="0 $BLK_DEV_SIZE log-writes $blkdev $LOGWRITES_DEV"
index 91147e47ac34a51a6ea1a745ba05ae070ea50b2d..7107d50804896ee008cfd3ac0e25b488dbf05bb2 100644 (file)
@@ -234,5 +234,10 @@ _dmthin_mount()
 _dmthin_mkfs()
 {
        _scratch_options mkfs
-       _mkfs_dev $SCRATCH_OPTIONS $@ $DMTHIN_VOL_DEV
+       _mkfs_dev $SCRATCH_OPTIONS "$@" $DMTHIN_VOL_DEV
+}
+_dmthin_try_mkfs()
+{
+       _scratch_options mkfs
+       _try_mkfs_dev $SCRATCH_OPTIONS "$@" $DMTHIN_VOL_DEV
 }
index 0dcc9655d328345a1789a4757657752282e5e480..50b2ba03c670f2b299481347ca0c763a502ae761 100644 (file)
@@ -128,7 +128,7 @@ _check_onl()
     fi
 
 
-    if egrep -i 'onl|ready' $tmp.status | grep -iv 'not ready' >/dev/null; then
+    if grep -Ei 'onl|ready' $tmp.status | grep -iv 'not ready' >/dev/null; then
        :
     else
        echo "ERROR: $dumptape is not online"
@@ -145,7 +145,7 @@ _wait_tape()
     i=0
     while [ $i -lt 20 ]; do
         echo "Checking status..." >>$seqres.full
-       if _mt status 2>&1 | tee -a $seqres.full | egrep -i "onl|ready" >/dev/null; then
+       if _mt status 2>&1 | tee -a $seqres.full | grep -Ei "onl|ready" >/dev/null; then
            break;
        else
            sleep 1
@@ -1003,7 +1003,7 @@ _parse_restore_args()
         --no-check-quota)
             do_quota_check=false
             ;;
-       -K|-R)
+       -K|-R|-x)
            restore_args="$restore_args $1"
             ;;
        *)
index 8f3c46f642082f0eef8390d8006806b0966445d0..d90a566ac134794de7957ee73c29c5d9c9f4c3c2 100644 (file)
@@ -7,6 +7,7 @@
 #
 # _require_scratch_encryption [-c CONTENTS_MODE] [-n FILENAMES_MODE]
 #                            [-f POLICY_FLAGS] [-v POLICY_VERSION]
+#                            [-s LOG2_DUSIZE]
 #
 # Require encryption support on the scratch device.
 #
 #
 _require_scratch_encryption()
 {
-       _require_scratch
+       local arg
 
+       _require_scratch
        _require_xfs_io_command "set_encpolicy"
 
+       for arg; do
+               if [ "$arg" = "-s" ]; then
+                       # -s option was added later.  Make sure it's available.
+                       _require_xfs_io_command "set_encpolicy" "-s"
+               fi
+       done
+
        # The 'test_dummy_encryption' mount option interferes with trying to use
        # encryption for real, even if we are just trying to get/set policies
        # and never put any keys in the keyring.  So skip the real encryption
@@ -49,7 +58,7 @@ _require_scratch_encryption()
        # some older kernels and is ext4-specific anyway.)
        mkdir $SCRATCH_MNT/tmpdir
        if _set_encpolicy $SCRATCH_MNT/tmpdir 2>&1 >>$seqres.full | \
-               egrep -q 'Inappropriate ioctl for device|Operation not supported'
+               grep -Eq 'Inappropriate ioctl for device|Operation not supported'
        then
                _notrun "kernel does not support $FSTYP encryption"
        fi
@@ -74,9 +83,9 @@ _require_encryption_policy_support()
        local c
 
        OPTIND=2
-       while getopts "c:n:f:v:" c; do
+       while getopts "c:n:f:s:v:" c; do
                case $c in
-               c|n)
+               c|n|s)
                        set_encpolicy_args+=" -$c $OPTARG"
                        ;;
                f)
@@ -88,7 +97,7 @@ _require_encryption_policy_support()
                        policy_version=$OPTARG
                        ;;
                *)
-                       _fail "Unrecognized option '$c'"
+                       _fail "${FUNCNAME[0]}: unrecognized option '$c'"
                        ;;
                esac
        done
@@ -125,7 +134,7 @@ _require_encryption_policy_support()
                local keyspec=$(_generate_session_encryption_key)
        fi
        if _set_encpolicy $dir $keyspec $set_encpolicy_args \
-               2>&1 >>$seqres.full | egrep -q 'Invalid argument'; then
+               2>&1 >>$seqres.full | grep -Eq 'Invalid argument'; then
                _notrun "kernel does not support encryption policy: '$set_encpolicy_args'"
        fi
        # fscrypt allows setting policies with modes it knows about, even
@@ -153,6 +162,9 @@ _scratch_mkfs_encrypted()
                # erase the UBI volume; reformated automatically on next mount
                $UBIUPDATEVOL_PROG ${SCRATCH_DEV} -t
                ;;
+       ceph)
+               _scratch_cleanup_files
+               ;;
        *)
                _notrun "No encryption support for $FSTYP"
                ;;
@@ -760,14 +772,13 @@ _do_verify_ciphertext_for_encryption_policy()
                nonce=$(_get_encryption_nonce $SCRATCH_DEV $inode)
                _dump_ciphertext_blocks $SCRATCH_DEV $blocklist > $tmp.actual_contents
                $crypt_contents_cmd $contents_encryption_mode $raw_key_hex \
-                       --file-nonce=$nonce --block-size=$blocksize \
-                       --inode-number=$inode < $src > $tmp.expected_contents
+                       --file-nonce=$nonce --inode-number=$inode \
+                        < $src > $tmp.expected_contents
                if ! cmp $tmp.expected_contents $tmp.actual_contents; then
                        _fail "Expected encrypted contents != actual encrypted contents.  File: $f"
                fi
                $crypt_contents_cmd $contents_encryption_mode $raw_key_hex \
-                       --decrypt --file-nonce=$nonce --block-size=$blocksize \
-                       --inode-number=$inode \
+                       --decrypt --file-nonce=$nonce --inode-number=$inode \
                        < $tmp.actual_contents > $tmp.decrypted_contents
                if ! cmp $src $tmp.decrypted_contents; then
                        _fail "Contents decryption sanity check failed.  File: $f"
@@ -783,14 +794,14 @@ _do_verify_ciphertext_for_encryption_policy()
                echo -n "$name" | \
                        $crypt_filename_cmd $filenames_encryption_mode \
                        $raw_key_hex --file-nonce=$nonce --padding=$padding \
-                       --block-size=255 --inode-number=$dir_inode \
+                       --data-unit-size=255 --inode-number=$dir_inode \
                        > $tmp.expected_name
                if ! cmp $tmp.expected_name $tmp.actual_name; then
                        _fail "Expected encrypted filename != actual encrypted filename.  File: $f"
                fi
                $crypt_filename_cmd $filenames_encryption_mode $raw_key_hex \
                        --decrypt --file-nonce=$nonce --padding=$padding \
-                       --block-size=255 --inode-number=$dir_inode \
+                       --data-unit-size=255 --inode-number=$dir_inode \
                        < $tmp.actual_name > $tmp.decrypted_name
                decrypted_name=$(tr -d '\0' < $tmp.decrypted_name)
                if [ "$name" != "$decrypted_name" ]; then
@@ -806,6 +817,7 @@ FSCRYPT_MODE_AES_256_CTS=4
 FSCRYPT_MODE_AES_128_CBC=5
 FSCRYPT_MODE_AES_128_CTS=6
 FSCRYPT_MODE_ADIANTUM=9
+FSCRYPT_MODE_AES_256_HCTR2=10
 
 FSCRYPT_POLICY_FLAG_DIRECT_KEY=0x04
 FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64=0x08
@@ -824,6 +836,7 @@ _fscrypt_mode_name_to_num()
        AES-128-CBC-ESSIV)      echo $FSCRYPT_MODE_AES_128_CBC ;;
        AES-128-CTS-CBC)        echo $FSCRYPT_MODE_AES_128_CTS ;;
        Adiantum)               echo $FSCRYPT_MODE_ADIANTUM ;;
+       AES-256-HCTR2)          echo $FSCRYPT_MODE_AES_256_HCTR2 ;;
        *)                      _fail "Unknown fscrypt mode: $name" ;;
        esac
 }
@@ -839,6 +852,7 @@ _fscrypt_mode_name_to_num()
 #      'direct':               test the DIRECT_KEY policy flag
 #      'iv_ino_lblk_64':       test the IV_INO_LBLK_64 policy flag
 #      'iv_ino_lblk_32':       test the IV_INO_LBLK_32 policy flag
+#      'log2_dusize=N':        test the log2_data_unit_size field
 #
 _verify_ciphertext_for_encryption_policy()
 {
@@ -847,6 +861,7 @@ _verify_ciphertext_for_encryption_policy()
        local opt
        local policy_version=1
        local policy_flags=0
+       local log2_dusize=0
        local set_encpolicy_args=""
        local crypt_util_args=""
        local crypt_util_contents_args=""
@@ -872,6 +887,9 @@ _verify_ciphertext_for_encryption_policy()
                iv_ino_lblk_32)
                        (( policy_flags |= FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 ))
                        ;;
+               log2_dusize=*)
+                       log2_dusize=$(echo "$opt" | sed 's/^log2_dusize=//')
+                       ;;
                *)
                        _fail "Unknown option '$opt' passed to ${FUNCNAME[0]}"
                        ;;
@@ -882,6 +900,9 @@ _verify_ciphertext_for_encryption_policy()
 
        set_encpolicy_args+=" -c $contents_mode_num"
        set_encpolicy_args+=" -n $filenames_mode_num"
+       if (( log2_dusize != 0 )); then
+               set_encpolicy_args+=" -s $log2_dusize"
+       fi
        crypt_util_contents_args+=" --mode-num=$contents_mode_num"
        crypt_util_filename_args+=" --mode-num=$filenames_mode_num"
 
@@ -925,6 +946,12 @@ _verify_ciphertext_for_encryption_policy()
        fi
        _scratch_mount
 
+       if (( log2_dusize != 0 )); then
+               crypt_util_contents_args+=" --data-unit-size=$((1 << log2_dusize))"
+       else
+               crypt_util_contents_args+=" --data-unit-size=$(_get_block_size $SCRATCH_MNT)"
+       fi
+
        crypt_util_args+=" --fs-uuid=$(blkid -s UUID -o value $SCRATCH_DEV | tr -d -)"
 
        crypt_util_contents_args+="$crypt_util_args"
diff --git a/common/ext4 b/common/ext4
new file mode 100644 (file)
index 0000000..3dcbfe1
--- /dev/null
@@ -0,0 +1,227 @@
+#
+# ext4 specific common functions
+#
+
+__generate_ext4_report_vars() {
+       __generate_blockdev_report_vars TEST_LOGDEV
+       __generate_blockdev_report_vars SCRATCH_LOGDEV
+}
+
+_setup_large_ext4_fs()
+{
+       local fs_size=$1
+       local tmp_dir=/tmp/
+
+       [ "$LARGE_SCRATCH_DEV" != yes ] && return 0
+       [ -z "$SCRATCH_DEV_EMPTY_SPACE" ] && SCRATCH_DEV_EMPTY_SPACE=0
+       [ $SCRATCH_DEV_EMPTY_SPACE -ge $fs_size ] && return 0
+
+       # Default free space in the FS is 50GB, but you can specify more via
+       # SCRATCH_DEV_EMPTY_SPACE
+       local space_to_consume=$(($fs_size - 50*1024*1024*1024 - $SCRATCH_DEV_EMPTY_SPACE))
+
+       # mount the filesystem and create 16TB - 4KB files until we consume
+       # all the necessary space.
+       _try_scratch_mount 2>&1 >$tmp_dir/mnt.err
+       local status=$?
+       if [ $status -ne 0 ]; then
+               echo "mount failed"
+               cat $tmp_dir/mnt.err >&2
+               rm -f $tmp_dir/mnt.err
+               return $status
+       fi
+       rm -f $tmp_dir/mnt.err
+
+       local file_size=$((16*1024*1024*1024*1024 - 4096))
+       local nfiles=0
+       while [ $space_to_consume -gt $file_size ]; do
+
+               xfs_io -F -f \
+                       -c "truncate $file_size" \
+                       -c "falloc -k 0 $file_size" \
+                       $SCRATCH_MNT/.use_space.$nfiles 2>&1
+               status=$?
+               if [ $status -ne 0 ]; then
+                       break;
+               fi
+
+               space_to_consume=$(( $space_to_consume - $file_size ))
+               nfiles=$(($nfiles + 1))
+       done
+
+       # consume the remaining space.
+       if [ $space_to_consume -gt 0 ]; then
+               xfs_io -F -f \
+                       -c "truncate $space_to_consume" \
+                       -c "falloc -k 0 $space_to_consume" \
+                       $SCRATCH_MNT/.use_space.$nfiles 2>&1
+               status=$?
+       fi
+       export NUM_SPACE_FILES=$nfiles
+
+       _scratch_unmount
+       if [ $status -ne 0 ]; then
+               echo "large file prealloc failed"
+               cat $tmp_dir/mnt.err >&2
+               return $status
+       fi
+       return 0
+}
+
+_scratch_mkfs_ext4_opts()
+{
+       mkfs_opts=$*
+
+       _scratch_options mkfs
+
+       echo "$MKFS_EXT4_PROG $SCRATCH_OPTIONS $mkfs_opts"
+}
+
+_scratch_mkfs_ext4()
+{
+       local mkfs_cmd="`_scratch_mkfs_ext4_opts`"
+       local mkfs_filter="grep -v -e ^Warning: -e \"^mke2fs \" | grep -v \"^$\""
+       local tmp=`mktemp -u`
+       local mkfs_status
+
+       if [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ]; then
+               $MKFS_EXT4_PROG -F -O journal_dev $MKFS_OPTIONS $* $SCRATCH_LOGDEV 2>$tmp.mkfserr 1>$tmp.mkfsstd
+               mkjournal_status=$?
+
+               if [ $mkjournal_status -ne 0 ]; then
+                       cat $tmp.mkfsstd
+                       cat $tmp.mkfserr >&2
+                       return $mkjournal_status
+               fi
+       fi
+
+       _scratch_do_mkfs "$mkfs_cmd" "$mkfs_filter" $* 2>$tmp.mkfserr 1>$tmp.mkfsstd
+       mkfs_status=$?
+
+       if [ $mkfs_status -eq 0 -a "$LARGE_SCRATCH_DEV" = yes ]; then
+               # manually parse the mkfs output to get the fs size in bytes
+               local fs_size=`cat $tmp.mkfsstd | awk ' \
+                       /^Block size/ { split($2, a, "="); bs = a[2] ; } \
+                       / inodes, / { blks = $3 } \
+                       /reserved for the super user/ { resv = $1 } \
+                       END { fssize = bs * blks - resv; print fssize }'`
+
+               _setup_large_ext4_fs $fs_size
+               mkfs_status=$?
+       fi
+
+       # output mkfs stdout and stderr
+       cat $tmp.mkfsstd
+       cat $tmp.mkfserr >&2
+       rm -f $tmp.mkfserr $tmp.mkfsstd
+
+       return $mkfs_status
+}
+
+_ext4_metadump()
+{
+       local device="$1"
+       local dumpfile="$2"
+       local compressopt="$3"
+
+       test -n "$E2IMAGE_PROG" || _fail "e2image not installed"
+       $E2IMAGE_PROG -Q "$device" "$dumpfile"
+       [ "$compressopt" = "compress" ] && [ -n "$DUMP_COMPRESSOR" ] &&
+               $DUMP_COMPRESSOR -f "$dumpfile" &>> "$seqres.full"
+}
+
+_ext4_mdrestore()
+{
+       local metadump="$1"
+       local device="$2"
+       shift; shift
+       local options="$@"
+
+       # If we're configured for compressed dumps and there isn't already an
+       # uncompressed dump, see if we can use DUMP_COMPRESSOR to decompress
+       # something.
+       if [ ! -e "$metadump" ] && [ -n "$DUMP_COMPRESSOR" ]; then
+               for compr in "$metadump".*; do
+                       [ -e "$compr" ] && $DUMP_COMPRESSOR -d -f -k "$compr" && break
+               done
+       fi
+       test -r "$metadump" || return 1
+
+       $E2IMAGE_PROG $options -r "${metadump}" "${SCRATCH_DEV}"
+}
+
+# this test requires the ext4 kernel support crc feature on scratch device
+#
+_require_scratch_ext4_crc()
+{
+       _scratch_mkfs_ext4 >/dev/null 2>&1
+       dumpe2fs -h $SCRATCH_DEV 2> /dev/null | grep -q metadata_csum || _notrun "metadata_csum not supported by this filesystem"
+       _try_scratch_mount >/dev/null 2>&1 \
+          || _notrun "Kernel doesn't support metadata_csum feature"
+       _scratch_unmount
+}
+
+# Check whether the specified feature whether it is supported by
+# mkfs.ext4 and the kernel.
+_require_scratch_ext4_feature()
+{
+    if [ -z "$1" ]; then
+        echo "Usage: _require_scratch_ext4_feature feature"
+        exit 1
+    fi
+    $MKFS_EXT4_PROG -F $MKFS_OPTIONS -O "$1" \
+                   $SCRATCH_DEV 512m >/dev/null 2>&1 \
+       || _notrun "mkfs.ext4 doesn't support $1 feature"
+    _try_scratch_mount >/dev/null 2>&1 \
+       || _notrun "Kernel doesn't support the ext4 feature(s): $1"
+    _scratch_unmount
+}
+
+# Disable extent zeroing for ext4 on the given device
+_ext4_disable_extent_zeroout()
+{
+       local dev=${1:-$TEST_DEV}
+       local sdev=`_short_dev $dev`
+
+       [ -f /sys/fs/ext4/$sdev/extent_max_zeroout_kb ] && \
+               echo 0 >/sys/fs/ext4/$sdev/extent_max_zeroout_kb
+}
+
+_require_scratch_richacl_ext4()
+{
+       _scratch_mkfs -O richacl >/dev/null 2>&1 \
+               || _notrun "can't mkfs $FSTYP with option -O richacl"
+       _try_scratch_mount >/dev/null 2>&1 \
+               || _notrun "kernel doesn't support richacl feature on $FSTYP"
+       _scratch_unmount
+}
+
+_scratch_ext4_options()
+{
+       local type=$1
+       local log_opt=""
+
+       case $type in
+       mkfs)
+               SCRATCH_OPTIONS="$SCRATCH_OPTIONS -F"
+               log_opt="-J device=$SCRATCH_LOGDEV"
+               ;;
+       mount)
+               # As of kernel 5.19, the kernel mount option path parser only
+               # accepts direct paths to block devices--the final path
+               # component cannot be a symlink.
+               log_opt="-o journal_path=$(realpath -q "$SCRATCH_LOGDEV")"
+               ;;
+       esac
+       [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
+               SCRATCH_OPTIONS="$SCRATCH_OPTIONS ${log_opt}"
+}
+
+# Get the inode flags for a particular inode number
+_ext4_get_inum_iflags() {
+       local dev="$1"
+       local inumber="$2"
+
+       debugfs -R "stat <${inumber}>" "${dev}" 2> /dev/null | \
+                       sed -n 's/^.*Flags: \([0-9a-fx]*\).*$/\1/p'
+}
diff --git a/common/fail_make_request b/common/fail_make_request
new file mode 100644 (file)
index 0000000..b5370ba
--- /dev/null
@@ -0,0 +1,64 @@
+##/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# common functions for setting up and tearing down block device error injection
+
+_require_fail_make_request()
+{
+    [ -f "$DEBUGFS_MNT/fail_make_request/probability" ] \
+       || _notrun "$DEBUGFS_MNT/fail_make_request \
+ not found. Seems that CONFIG_FAULT_INJECTION_DEBUG_FS kernel config option not enabled"
+}
+
+_allow_fail_make_request()
+{
+    local prob="${1:-100}"
+    local times="${2:-9999999}"
+    local verbose="${3:-0}"
+
+    echo "Allow global fail_make_request feature"
+    echo "$prob" > $DEBUGFS_MNT/fail_make_request/probability
+    echo "$times" > $DEBUGFS_MNT/fail_make_request/times
+    echo "$verbose" > $DEBUGFS_MNT/fail_make_request/verbose
+}
+
+_disallow_fail_make_request()
+{
+    echo "Disallow global fail_make_request feature"
+    echo 0 > $DEBUGFS_MNT/fail_make_request/probability
+    echo 0 > $DEBUGFS_MNT/fail_make_request/times
+    echo 0 > $DEBUGFS_MNT/fail_make_request/verbose
+}
+
+_bdev_fail_make_request()
+{
+    local bdev="$1"
+    local status="$2"
+    local sysfs_bdev=$(_sysfs_dev $bdev)
+
+    echo " echo $status > $sysfs_bdev/make-it-fail" >> $seqres.full
+    echo "$status" > $sysfs_bdev/make-it-fail
+}
+
+_start_fail_scratch_dev()
+{
+    echo "Force SCRATCH_DEV device failure"
+
+    _prepare_for_eio_shutdown $SCRATCH_DEV
+    _bdev_fail_make_request $SCRATCH_DEV 1
+    [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
+        _bdev_fail_make_request $SCRATCH_LOGDEV 1
+    [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
+        _bdev_fail_make_request $SCRATCH_RTDEV 1
+}
+
+_stop_fail_scratch_dev()
+{
+    echo "Make SCRATCH_DEV device operable again"
+
+    [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
+        _bdev_fail_make_request $SCRATCH_RTDEV 0
+    [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
+        _bdev_fail_make_request $SCRATCH_LOGDEV 0
+    _bdev_fail_make_request $SCRATCH_DEV 0
+}
index a6a42b7a6ad25873fd374d59ea9ce1512ec9c6d3..36d51bd957dd53c3efb132908bc52a471920e8aa 100644 (file)
@@ -221,14 +221,14 @@ _filter_xfs_io_units_modified()
 
 _filter_xfs_io_blocks_modified()
 {
-       BLOCK_SIZE=$(_get_block_size $SCRATCH_MNT)
+       BLOCK_SIZE=$(_get_file_block_size $SCRATCH_MNT)
 
        _filter_xfs_io_units_modified "Block" $BLOCK_SIZE
 }
 
 _filter_xfs_io_pages_modified()
 {
-       PAGE_SIZE=$(get_page_size)
+       PAGE_SIZE=$(_get_page_size)
 
        _filter_xfs_io_units_modified "Page" $PAGE_SIZE
 }
@@ -293,7 +293,7 @@ _filter_project_quota()
        # until the GETNEXTQUOTA ioctl came into use.  Filter it out.
        # But if you specify a name for ID 0, that means you want to
        # deal with it by yourself, this function won't filter it out.
-       _filter_quota | grep -v "^\#0 \|^(null) "
+       _filter_quota | grep -v "^#0 \|^(null) "
 }
 
 # Account for different "ln" failure messages
@@ -347,7 +347,7 @@ _filter_size_to_bytes()
 # verbose output
 _filter_fstrim()
 {
-       egrep -o "[0-9]+ bytes" | $AWK_PROG '{print $1}'
+       grep -Eo "[0-9]+ bytes" | $AWK_PROG '{print $1}'
 }
 
 # Remove the ending dot appended to mount error message, util-linux 2.30
@@ -380,6 +380,8 @@ _filter_ending_dot()
 # ancient:        mount: cannot remount block device <device> read-write, is write-protected
 # prior to v2.30:  mount: cannot remount <device> read-write, is write-protected
 # v2.30 and later: mount: <mountpoint>: cannot remount <device> read-write, is write-protected.
+# v2.38 and later:
+# dmesg(1) may have more information after failed mount mount system call
 #
 # Now use _filter_ro_mount to unify all these differences across old & new
 # util-linux versions. So the filtered format would be:
@@ -412,7 +414,8 @@ _filter_ro_mount() {
                print "mount: cannot remount device read-write, is write-protected\n";
        } else {
                print "$_";
-       }' | _filter_ending_dot
+       }' | grep -v "dmesg(1) may have more information after failed mount" | \
+       _filter_ending_dot
 }
 
 # Filter a failed mount output due to EUCLEAN and USTALE, util-linux changed
@@ -424,6 +427,8 @@ _filter_ro_mount() {
 # mount: mount <device> on <mountpoint> failed: Structure needs cleaning
 # v2.30 and later:
 # mount: <mountpoint>: mount(2) system call failed: Structure needs cleaning.
+# v2.38 and later:
+# dmesg(1) may have more information after failed mount mount system call
 #
 # This is also true for ESTALE error. So let's remove all the changing parts
 # and keep the 'prior to v2.21' format:
@@ -431,7 +436,8 @@ _filter_ro_mount() {
 # mount: Stale file handle
 _filter_error_mount()
 {
-       sed -e "s/mount:\(.*failed:\)/mount:/" | _filter_ending_dot
+       grep -v "dmesg(1) may have more information after failed mount" | \
+               sed -e "s/mount:\(.*failed:\)/mount:/" | _filter_ending_dot
 }
 
 # Similar to _filter_error_mount, filter a busy mount output.
@@ -440,15 +446,18 @@ _filter_error_mount()
 # old: mount: <device> is already mounted or <mountpoint> busy
 # new: mount: <mountpoint>: <device> already mounted or mount point busy.
 # filtered: mount: device already mounted or mount point busy
+# v2.38 and later, filter out:
+# dmesg(1) may have more information after failed mount mount system call
 _filter_busy_mount()
 {
+       grep -v "dmesg(1) may have more information after failed mount" | \
        sed -e "s/.*: .* already mounted or .* busy/mount: device already mounted or mount point busy/" | \
                _filter_ending_dot
 }
 
 _filter_od()
 {
-       BLOCK_SIZE=$(_get_block_size $SCRATCH_MNT)
+       BLOCK_SIZE=$(_get_file_block_size $SCRATCH_MNT)
        $AWK_PROG -v block_size=$BLOCK_SIZE '
                /^[0-9]+/ {
                        offset = strtonum("0"$1);
@@ -642,17 +651,9 @@ _filter_bash()
        sed -e "s/^bash: line 1: /bash: /"
 }
 
-#
-# blkzone report added zone capacity to be printed from v2.37.
-# This filter will add an extra column 'cap' with the same value of
-# 'len'(zone size) for blkzone version < 2.37
-#
-# Before: start: 0x000100000, len 0x040000, wptr 0x000000 ..
-# After: start: 0x000100000, len 0x040000, cap 0x040000, wptr 0x000000 ..
-_filter_blkzone_report()
+_filter_trailing_whitespace()
 {
-       $AWK_PROG -F "," 'BEGIN{OFS=",";} $3 !~ /cap/ {$2=$2","$2;} {print;}' |\
-       sed -e 's/len/cap/2'
+       sed -E -e "s/\s+$//"
 }
 
 # make sure this script returns success
index d4169cc69112588d74a0fd3a8362c6cc35c8060a..9ef9676175c9d26a89725bca181c3fa95597ab26 100644 (file)
@@ -35,7 +35,18 @@ _filter_btrfs_filesystem_show()
        _filter_size | _filter_btrfs_version | _filter_devid | \
        _filter_zero_size | \
        sed -e "s/\(Total devices\) $NUMDEVS/\1 $NUM_SUBST/g" | \
-       uniq
+       uniq > $tmp.btrfs_filesystem_show
+
+       # The first two lines are Label/UUID and total devices
+       head -n 2 $tmp.btrfs_filesystem_show
+
+       # The remaining is the device list, first filter out the detailed
+       # missing device to the generic one.
+       # Then sort and uniq the result
+       tail -n +3 $tmp.btrfs_filesystem_show | \
+       sed -e "s/devid <DEVID> size 0 used 0 path  MISSING/*** Some devices missing/" | \
+       sort -r | uniq
+       rm -f $tmp.btrfs_filesystem_show
 }
 
 # This eliminates all numbers, and shows only unique lines,
@@ -58,8 +69,9 @@ _filter_btrfs_device_stats()
 }
 
 _filter_transaction_commit() {
-       sed -e "/Transaction commit: none (default)/d" | \
-       sed -e "s/Delete subvolume (.*commit):/Delete subvolume/g"
+       sed -e "/Transaction commit: none (default)/d" \
+           -e "s/Delete subvolume [0-9]\+ (.*commit):/Delete subvolume/g" \
+           -e "s/Delete subvolume (.*commit):/Delete subvolume/g"
 }
 
 _filter_btrfs_subvol_delete()
@@ -114,5 +126,35 @@ _filter_btrfs_cloner_error()
        sed -e "s/\(clone failed:\) Operation not supported/\1 Invalid argument/g"
 }
 
+# filter output of "btrfs inspect-internal dump-tree -t raid-stripe"
+_filter_stripe_tree()
+{
+       _filter_trailing_whitespace | _filter_btrfs_version |\
+       sed -E -e "s/leaf [0-9]+ items [0-9]+ free space [0-9]+ generation [0-9]+ owner RAID_STRIPE_TREE/leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE/" \
+               -e "s/leaf [0-9]+ flags 0x1\(WRITTEN\) backref revision 1/leaf XXXXXXXXX flags 0x1\(WRITTEN\) backref revision 1/" \
+               -e "s/checksum stored [0-9a-f]+/checksum stored <CHECKSUM>/"  \
+               -e "s/checksum calced [0-9a-f]+/checksum calced <CHECKSUM>/"  \
+               -e "s/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/<UUID>/" \
+               -e "s/item ([0-9]+) key \([0-9]+ RAID_STRIPE ([0-9]+)\) itemoff [0-9]+ itemsize ([0-9]+)/item \1 key \(XXXXXX RAID_STRIPE \2\) itemoff XXXXX itemsize \3/" \
+               -e "s/stripe ([0-9]+) devid ([0-9]+) physical [0-9]+/stripe \1 devid \2 physical XXXXXXXXX/" \
+               -e "s/total bytes [0-9]+/total bytes XXXXXXXX/" \
+               -e "s/bytes used [0-9]+/bytes used XXXXXX/"
+}
+
+# filter output of "btrfs balance start -[smd] convert
+_filter_balance_convert()
+{
+       _filter_scratch | \
+       sed -e "s/relocate [0-9]\+ out of [0-9]\+ chunks/relocate X out of X chunks/g"
+}
+
+# filter output of "btrfs device add"
+_filter_device_add()
+{
+       _filter_scratch | _filter_scratch_pool | \
+       sed -e "/Resetting device zones SCRATCH_DEV ([0-9]\+/d"
+
+}
+
 # make sure this script returns success
 /bin/true
index 809dee5465c2d16df903b8004cd240219baf56f4..218fe165438619a5ad0e842cb40fde7838eb0b50 100644 (file)
@@ -6,30 +6,20 @@
 
 # Modify various files after a fuzzing operation
 _scratch_fuzz_modify() {
-       nr="$1"
-
-       test -z "${nr}" && nr=50000
-       echo "+++ touch ${nr} files"
-       blk_sz=$(stat -f -c '%s' ${SCRATCH_MNT})
-       $XFS_IO_PROG -f -c "pwrite -S 0x63 0 ${blk_sz}" "/tmp/afile" > /dev/null
-       date="$(date)"
-       find "${SCRATCH_MNT}/" -type f 2> /dev/null | head -n "${nr}" | while read f; do
-               # try to remove append, immutable (and even dax) flag if exists
-               $XFS_IO_PROG -rc 'chattr -x -i -a' "$f" > /dev/null 2>&1
-               setfattr -n "user.date" -v "${date}" "$f"
-               cat "/tmp/afile" >> "$f"
-               mv "$f" "$f.longer"
-       done
-       sync
-       rm -rf "/tmp/afile"
-
-       echo "+++ create files"
-       mkdir -p "${SCRATCH_MNT}/test.moo"
-       $XFS_IO_PROG -f -c 'pwrite -S 0x80 0 65536' "${SCRATCH_MNT}/test.moo/urk" > /dev/null
-       sync
+       echo "+++ stressing filesystem"
+       mkdir -p $SCRATCH_MNT/data
+       [ "$FSTYP" == "xfs" ] && _xfs_force_bdev data $SCRATCH_MNT/data
+       $FSSTRESS_PROG -n $((TIME_FACTOR * 10000)) -p $((LOAD_FACTOR * 4)) -d $SCRATCH_MNT/data
 
-       echo "+++ remove files"
-       rm -rf "${SCRATCH_MNT}/test.moo"
+       if [ "$FSTYP" = "xfs" ]; then
+               if _xfs_has_feature "$SCRATCH_MNT" realtime; then
+                       mkdir -p $SCRATCH_MNT/rt
+                       _xfs_force_bdev realtime $SCRATCH_MNT/rt
+                       $FSSTRESS_PROG -n $((TIME_FACTOR * 10000)) -p $((LOAD_FACTOR * 4)) -d $SCRATCH_MNT/rt
+               else
+                       echo "+++ xfs realtime not configured"
+               fi
+       fi
 }
 
 # Try to access files after fuzzing
@@ -65,12 +55,74 @@ _scratch_scrub() {
        esac
 }
 
-# Filter out any keys with an array index >= 10, collapse any array range
-# ("[1-195]") to the first item, and ignore padding fields.
-__filter_xfs_db_keys() {
-       sed -e '/\([a-z]*\)\[\([0-9][0-9]\+\)\].*/d' \
-           -e 's/\([a-zA-Z0-9_]*\)\[\([0-9]*\)-[0-9]*\]/\1[\2]/g' \
-           -e '/pad/d'
+# Expand indexed keys (i.e. arrays) into a long format so that we can filter
+# the array indices individually, and pass regular keys right through.
+#
+# For example, "u3.bmx[0-1] = [foo,bar]" is exploded into:
+# u3.bmx[0] = [foo,bar]
+# u3.bmx[1] = [foo,bar]
+#
+# Note that we restrict array indices to [0-9] to reduce fuzz runtime.  The
+# minimum and maximum array indices can be changed by setting the variables
+# SCRATCH_XFS_{MIN,MAX}_ARRAY_IDX.
+#
+# Also filter padding fields.
+__explode_xfs_db_fields() {
+       local min_idx="${SCRATCH_XFS_MIN_ARRAY_IDX}"
+       local max_idx="${SCRATCH_XFS_MAX_ARRAY_IDX}"
+
+       test -z "${min_idx}" && min_idx=0
+       test -z "${max_idx}" && max_idx=9
+       test "${max_idx}" = "none" && max_idx=99999
+
+       grep ' = ' | \
+       sed -e 's/^\([.a-zA-Z0-9_]*\)\[\([0-9]*\)-\([0-9]*\)\]\(.*\) = \(.*\)$/\1[%d]\4 \2 \3 = \5/g' \
+           -e 's/^\([.a-zA-Z0-9_]*\)\[\([0-9]*\)\]\(.*\) = \(.*\)$/\1[%d]\3 \2 \2 = \4/g' | \
+       while read name col1 col2 rest; do
+               if [[ "${name}" == *pad* ]]; then
+                       continue
+               fi
+
+               if [ "${col1}" = "=" ]; then
+                       echo "${name} ${col1} ${col2} ${rest}"
+                       continue
+               fi
+
+               test "${min_idx}" -gt "${col1}" && col1="${min_idx}"
+               test "${max_idx}" -lt "${col2}" && col2="${max_idx}"
+
+               seq "${col1}" "${col2}" | while read idx; do
+                       printf "${name} %s\n" "${idx}" "${rest}"
+               done
+       done
+}
+
+# Filter out metadata fields that are completely controlled by userspace
+# or are arbitrary bit sequences.  In other words, fields where the filesystem
+# does no validation.
+__filter_unvalidated_xfs_db_fields() {
+       sed -e '/\.sec/d' \
+           -e '/\.nsec/d' \
+           -e '/^lsn$/d' \
+           -e '/\.lsn/d' \
+           -e '/^core.flushiter/d' \
+           -e '/^core.dmevmask/d' \
+           -e '/^core.dmstate/d' \
+           -e '/^core.gen/d' \
+           -e '/^core.prealloc/d' \
+           -e '/^core.immutable/d' \
+           -e '/^core.append/d' \
+           -e '/^core.sync/d' \
+           -e '/^core.noatime/d' \
+           -e '/^core.nodump/d' \
+           -e '/^core.nodefrag/d' \
+           -e '/^v3.dax/d' \
+           -e '/^nvlist.*value/d' \
+           -e '/^entries.*root/d' \
+           -e '/^entries.*secure/d' \
+           -e '/^a.sfattr.list.*value/d' \
+           -e '/^a.sfattr.list.*root/d' \
+           -e '/^a.sfattr.list.*secure/d'
 }
 
 # Filter the xfs_db print command's field debug information
@@ -80,22 +132,38 @@ __filter_xfs_db_print_fields() {
        if [ -z "${filter}" ] || [ "${filter}" = "nofilter" ]; then
                filter='^'
        fi
-       grep ' = ' | while read key equals value; do
-               fuzzkey="$(echo "${key}" | __filter_xfs_db_keys)"
+       __explode_xfs_db_fields | while read key equals value; do
+               fuzzkey="$(echo "${key}")"
                if [ -z "${fuzzkey}" ]; then
                        continue
                elif [[ "${value}" == "["* ]]; then
                        echo "${value}" | sed -e 's/^.//g' -e 's/.$//g' -e 's/,/\n/g' | while read subfield; do
                                echo "${fuzzkey}.${subfield}"
-                       done | __filter_xfs_db_keys
+                       done
                else
                        echo "${fuzzkey}"
                fi
-       done | egrep "${filter}"
+       done | grep -E "${filter}" | __filter_unvalidated_xfs_db_fields
+}
+
+# Dump the current contents of a metadata object.
+# All arguments are xfs_db commands to locate the metadata.
+_scratch_xfs_dump_metadata() {
+       local cmds=()
+       for arg in "$@"; do
+               cmds+=("-c" "${arg}")
+       done
+       _scratch_xfs_db "${cmds[@]}" -c print
+}
+
+# Decide from the output of the xfs_db "stack" command if the debugger's io
+# cursor is pointed at a block that is an unstructured data format (blob).
+__scratch_xfs_detect_blob_from_stack() {
+       grep -q -E 'inode.*, type (data|rtsummary|rtbitmap)'
 }
 
 # Navigate to some part of the filesystem and print the field info.
-# The first argument is an egrep filter for the fields
+# The first argument is an grep filter for the fields
 # The rest of the arguments are xfs_db commands to locate the metadata.
 _scratch_xfs_list_metadata_fields() {
        filter="$1"
@@ -109,7 +177,17 @@ _scratch_xfs_list_metadata_fields() {
        for arg in "$@"; do
                cmds+=("-c" "${arg}")
        done
-       _scratch_xfs_db "${cmds[@]}" -c print | __filter_xfs_db_print_fields "${filter}"
+
+       # Does the path argument point towards something that is an
+       # unstructured blob?
+       if _scratch_xfs_db "${cmds[@]}" -c stack 2>/dev/null | \
+                       __scratch_xfs_detect_blob_from_stack; then
+               echo "<blob>"
+               return
+       fi
+
+       _scratch_xfs_db "${cmds[@]}" -c print | \
+               __filter_xfs_db_print_fields "${filter}"
 }
 
 # Fuzz a metadata field
@@ -147,6 +225,70 @@ _scratch_xfs_fuzz_metadata_field() {
        return 0
 }
 
+# List the fuzzing verbs available for unstructured blobs
+__scratch_xfs_list_blob_fuzz_verbs() {
+               cat << ENDL
+zeroes
+ones
+firstbit
+middlebit
+lastbit
+random
+ENDL
+}
+
+# Fuzz a metadata blob
+# The first arg is a blob fuzzing verb
+# The rest of the arguments are xfs_db commands to find the metadata.
+_scratch_xfs_fuzz_metadata_blob() {
+       local fuzzverb="$1"
+       shift
+       local trashcmd=(blocktrash -z)
+
+       local cmds=()
+       for arg in "$@"; do
+               cmds+=("-c" "${arg}")
+       done
+
+       local bytecount=$(_scratch_xfs_db "${cmds[@]}" -c "stack" | grep 'byte.*length' | awk '{print $5}')
+       local bitmax=$((bytecount * 8))
+
+       case "${fuzzverb}" in
+       "zeroes")
+               trashcmd+=(-0 -o 0 -x "${bitmax}" -y "${bitmax}");;
+       "ones")
+               trashcmd+=(-1 -o 0 -x "${bitmax}" -y "${bitmax}");;
+       "firstbit")
+               trashcmd+=(-2 -o 0 -x 1 -y 1);;
+       "middlebit")
+               trashcmd+=(-2 -o $((bitmax / 2)) -x 1 -y 1);;
+       "lastbit")
+               trashcmd+=(-2 -o "${bitmax}" -x 1 -y 1);;
+       "random")
+               trashcmd+=(-3 -o 0 -x "${bitmax}" -y "${bitmax}");;
+       *)
+               echo "Unknown blob fuzz verb \"${fuzzverb}\"."
+               return 1
+               ;;
+       esac
+
+       trashcmd="${trashcmd[@]}"
+       oldval="$(_scratch_xfs_get_metadata_field "" "$@")"
+       while true; do
+               _scratch_xfs_db -x "${cmds[@]}" -c "${trashcmd}"
+               echo
+               newval="$(_scratch_xfs_get_metadata_field "" "$@" 2> /dev/null)"
+               if [ "${fuzzverb}" != "random" ] || [ "${oldval}" != "${newval}" ]; then
+                       break;
+               fi
+       done
+       if [ "${oldval}" = "${newval}" ]; then
+               echo "Blob already set to new value, skipping test."
+               return 1
+       fi
+       return 0
+}
+
 # Try to forcibly unmount the scratch fs
 __scratch_xfs_fuzz_unmount()
 {
@@ -156,116 +298,337 @@ __scratch_xfs_fuzz_unmount()
 # Restore metadata to scratch device prior to field-fuzzing.
 __scratch_xfs_fuzz_mdrestore()
 {
-       test -e "${POPULATE_METADUMP}" || _fail "Need to set POPULATE_METADUMP"
-
        __scratch_xfs_fuzz_unmount
-       xfs_mdrestore "${POPULATE_METADUMP}" "${SCRATCH_DEV}"
+       _xfs_mdrestore "${POPULATE_METADUMP}" "${SCRATCH_DEV}" || \
+               _fail "${POPULATE_METADUMP}: Could not find metadump to restore?"
 }
 
 __fuzz_notify() {
-       echo "$@"
-       test -w /dev/ttyprintk && echo "$@" >> /dev/ttyprintk
+       echo '========================================'
+       echo "$*"
+       echo '========================================'
+       _kernlog "$*"
 }
 
-# Fuzz one field of some piece of metadata.
-# First arg is the field name
-# Second arg is the fuzz verb (ones, zeroes, random, add, sub...)
-# Third arg is the repair mode (online, offline, both, none)
-__scratch_xfs_fuzz_field_test() {
-       field="$1"
-       fuzzverb="$2"
-       repair="$3"
-       shift; shift; shift
+# Perform the online repair part of a fuzz test.
+__scratch_xfs_fuzz_field_online() {
+       local fuzz_action="$1"
 
-       # Set the new field value
-       __fuzz_notify "+ Fuzz ${field} = ${fuzzverb}"
-       echo "========================"
-       _scratch_xfs_fuzz_metadata_field "${field}" ${fuzzverb} "$@"
+       # Mount or else we can't do anything online
+       __fuzz_notify "+ Mount filesystem to try online repair"
+       _try_scratch_mount 2>&1
        res=$?
-       test $res -ne 0 && return
+       if [ $res -ne 0 ]; then
+               (>&2 echo "${fuzz_action}: mount failed ($res).")
+               return 1
+       fi
 
-       # Try to catch the error with scrub
-       echo "+ Try to catch the error"
-       _try_scratch_mount 2>&1
+       # Make sure online scrub will catch whatever we fuzzed
+       __fuzz_notify "++ Detect fuzzed field (online)"
+       _scratch_scrub -n -a 1 -e continue 2>&1
        res=$?
-       if [ $res -eq 0 ]; then
-               # Try an online scrub unless we're fuzzing ag 0's sb,
-               # which scrub doesn't know how to fix.
-               if [ "${repair}" != "none" ]; then
-                       echo "++ Online scrub"
-                       if [ "$1" != "sb 0" ]; then
-                               _scratch_scrub -n -a 1 -e continue 2>&1
-                               res=$?
-                               test $res -eq 0 && \
-                                       (>&2 echo "scrub didn't fail with ${field} = ${fuzzverb}.")
-                       fi
+       test $res -eq 0 && \
+               (>&2 echo "${fuzz_action}: online scrub didn't fail.")
+
+       # Does the health status report reflect the corruption?
+       if [ $res -ne 0 ]; then
+               __fuzz_notify "++ Detect fuzzed field ill-health report"
+               _check_xfs_health $SCRATCH_MNT 2>&1
+               res=$?
+               test $res -ne 1 && \
+                       (>&2 echo "${fuzz_action}: online health check failed ($res).")
+       fi
+
+       # Try fixing the filesystem online
+       __fuzz_notify "++ Try to repair filesystem (online)"
+       _scratch_scrub 2>&1
+       res=$?
+       test $res -ne 0 && \
+               (>&2 echo "${fuzz_action}: online repair failed ($res).")
+
+       # Online scrub should pass now
+       __fuzz_notify "++ Make sure error is gone (online)"
+       _scratch_scrub -n -a 1 -e continue 2>&1
+       res=$?
+       test $res -ne 0 && \
+               (>&2 echo "${fuzz_action}: online re-scrub failed ($res).")
+
+       __scratch_xfs_fuzz_unmount
+
+       # Offline scrub should pass now
+       __fuzz_notify "+ Make sure error is gone (offline)"
+       _scratch_xfs_repair -P -n 2>&1
+       res=$?
+       test $res -ne 0 && \
+               (>&2 echo "${fuzz_action}: offline re-scrub failed ($res).")
+
+       return 0
+}
+
+# Perform the offline repair part of a fuzz test.
+__scratch_xfs_fuzz_field_offline() {
+       local fuzz_action="$1"
+
+       # Make sure offline scrub will catch whatever we fuzzed
+       __fuzz_notify "+ Detect fuzzed field (offline)"
+       _scratch_xfs_repair -P -n 2>&1
+       res=$?
+       test $res -eq 0 && \
+               (>&2 echo "${fuzz_action}: offline scrub didn't fail.")
+
+       # Make sure xfs_repair catches at least as many things as the old
+       # xfs_check did.
+       if [ -n "${SCRATCH_XFS_FUZZ_CHECK}" ]; then
+               __fuzz_notify "+ Detect fuzzed field (xfs_check)"
+               _scratch_xfs_check 2>&1
+               res1=$?
+               if [ $res1 -ne 0 ] && [ $res -eq 0 ]; then
+                       (>&2 echo "${fuzz_action}: xfs_repair passed but xfs_check failed ($res1).")
                fi
+       fi
+
+       # Repair the filesystem offline
+       __fuzz_notify "+ Try to repair the filesystem (offline)"
+       _repair_scratch_fs -P 2>&1
+       res=$?
+       test $res -ne 0 && \
+               (>&2 echo "${fuzz_action}: offline repair failed ($res).")
+
+       # See if repair finds a clean fs
+       __fuzz_notify "+ Make sure error is gone (offline)"
+       _scratch_xfs_repair -P -n 2>&1
+       res=$?
+       test $res -ne 0 && \
+               (>&2 echo "${fuzz_action}: offline re-scrub failed ($res).")
+
+       return 0
+}
+
+# Perform the no-repair part of a fuzz test.
+__scratch_xfs_fuzz_field_norepair() {
+       local fuzz_action="$1"
+
+       # Make sure offline scrub will catch whatever we fuzzed
+       __fuzz_notify "+ Detect fuzzed field (offline)"
+       _scratch_xfs_repair -P -n 2>&1
+       res=$?
+       test $res -eq 0 && \
+               (>&2 echo "${fuzz_action}: offline scrub didn't fail.")
+
+       # Mount or else we can't do anything in norepair mode
+       __fuzz_notify "+ Mount filesystem to try online scan"
+       _try_scratch_mount 2>&1
+       res=$?
+       if [ $res -ne 0 ]; then
+               (>&2 echo "${fuzz_action}: mount failed ($res).")
+               return 1
+       fi
+
+       # Skip scrub and health check if scrub is not supported
+       if ! _supports_xfs_scrub $SCRATCH_MNT $SCRATCH_DEV; then
+               __scratch_xfs_fuzz_unmount
+               return 0
+       fi
+
+       # Make sure online scrub will catch whatever we fuzzed
+       __fuzz_notify "++ Detect fuzzed field (online)"
+       _scratch_scrub -n -a 1 -e continue 2>&1
+       res=$?
+       test $res -eq 0 && \
+               (>&2 echo "${fuzz_action}: online scrub didn't fail.")
+
+       # Does the health status report reflect the corruption?
+       if [ $res -ne 0 ]; then
+               __fuzz_notify "++ Detect fuzzed field ill-health report"
+               _check_xfs_health $SCRATCH_MNT 2>&1
+               res=$?
+               test $res -ne 1 && \
+                       (>&2 echo "${fuzz_action}: online health check failed ($res).")
+       fi
+
+       __scratch_xfs_fuzz_unmount
+
+       return 0
+}
+
+# Perform the online-then-offline repair part of a fuzz test.
+__scratch_xfs_fuzz_field_both() {
+       local fuzz_action="$1"
+
+       # Make sure offline scrub will catch whatever we fuzzed
+       __fuzz_notify "+ Detect fuzzed field (offline)"
+       _scratch_xfs_repair -P -n 2>&1
+       res=$?
+       test $res -eq 0 && \
+               (>&2 echo "${fuzz_action}: offline scrub didn't fail.")
 
-               # Try fixing the filesystem online?!
-               if [ "${repair}" = "online" ] || [ "${repair}" = "both" ]; then
-                       __fuzz_notify "++ Try to repair filesystem online"
-                       _scratch_scrub 2>&1
+       # Mount or else we can't do anything in both repair mode
+       __fuzz_notify "+ Mount filesystem to try both repairs"
+       _try_scratch_mount 2>&1
+       res=$?
+       if [ $res -ne 0 ]; then
+               (>&2 echo "${fuzz_action}: mount failed ($res).")
+       else
+               # Make sure online scrub will catch whatever we fuzzed
+               __fuzz_notify "++ Detect fuzzed field (online)"
+               _scratch_scrub -n -a 1 -e continue 2>&1
+               res=$?
+               test $res -eq 0 && \
+                       (>&2 echo "${fuzz_action}: online scrub didn't fail.")
+
+               # Does the health status report reflect the corruption?
+               if [ $res -ne 0 ]; then
+                       __fuzz_notify "++ Detect fuzzed field ill-health report"
+                       _check_xfs_health $SCRATCH_MNT 2>&1
                        res=$?
-                       test $res -ne 0 && \
-                               (>&2 echo "online repair failed ($res) with ${field} = ${fuzzverb}.")
+                       test $res -ne 1 && \
+                               (>&2 echo "${fuzz_action}: online health check failed ($res).")
                fi
 
+               # Try fixing the filesystem online
+               __fuzz_notify "++ Try to repair filesystem (online)"
+               _scratch_scrub 2>&1
+               res=$?
+               test $res -ne 0 && \
+                       (>&2 echo "${fuzz_action}: online repair failed ($res).")
+
                __scratch_xfs_fuzz_unmount
-       elif [ "${repair}" = "online" ] || [ "${repair}" = "both" ]; then
-               (>&2 echo "mount failed ($res) with ${field} = ${fuzzverb}.")
        fi
 
-       # Repair the filesystem offline?
-       if [ "${repair}" = "offline" ] || [ "${repair}" = "both" ]; then
-               echo "+ Try to repair the filesystem offline"
-               _repair_scratch_fs 2>&1
+       # Repair the filesystem offline if online repair failed?
+       if [ $res -ne 0 ]; then
+               __fuzz_notify "+ Try to repair the filesystem (offline)"
+               _repair_scratch_fs -P 2>&1
                res=$?
                test $res -ne 0 && \
-                       (>&2 echo "offline repair failed ($res) with ${field} = ${fuzzverb}.")
+                       (>&2 echo "${fuzz_action}: offline repair failed ($res).")
        fi
 
        # See if repair finds a clean fs
-       if [ "${repair}" != "none" ]; then
-               echo "+ Make sure error is gone (offline)"
-               _scratch_xfs_repair -n 2>&1
-               res=$?
-               test $res -ne 0 && \
-                       (>&2 echo "offline re-scrub ($res) with ${field} = ${fuzzverb}.")
+       __fuzz_notify "+ Make sure error is gone (offline)"
+       _scratch_xfs_repair -P -n 2>&1
+       res=$?
+       test $res -ne 0 && \
+               (>&2 echo "${fuzz_action}: offline re-scrub failed ($res).")
+
+       # Mount so that we can see what scrub says after we've fixed the fs
+       __fuzz_notify "+ Re-mount filesystem to re-try online scan"
+       _try_scratch_mount 2>&1
+       res=$?
+       if [ $res -ne 0 ]; then
+               (>&2 echo "${fuzz_action}: mount failed ($res).")
+               return 1
        fi
 
-       # See if scrub finds a clean fs
-       echo "+ Make sure error is gone (online)"
+       # Online scrub should pass now
+       __fuzz_notify "++ Make sure error is gone (online)"
+       _scratch_scrub -n -a 1 -e continue 2>&1
+       res=$?
+       test $res -ne 0 && \
+               (>&2 echo "${fuzz_action}: online re-scrub failed ($res).")
+
+       __scratch_xfs_fuzz_unmount
+
+       return 0
+}
+
+# Assess the state of the filesystem after a repair strategy has been run by
+# trying to make changes to it.
+_scratch_xfs_fuzz_field_modifyfs() {
+       local fuzz_action="$1"
+       local repair="$2"
+
+       # Try to mount the filesystem so that we can make changes
+       __fuzz_notify "+ Mount filesystem to make changes"
        _try_scratch_mount 2>&1
        res=$?
-       if [ $res -eq 0 ]; then
-               # Try an online scrub unless we're fuzzing ag 0's sb,
-               # which scrub doesn't know how to fix.
-               if [ "${repair}" != "none" ]; then
-                       echo "++ Online scrub"
-                       if [ "$1" != "sb 0" ]; then
-                               _scratch_scrub -n -e continue 2>&1
-                               res=$?
-                               test $res -ne 0 && \
-                                       (>&2 echo "online re-scrub ($res) with ${field} = ${fuzzverb}.")
-                       fi
-               fi
+       if [ $res -ne 0 ]; then
+               (>&2 echo "${fuzz_action}: pre-mod mount failed ($res).")
+               return $res
+       fi
+
+       # Try modifying the filesystem again
+       __fuzz_notify "++ Try to write filesystem again"
+       _scratch_fuzz_modify 2>&1
 
-               # Try modifying the filesystem again!
-               __fuzz_notify "++ Try to write filesystem again"
-               _scratch_fuzz_modify 100 2>&1
+       # If we didn't repair anything, there's no point in checking further,
+       # the fs is still corrupt.
+       if [ "${repair}" = "none" ]; then
                __scratch_xfs_fuzz_unmount
+               return 0
+       fi
+
+       # Run an online check to make sure the fs is still ok, unless we
+       # are running the norepair strategy.
+       __fuzz_notify "+ Re-check the filesystem (online)"
+       _scratch_scrub -n -e continue 2>&1
+       res=$?
+       test $res -ne 0 && \
+               (>&2 echo "${fuzz_action}: online post-mod scrub failed ($res).")
+
+       __scratch_xfs_fuzz_unmount
+
+       # Run an offline check to make sure the fs is still ok, unless we
+       # are running the norepair strategy.
+       __fuzz_notify "+ Re-check the filesystem (offline)"
+       _scratch_xfs_repair -P -n 2>&1
+       res=$?
+       test $res -ne 0 && \
+               (>&2 echo "${fuzz_action}: offline post-mod scrub failed ($res).")
+
+       return 0
+}
+
+# Fuzz one field of some piece of metadata.
+# First arg is the field name
+# Second arg is the fuzz verb (ones, zeroes, random, add, sub...)
+# Third arg is the repair mode (online, offline, both, none)
+__scratch_xfs_fuzz_field_test() {
+       field="$1"
+       fuzzverb="$2"
+       repair="$3"
+       shift; shift; shift
+
+       # Set the new field value
+       __fuzz_notify "+ Fuzz ${field} = ${fuzzverb}"
+       if [ "$field" = "<blob>" ]; then
+               _scratch_xfs_fuzz_metadata_blob ${fuzzverb} "$@"
        else
-               (>&2 echo "re-mount failed ($res) with ${field} = ${fuzzverb}.")
+               _scratch_xfs_fuzz_metadata_field "${field}" ${fuzzverb} "$@"
        fi
+       res=$?
+       test $res -ne 0 && return
 
-       # See if repair finds a clean fs
-       if [ "${repair}" != "none" ]; then
-               echo "+ Re-check the filesystem (offline)"
-               _scratch_xfs_repair -n 2>&1
+       # Try to catch the error with whatever repair strategy we picked.
+       # The fs should not be mounted before or after the strategy call.
+       local fuzz_action="${field} = ${fuzzverb}"
+       case "${repair}" in
+       "online")
+               __scratch_xfs_fuzz_field_online "${fuzz_action}"
                res=$?
-               test $res -ne 0 && \
-                       (>&2 echo "re-repair failed ($res) with ${field} = ${fuzzverb}.")
-       fi
+               ;;
+       "offline")
+               __scratch_xfs_fuzz_field_offline "${fuzz_action}"
+               res=$?
+               ;;
+       "none")
+               __scratch_xfs_fuzz_field_norepair "${fuzz_action}"
+               res=$?
+               ;;
+       "both")
+               __scratch_xfs_fuzz_field_both "${fuzz_action}"
+               res=$?
+               ;;
+       *)
+               (>&2 echo "unknown repair strategy ${repair}.")
+               res=2
+               ;;
+       esac
+       test $res -eq 0 || return $res
+
+       # See what happens when we modify the fs
+       _scratch_xfs_fuzz_field_modifyfs "${fuzz_action}" "${repair}"
+       return $?
 }
 
 # Make sure we have all the pieces we need for field fuzzing
@@ -278,18 +641,66 @@ _require_scratch_xfs_fuzz_fields()
        _require_xfs_db_command "fuzz"
 }
 
+# Sets the array SCRATCH_XFS_DIR_FUZZ_TYPES to the list of directory formats
+# available for fuzzing.  Each list item must match one of the /S_IFDIR.FMT_*
+# files created by the fs population code.  Users can override this by setting
+# SCRATCH_XFS_LIST_FUZZ_DIRTYPE in the environment.  BTREE is omitted here
+# because that refers to the fork format and does not affect the directory
+# structure at all.
+_scratch_xfs_set_dir_fuzz_types() {
+       if [ -n "${SCRATCH_XFS_LIST_FUZZ_DIRTYPE}" ]; then
+               mapfile -t SCRATCH_XFS_DIR_FUZZ_TYPES < \
+                               <(echo "${SCRATCH_XFS_LIST_FUZZ_DIRTYPE}" | tr '[ ,]' '[\n\n]')
+               return
+       fi
+
+       SCRATCH_XFS_DIR_FUZZ_TYPES=(BLOCK LEAF LEAFN NODE)
+}
+
+# Sets the array SCRATCH_XFS_XATTR_FUZZ_TYPES to the list of xattr formats
+# available for fuzzing.  Each list item must match one of the /ATTR.FMT_*
+# files created by the fs population code.  Users can override this by setting
+# SCRATCH_XFS_LIST_FUZZ_XATTRTYPE in the environment.  BTREE is omitted here
+# because that refers to the fork format and does not affect the extended
+# attribute structure at all.
+_scratch_xfs_set_xattr_fuzz_types() {
+       if [ -n "${SCRATCH_XFS_LIST_FUZZ_XATTRTYPE}" ]; then
+               mapfile -t SCRATCH_XFS_XATTR_FUZZ_TYPES < \
+                               <(echo "${SCRATCH_XFS_LIST_FUZZ_XATTRTYPE}" | tr '[ ,]' '[\n\n]')
+               return
+       fi
+
+       SCRATCH_XFS_XATTR_FUZZ_TYPES=(EXTENTS_REMOTE3K EXTENTS_REMOTE4K LEAF NODE)
+}
+
 # Grab the list of available fuzzing verbs
 _scratch_xfs_list_fuzz_verbs() {
        if [ -n "${SCRATCH_XFS_LIST_FUZZ_VERBS}" ]; then
                echo "${SCRATCH_XFS_LIST_FUZZ_VERBS}" | tr '[ ,]' '[\n\n]'
                return;
        fi
-       _scratch_xfs_db -x -c 'sb 0' -c 'fuzz' | grep '^Fuzz commands:' | \
-               sed -e 's/[,.]//g' -e 's/Fuzz commands: //g' -e 's/ /\n/g'
+
+       local cmds=()
+       for arg in "$@"; do
+               cmds+=("-c" "${arg}")
+       done
+       test "${#cmds[@]}" -eq 0 && cmds=('-c' 'sb 0')
+
+       # Does the path argument point towards something that is an
+       # unstructured blob?
+       if _scratch_xfs_db "${cmds[@]}" -c stack 2>/dev/null | \
+                       __scratch_xfs_detect_blob_from_stack; then
+               __scratch_xfs_list_blob_fuzz_verbs
+               return
+       fi
+
+       _scratch_xfs_db -x "${cmds[@]}" -c 'fuzz' | grep '^Fuzz commands:' | \
+               sed -e 's/[,.]//g' -e 's/Fuzz commands: //g' -e 's/ /\n/g' | \
+               grep -v '^random$'
 }
 
 # Fuzz some of the fields of some piece of metadata
-# The first argument is an egrep filter for the field names
+# The first argument is an grep filter for the field names
 # The second argument is the repair mode (online, offline, both)
 # The rest of the arguments are xfs_db commands to locate the metadata.
 #
@@ -301,19 +712,731 @@ _scratch_xfs_fuzz_metadata() {
        shift; shift
 
        fields="$(_scratch_xfs_list_metadata_fields "${filter}" "$@")"
-       verbs="$(_scratch_xfs_list_fuzz_verbs)"
-       echo "Fields we propose to fuzz under: $@"
+       verbs="$(_scratch_xfs_list_fuzz_verbs "$@")"
+       echo "Fields we propose to fuzz with the \"${repair}\" repair strategy: $@"
        echo $(echo "${fields}")
        echo "Verbs we propose to fuzz with:"
        echo $(echo "${verbs}")
+       echo "Current metadata object state:"
+       _scratch_xfs_dump_metadata "$@"
 
        # Always capture full core dumps from crashing tools
        ulimit -c unlimited
 
+       _xfs_skip_online_rebuild
+       _xfs_skip_offline_rebuild
+
        echo "${fields}" | while read field; do
                echo "${verbs}" | while read fuzzverb; do
                        __scratch_xfs_fuzz_mdrestore
                        __scratch_xfs_fuzz_field_test "${field}" "${fuzzverb}" "${repair}" "$@"
+
+                       # Collect compresssed coredumps in the test results
+                       # directory if the sysadmin didn't override the default
+                       # coredump strategy.
+                       for i in core core.*; do
+                               test -f "$i" || continue
+                               _save_coredump "$i"
+                       done
+               done
+       done
+}
+
+# Functions to race fsstress, fs freeze, and xfs metadata scrubbing against
+# each other to shake out bugs in xfs online repair.
+
+# Filter freeze and thaw loop output so that we don't tarnish the golden output
+# if the kernel temporarily won't let us freeze.
+__stress_freeze_filter_output() {
+       _filter_scratch | \
+               sed -e '/Device or resource busy/d' \
+                   -e '/Invalid argument/d'
+}
+
+# Filter scrub output so that we don't tarnish the golden output if the fs is
+# too busy to scrub.  Note: Tests should _notrun if the scrub type is not
+# supported.  Callers can provide extra strings to filter out as function
+# arguments.
+__stress_scrub_filter_output() {
+       local extra_args=()
+
+       for arg in "$@"; do
+               extra_args+=(-e "/${arg}/d")
+       done
+
+       _filter_scratch | \
+               sed -e '/Device or resource busy/d' \
+                   -e '/Optimization possible/d' \
+                   -e '/No space left on device/d' \
+                   "${extra_args[@]}"
+}
+
+# Decide if the scratch filesystem is still alive.
+__stress_scrub_scratch_alive() {
+       # If we can't stat the scratch filesystem, there's a reasonably good
+       # chance that the fs shut down, which is not good.
+       stat "$SCRATCH_MNT" &>/dev/null
+}
+
+# Decide if we want to keep running stress tests.  The first argument is the
+# stop time, and second argument is the path to the sentinel file.
+__stress_scrub_running() {
+       test -e "$2" && test "$(date +%s)" -lt "$1" && __stress_scrub_scratch_alive
+}
+
+# Run fs freeze and thaw in a tight loop.
+__stress_scrub_freeze_loop() {
+       local end="$1"
+       local runningfile="$2"
+
+       while __stress_scrub_running "$end" "$runningfile"; do
+               $XFS_IO_PROG -x -c 'freeze' -c 'thaw' $SCRATCH_MNT 2>&1 | \
+                       __stress_freeze_filter_output
+       done
+}
+
+# Run individual xfs_io commands in a tight loop.
+__stress_xfs_io_loop() {
+       local end="$1"
+       local runningfile="$2"
+       shift; shift
+
+       local xfs_io_args=()
+       for arg in "$@"; do
+               xfs_io_args+=('-c' "$arg")
+       done
+
+       while __stress_scrub_running "$end" "$runningfile"; do
+               $XFS_IO_PROG -x "${xfs_io_args[@]}" "$SCRATCH_MNT" \
+                               > /dev/null 2>> $seqres.full
+       done
+}
+
+# Run individual XFS online fsck commands in a tight loop with xfs_io.
+__stress_one_scrub_loop() {
+       local end="$1"
+       local runningfile="$2"
+       local scrub_tgt="$3"
+       local scrub_startat="$4"
+       local start_agno="$5"
+       shift; shift; shift; shift; shift
+       local agcount="$(_xfs_mount_agcount $SCRATCH_MNT)"
+
+       local xfs_io_args=()
+       for arg in "$@"; do
+               if [ -n "$SCRUBSTRESS_USE_FORCE_REBUILD" ]; then
+                       arg="$(echo "$arg" | sed -e 's/^repair/repair -R/g')"
+               fi
+               if echo "$arg" | grep -q -w '%agno%'; then
+                       # Substitute the AG number
+                       for ((agno = start_agno; agno < agcount; agno++)); do
+                               local ag_arg="$(echo "$arg" | sed -e "s|%agno%|$agno|g")"
+                               xfs_io_args+=('-c' "$ag_arg")
+                       done
+               else
+                       xfs_io_args+=('-c' "$arg")
+               fi
+       done
+
+       local extra_filters=()
+       case "$scrub_tgt" in
+       "%file%"|"%datafile%"|"%attrfile%")
+               extra_filters+=('No such file or directory' 'No such device or address')
+               ;;
+       "%dir%")
+               extra_filters+=('No such file or directory' 'Not a directory')
+               ;;
+       "%regfile%"|"%cowfile%")
+               extra_filters+=('No such file or directory')
+               ;;
+       esac
+
+       local target_cmd=(echo "$scrub_tgt")
+       case "$scrub_tgt" in
+       "%file%")       target_cmd=($here/src/xfsfind -q  "$SCRATCH_MNT");;
+       "%attrfile%")   target_cmd=($here/src/xfsfind -qa "$SCRATCH_MNT");;
+       "%datafile%")   target_cmd=($here/src/xfsfind -qb "$SCRATCH_MNT");;
+       "%dir%")        target_cmd=($here/src/xfsfind -qd "$SCRATCH_MNT");;
+       "%regfile%")    target_cmd=($here/src/xfsfind -qr "$SCRATCH_MNT");;
+       "%cowfile%")    target_cmd=($here/src/xfsfind -qs "$SCRATCH_MNT");;
+       esac
+
+       while __stress_scrub_running "$scrub_startat" "$runningfile"; do
+               sleep 1
+       done
+
+       while __stress_scrub_running "$end" "$runningfile"; do
+               readarray -t fnames < <("${target_cmd[@]}" 2>> $seqres.full)
+               for fname in "${fnames[@]}"; do
+                       $XFS_IO_PROG -x "${xfs_io_args[@]}" "$fname" 2>&1 | \
+                               __stress_scrub_filter_output "${extra_filters[@]}"
+                       __stress_scrub_running "$end" "$runningfile" || break
                done
        done
 }
+
+# Run xfs_scrub online fsck in a tight loop.
+__stress_xfs_scrub_loop() {
+       local end="$1"
+       local runningfile="$2"
+       local scrub_startat="$3"
+       shift; shift; shift
+       local sigint_ret="$(( $(kill -l SIGINT) + 128 ))"
+       local scrublog="$tmp.scrub"
+
+       while __stress_scrub_running "$scrub_startat" "$runningfile"; do
+               sleep 1
+       done
+
+       while __stress_scrub_running "$end" "$runningfile"; do
+               _scratch_scrub "$@" &> $scrublog
+               res=$?
+               if [ "$res" -eq "$sigint_ret" ]; then
+                       # Ignore SIGINT because the cleanup function sends
+                       # that to terminate xfs_scrub
+                       res=0
+               fi
+               echo "xfs_scrub exits with $res at $(date)" >> $seqres.full
+               if [ "$res" -ge 128 ]; then
+                       # Report scrub death due to fatal signals
+                       echo "xfs_scrub died with SIG$(kill -l $res)"
+                       cat $scrublog >> $seqres.full 2>/dev/null
+               elif [ "$((res & 0x1))" -gt 0 ]; then
+                       # Report uncorrected filesystem errors
+                       echo "xfs_scrub reports uncorrected errors:"
+                       grep -E '(Repair unsuccessful;|Corruption:)' $scrublog
+                       cat $scrublog >> $seqres.full 2>/dev/null
+               fi
+               rm -f $scrublog
+       done
+}
+
+# Clean the scratch filesystem between rounds of fsstress if there is 2%
+# available space or less because that isn't an interesting stress test.
+#
+# Returns 0 if we cleared anything, and 1 if we did nothing.
+__stress_scrub_clean_scratch() {
+       local used_pct="$(_used $SCRATCH_DEV)"
+
+       test "$used_pct" -lt 98 && return 1
+
+       echo "Clearing scratch fs at $(date)" >> $seqres.full
+       rm -r -f $SCRATCH_MNT/p*
+       return 0
+}
+
+# Run fsx while we're testing online fsck.
+__stress_scrub_fsx_loop() {
+       local end="$1"
+       local runningfile="$2"
+       local remount_period="$3"
+       local stress_tgt="$4"   # ignored
+       local focus=(-q -X)     # quiet, validate file contents
+
+       # As of November 2022, 2 million fsx ops should be enough to keep
+       # any filesystem busy for a couple of hours.
+       focus+=(-N 2000000)
+       focus+=(-o $((128000 * LOAD_FACTOR)) )
+       focus+=(-l $((600000 * LOAD_FACTOR)) )
+
+       local args="$FSX_AVOID ${focus[@]} ${SCRATCH_MNT}/fsx.$seq"
+       echo "Running $here/ltp/fsx $args" >> $seqres.full
+
+       if [ -n "$remount_period" ]; then
+               local mode="rw"
+               local rw_arg=""
+               while __stress_scrub_running "$end" "$runningfile"; do
+                       # Need to recheck running conditions if we cleared
+                       # anything.
+                       test "$mode" = "rw" && __stress_scrub_clean_scratch && continue
+
+                       timeout -s TERM "$remount_period" $here/ltp/fsx \
+                                       $args $rw_arg >> $seqres.full
+                       res=$?
+                       echo "$mode fsx exits with $res at $(date)" >> $seqres.full
+                       if [ "$res" -ne 0 ] && [ "$res" -ne 124 ]; then
+                               # Stop if fsstress returns error.  Mask off
+                               # the magic code 124 because that is how the
+                               # timeout(1) program communicates that we ran
+                               # out of time.
+                               break;
+                       fi
+                       if [ "$mode" = "rw" ]; then
+                               mode="ro"
+                               rw_arg="-t 0 -w 0 -FHzCIJBE0"
+                       else
+                               mode="rw"
+                               rw_arg=""
+                       fi
+
+                       # Try remounting until we get the result we wanted
+                       while ! _scratch_remount "$mode" &>/dev/null && \
+                             __stress_scrub_running "$end" "$runningfile"; do
+                               sleep 0.2
+                       done
+               done
+               rm -f "$runningfile"
+               return 0
+       fi
+
+       while __stress_scrub_running "$end" "$runningfile"; do
+               # Need to recheck running conditions if we cleared anything
+               __stress_scrub_clean_scratch && continue
+               $here/ltp/fsx $args >> $seqres.full
+               echo "fsx exits with $? at $(date)" >> $seqres.full
+       done
+       rm -f "$runningfile"
+}
+
+# Run fsstress while we're testing online fsck.
+__stress_scrub_fsstress_loop() {
+       local end="$1"
+       local runningfile="$2"
+       local remount_period="$3"
+       local stress_tgt="$4"
+       local focus=()
+
+       case "$stress_tgt" in
+       "parent")
+               focus+=('-z')
+
+               # Create a directory tree very gradually
+               for op in creat link mkdir; do
+                       focus+=('-f' "${op}=2")
+               done
+               focus+=('-f' 'unlink=1' '-f' 'rmdir=1')
+
+               # But do a lot of renames to cycle parent pointers
+               for op in rename rnoreplace rexchange; do
+                       focus+=('-f' "${op}=40")
+               done
+               ;;
+       "dir")
+               focus+=('-z')
+
+               # Create a directory tree rapidly
+               for op in creat link mkdir mknod symlink; do
+                       focus+=('-f' "${op}=8")
+               done
+               focus+=('-f' 'rmdir=2' '-f' 'unlink=8')
+
+               # Rename half as often
+               for op in rename rnoreplace rexchange; do
+                       focus+=('-f' "${op}=4")
+               done
+
+               # Read and sync occasionally
+               for op in getdents stat fsync; do
+                       focus+=('-f' "${op}=1")
+               done
+               ;;
+       "xattr")
+               focus+=('-z')
+
+               # Create a directory tree slowly
+               for op in creat ; do
+                       focus+=('-f' "${op}=2")
+               done
+               for op in unlink rmdir; do
+                       focus+=('-f' "${op}=1")
+               done
+
+               # Create xattrs rapidly
+               for op in attr_set setfattr; do
+                       focus+=('-f' "${op}=80")
+               done
+
+               # Remove xattrs 1/4 as quickly
+               for op in attr_remove removefattr; do
+                       focus+=('-f' "${op}=20")
+               done
+
+               # Read and sync occasionally
+               for op in listfattr getfattr fsync; do
+                       focus+=('-f' "${op}=10")
+               done
+               ;;
+       "writeonly")
+               # Only do things that cause filesystem writes
+               focus+=('-w')
+               ;;
+       "default")
+               # No new arguments
+               ;;
+       "symlink")
+               focus+=('-z')
+
+               # Only create, read, and delete symbolic links
+               focus+=('-f' 'symlink=4')
+               focus+=('-f' 'readlink=10')
+               focus+=('-f' 'unlink=1')
+               ;;
+       "mknod")
+               focus+=('-z')
+
+               # Only create and delete special files
+               focus+=('-f' 'mknod=4')
+               focus+=('-f' 'getdents=100')
+               focus+=('-f' 'unlink=1')
+               ;;
+       *)
+               echo "$stress_tgt: Unrecognized stress target, using defaults."
+               ;;
+       esac
+
+       # As of March 2022, 2 million fsstress ops should be enough to keep
+       # any filesystem busy for a couple of hours.
+       local args=$(_scale_fsstress_args -p 4 -d $SCRATCH_MNT -n 2000000 "${focus[@]}" $FSSTRESS_AVOID)
+       echo "Running $FSSTRESS_PROG $args" >> $seqres.full
+
+       if [ -n "$remount_period" ]; then
+               local mode="rw"
+               local rw_arg=""
+               while __stress_scrub_running "$end" "$runningfile"; do
+                       # Need to recheck running conditions if we cleared
+                       # anything.
+                       test "$mode" = "rw" && __stress_scrub_clean_scratch && continue
+
+                       timeout -s TERM "$remount_period" $FSSTRESS_PROG \
+                                       $args $rw_arg >> $seqres.full
+                       res=$?
+                       echo "$mode fsstress exits with $res at $(date)" >> $seqres.full
+                       if [ "$res" -ne 0 ] && [ "$res" -ne 124 ]; then
+                               # Stop if fsstress returns error.  Mask off
+                               # the magic code 124 because that is how the
+                               # timeout(1) program communicates that we ran
+                               # out of time.
+                               break;
+                       fi
+                       if [ "$mode" = "rw" ]; then
+                               mode="ro"
+                               rw_arg="-R"
+                       else
+                               mode="rw"
+                               rw_arg=""
+                       fi
+
+                       # Try remounting until we get the result we wanted
+                       while ! _scratch_remount "$mode" &>/dev/null && \
+                             __stress_scrub_running "$end" "$runningfile"; do
+                               sleep 0.2
+                       done
+               done
+               rm -f "$runningfile"
+               return 0
+       fi
+
+       while __stress_scrub_running "$end" "$runningfile"; do
+               # Need to recheck running conditions if we cleared anything
+               __stress_scrub_clean_scratch && continue
+               $FSSTRESS_PROG $args >> $seqres.full
+               echo "fsstress exits with $? at $(date)" >> $seqres.full
+       done
+       rm -f "$runningfile"
+}
+
+# Make sure we have everything we need to run stress and scrub
+_require_xfs_stress_scrub() {
+       _require_xfs_io_command "scrub"
+       _require_test_program "xfsfind"
+       _require_command "$KILLALL_PROG" killall
+       _require_freeze
+       command -v _filter_scratch &>/dev/null || \
+               _notrun 'xfs scrub stress test requires common/filter'
+}
+
+# Make sure that we can force repairs either by error injection or passing
+# FORCE_REBUILD via ioctl.
+__require_xfs_stress_force_rebuild() {
+       local output="$($XFS_IO_PROG -x -c 'repair -R probe' $SCRATCH_MNT 2>&1)"
+       test -z "$output" && return
+       _require_xfs_io_error_injection "force_repair"
+}
+
+# Make sure we have everything we need to run stress and online repair
+_require_xfs_stress_online_repair() {
+       _require_xfs_stress_scrub
+       _require_xfs_io_command "repair"
+       command -v _require_xfs_io_error_injection &>/dev/null || \
+               _notrun 'xfs repair stress test requires common/inject'
+       __require_xfs_stress_force_rebuild
+       _require_freeze
+}
+
+# Clean up after the loops in case they didn't do it themselves.
+_scratch_xfs_stress_scrub_cleanup() {
+       rm -f "$runningfile"
+       echo "Cleaning up scrub stress run at $(date)" >> $seqres.full
+
+       # Send SIGINT so that bash won't print a 'Terminated' message that
+       # distorts the golden output.
+       echo "Killing stressor processes at $(date)" >> $seqres.full
+       $KILLALL_PROG -INT xfs_io fsstress fsx xfs_scrub >> $seqres.full 2>&1
+
+       # Tests are not allowed to exit with the scratch fs frozen.  If we
+       # started a fs freeze/thaw background loop, wait for that loop to exit
+       # and then thaw the filesystem.  Cleanup for the freeze loop must be
+       # performed prior to waiting for the other children to avoid triggering
+       # a race condition that can hang fstests.
+       #
+       # If the xfs_io -c freeze process is asleep waiting for a write lock on
+       # s_umount or sb_write when the killall signal is delivered, it will
+       # not check for pending signals until after it has frozen the fs.  If
+       # even one thread of the stress test processes (xfs_io, fsstress, etc.)
+       # is waiting for read locks on sb_write when the killall signals are
+       # delivered, they will block in the kernel until someone thaws the fs,
+       # and the `wait' below will wait forever.
+       #
+       # Hence we issue the killall, wait for the freezer loop to exit, thaw
+       # the filesystem, and wait for the rest of the children.
+       if [ -n "$__SCRUB_STRESS_FREEZE_PID" ]; then
+               echo "Waiting for fs freezer $__SCRUB_STRESS_FREEZE_PID to exit at $(date)" >> $seqres.full
+               wait "$__SCRUB_STRESS_FREEZE_PID"
+
+               echo "Thawing filesystem at $(date)" >> $seqres.full
+               $XFS_IO_PROG -x -c 'thaw' $SCRATCH_MNT >> $seqres.full 2>&1
+               __SCRUB_STRESS_FREEZE_PID=""
+       fi
+
+       # Wait for the remaining children to exit.
+       echo "Waiting for children to exit at $(date)" >> $seqres.full
+       wait
+
+       # Ensure the scratch fs is also writable before we exit.
+       if [ -n "$__SCRUB_STRESS_REMOUNT_LOOP" ]; then
+               echo "Remounting rw at $(date)" >> $seqres.full
+               _scratch_remount rw >> $seqres.full 2>&1
+               __SCRUB_STRESS_REMOUNT_LOOP=""
+       fi
+
+       echo "Cleanup finished at $(date)" >> $seqres.full
+}
+
+# Make sure the provided scrub/repair commands actually work on the scratch
+# filesystem before we start running them in a loop.
+__stress_scrub_check_commands() {
+       local scrub_tgt="$1"
+       local start_agno="$2"
+       shift; shift
+
+       local cooked_tgt="$scrub_tgt"
+       case "$scrub_tgt" in
+       "%file%"|"%dir%")
+               cooked_tgt="$SCRATCH_MNT"
+               ;;
+       "%regfile%"|"%datafile%")
+               cooked_tgt="$SCRATCH_MNT/testfile"
+               echo test > "$cooked_tgt"
+               ;;
+       "%attrfile%")
+               cooked_tgt="$SCRATCH_MNT/testfile"
+               $XFS_IO_PROG -f -c 'pwrite -S 0x58 0 64k' "$cooked_tgt" &>/dev/null
+               attr -s attrname "$cooked_tgt" < "$cooked_tgt" &>/dev/null
+               ;;
+       "%cowfile%")
+               cooked_tgt="$SCRATCH_MNT/testfile"
+               $XFS_IO_PROG -f -c 'pwrite -S 0x58 0 128k' "$cooked_tgt" &>/dev/null
+               _cp_reflink "$cooked_tgt" "$cooked_tgt.1"
+               $XFS_IO_PROG -f -c 'pwrite -S 0x58 0 1' "$cooked_tgt.1" &>/dev/null
+               ;;
+       esac
+
+       for arg in "$@"; do
+               local cooked_arg="$arg"
+               if [ -n "$SCRUBSTRESS_USE_FORCE_REBUILD" ]; then
+                       cooked_arg="$(echo "$cooked_arg" | sed -e 's/^repair/repair -R/g')"
+               fi
+               cooked_arg="$(echo "$cooked_arg" | sed -e "s/%agno%/$start_agno/g")"
+               testio=`$XFS_IO_PROG -x -c "$cooked_arg" "$cooked_tgt" 2>&1`
+               echo $testio | grep -q "Unknown type" && \
+                       _notrun "xfs_io scrub subcommand support is missing"
+               echo $testio | grep -q "Inappropriate ioctl" && \
+                       _notrun "kernel scrub ioctl is missing"
+               echo $testio | grep -q "No such file or directory" && \
+                       _notrun "kernel does not know about: $arg"
+               echo $testio | grep -q "Operation not supported" && \
+                       _notrun "kernel does not support: $arg"
+       done
+}
+
+# Start scrub, freeze, and fsstress in background looping processes, and wait
+# for 30*TIME_FACTOR seconds to see if the filesystem goes down.  Callers
+# must call _scratch_xfs_stress_scrub_cleanup from their cleanup functions.
+#
+# Various options include:
+#
+# -a   For %agno% substitution, start with this AG instead of AG 0.
+# -f   Run a freeze/thaw loop while we're doing other things.  Defaults to
+#      disabled, unless XFS_SCRUB_STRESS_FREEZE is set.
+# -i   Pass this command to xfs_io to exercise something that is not scrub
+#      in a separate loop.  If zero -i options are specified, do not run.
+#      Callers must check each of these commands (via _require_xfs_io_command)
+#      before calling here.
+# -r   Run fsstress for this amount of time, then remount the fs ro or rw.
+#      The default is to run fsstress continuously with no remount, unless
+#      XFS_SCRUB_STRESS_REMOUNT_PERIOD is set.
+# -s   Pass this command to xfs_io to test scrub.  If zero -s options are
+#      specified, xfs_io will not be run.
+# -S   Pass this option to xfs_scrub.  If zero -S options are specified,
+#      xfs_scrub will not be run.  To select repair mode, pass '-k' or '-v'.
+# -t   Run online scrub against this file; $SCRATCH_MNT is the default.
+#      Special values are as follows:
+#
+#      %file%          all files
+#      %regfile%       regular files
+#      %dir%           direct
+#      %datafile%      regular files with data blocks
+#      %attrfile%      regular files with xattr blocks
+#      %cowfile%       regular files with shared blocks
+#
+#      File selection races with fsstress, so the selection is best-effort.
+# -w   Delay the start of the scrub/repair loop by this number of seconds.
+#      Defaults to no delay unless XFS_SCRUB_STRESS_DELAY is set.  This value
+#      will be clamped to ten seconds before the end time.
+# -x   Focus on this type of fsstress operation.  Possible values:
+#
+#       'dir': Grow the directory trees as much as possible.
+#       'xattr': Grow extended attributes in a small tree.
+#       'default': Run fsstress with default arguments.
+#       'writeonly': Only perform fs updates, no reads.
+#       'symlink': Only create symbolic links.
+#       'mknod': Only create special files.
+#       'parent': Focus on updating parent pointers
+#
+#       The default is 'default' unless XFS_SCRUB_STRESS_TARGET is set.
+# -X   Run this program to exercise the filesystem.  Currently supported
+#       options are 'fsx' and 'fsstress'.  The default is 'fsstress'.
+_scratch_xfs_stress_scrub() {
+       local one_scrub_args=()
+       local xfs_scrub_args=()
+       local scrub_tgt="$SCRATCH_MNT"
+       local runningfile="$tmp.fsstress"
+       local freeze="${XFS_SCRUB_STRESS_FREEZE}"
+       local scrub_delay="${XFS_SCRUB_STRESS_DELAY:--1}"
+       local exerciser="fsstress"
+       local io_args=()
+       local remount_period="${XFS_SCRUB_STRESS_REMOUNT_PERIOD}"
+       local stress_tgt="${XFS_SCRUB_STRESS_TARGET:-default}"
+       local start_agno=0
+
+       __SCRUB_STRESS_FREEZE_PID=""
+       __SCRUB_STRESS_REMOUNT_LOOP=""
+       rm -f "$runningfile"
+       touch "$runningfile"
+
+       OPTIND=1
+       while getopts "a:fi:r:s:S:t:w:x:X:" c; do
+               case "$c" in
+                       a) start_agno="$OPTARG";;
+                       f) freeze=yes;;
+                       i) io_args+=("$OPTARG");;
+                       r) remount_period="$OPTARG";;
+                       s) one_scrub_args+=("$OPTARG");;
+                       S) xfs_scrub_args+=("$OPTARG");;
+                       t) scrub_tgt="$OPTARG";;
+                       w) scrub_delay="$OPTARG";;
+                       x) stress_tgt="$OPTARG";;
+                       X) exerciser="$OPTARG";;
+                       *) return 1; ;;
+               esac
+       done
+
+       __stress_scrub_check_commands "$scrub_tgt" "$start_agno" \
+                       "${one_scrub_args[@]}"
+
+       if ! command -v "__stress_scrub_${exerciser}_loop" &>/dev/null; then
+               echo "${exerciser}: Unknown fs exercise program."
+               return 1
+       fi
+
+       if [ "${#xfs_scrub_args[@]}" -gt 0 ]; then
+               _scratch_scrub "${xfs_scrub_args[@]}" &> "$tmp.scrub"
+               res=$?
+               if [ $res -ne 0 ]; then
+                       echo "xfs_scrub ${xfs_scrub_args[@]} failed, err $res" >> $seqres.full
+                       cat "$tmp.scrub" >> $seqres.full
+                       rm -f "$tmp.scrub"
+                       _notrun 'scrub not supported on scratch filesystem'
+               fi
+               rm -f "$tmp.scrub"
+       fi
+
+       _xfs_skip_online_rebuild
+       _xfs_skip_offline_rebuild
+
+       local start="$(date +%s)"
+       local end
+       if [ -n "$SOAK_DURATION" ]; then
+               end="$((start + SOAK_DURATION))"
+       else
+               end="$((start + (30 * TIME_FACTOR) ))"
+       fi
+       local scrub_startat="$((start + scrub_delay))"
+       test "$scrub_startat" -gt "$((end - 10))" &&
+               scrub_startat="$((end - 10))"
+
+       echo "Loop started at $(date --date="@${start}")," \
+                  "ending at $(date --date="@${end}")" >> $seqres.full
+
+       if [ -n "$remount_period" ]; then
+               __SCRUB_STRESS_REMOUNT_LOOP="1"
+       fi
+
+       "__stress_scrub_${exerciser}_loop" "$end" "$runningfile" \
+                       "$remount_period" "$stress_tgt" &
+
+       if [ -n "$freeze" ]; then
+               __stress_scrub_freeze_loop "$end" "$runningfile" &
+               __SCRUB_STRESS_FREEZE_PID="$!"
+       fi
+
+       if [ "${#io_args[@]}" -gt 0 ]; then
+               __stress_xfs_io_loop "$end" "$runningfile" \
+                               "${io_args[@]}" &
+       fi
+
+       if [ "${#one_scrub_args[@]}" -gt 0 ]; then
+               __stress_one_scrub_loop "$end" "$runningfile" "$scrub_tgt" \
+                               "$scrub_startat" "$start_agno" \
+                               "${one_scrub_args[@]}" &
+       fi
+
+       if [ "${#xfs_scrub_args[@]}" -gt 0 ]; then
+               __stress_xfs_scrub_loop "$end" "$runningfile" "$scrub_startat" \
+                               "${xfs_scrub_args[@]}" &
+       fi
+
+       # Wait until the designated end time or fsstress dies, then kill all of
+       # our background processes.
+       while __stress_scrub_running "$end" "$runningfile"; do
+               sleep 1
+       done
+       _scratch_xfs_stress_scrub_cleanup
+
+       # Warn the user if we think the scratch filesystem went down.
+       __stress_scrub_scratch_alive || \
+               echo "Did the scratch filesystem die?"
+
+       echo "Loop finished at $(date)" >> $seqres.full
+}
+
+# Decide if we're going to force repairs either by error injection or passing
+# FORCE_REBUILD via ioctl.
+__scratch_xfs_stress_setup_force_rebuild() {
+       local output="$($XFS_IO_PROG -x -c 'repair -R probe' $SCRATCH_MNT 2>&1)"
+
+       if [ -z "$output" ]; then
+               SCRUBSTRESS_USE_FORCE_REBUILD=1
+               return
+       fi
+
+       $XFS_IO_PROG -x -c 'inject force_repair' $SCRATCH_MNT
+}
+
+# Start online repair, freeze, and fsstress in background looping processes,
+# and wait for 30*TIME_FACTOR seconds to see if the filesystem goes down.
+# Same requirements and arguments as _scratch_xfs_stress_scrub.
+_scratch_xfs_stress_online_repair() {
+       __scratch_xfs_stress_setup_force_rebuild
+       XFS_SCRUB_FORCE_REPAIR=1 _scratch_xfs_stress_scrub "$@"
+}
diff --git a/common/gcov b/common/gcov
new file mode 100644 (file)
index 0000000..b7e3ed5
--- /dev/null
@@ -0,0 +1,87 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle.  All Rights Reserved.
+#
+# Routines for capturing kernel code coverage reports
+
+GCOV_DIR=/sys/kernel/debug/gcov
+
+# Find the topmost directories of the .gcno directory hierarchy
+__gcov_find_topdirs() {
+       find "${GCOV_DIR}/" -name '*.gcno' -printf '%d|%h\n' | \
+               sort -g -k 1 | \
+               uniq | \
+               $AWK_PROG -F '|' 'BEGIN { x = -1 } { if (x < 0) x = $1; if ($1 == x) printf("%s\n", $2);}'
+}
+
+# Generate lcov html report from kernel gcov data if configured
+_gcov_generate_report() {
+       local output_dir="$1"
+       test -n "${output_dir}" || return
+
+       # Kernel support built in?
+       test -d "$GCOV_DIR" || return
+
+       readarray -t gcno_dirs < <(__gcov_find_topdirs)
+       test "${#gcno_dirs[@]}" -gt 0 || return
+
+       mkdir -p "${output_dir}/raw/"
+
+       # Collect raw coverage data from the kernel
+       readarray -t source_dirs < <(find "${GCOV_DIR}/" -mindepth 1 -maxdepth 1 -type d)
+       for dir in "${source_dirs[@]}"; do
+               cp -p -R -d -u "${dir}" "${output_dir}/raw/"
+       done
+
+       # If lcov is installed, use it to summarize the gcda data.
+       # If it is not installed, there's no point in going forward
+       command -v lcov > /dev/null || return
+       local lcov=(lcov --exclude 'include*' --capture)
+       lcov+=(--output-file "${output_dir}/gcov.report")
+       for d in "${gcno_dirs[@]}"; do
+               lcov+=(--directory "${d}")
+       done
+
+       # Generate a detailed HTML report from the summary
+       local gcov_start_time="$(date --date="${fstests_start_time:-now}")"
+       local genhtml=()
+       if command -v genhtml > /dev/null; then
+               genhtml+=(genhtml -o "${output_dir}/" "${output_dir}/gcov.report")
+               genhtml+=(--title "fstests on $(hostname -s) @ ${gcov_start_time}" --legend)
+       fi
+
+       # Try to convert the HTML report summary as text for easier grepping if
+       # there's an HTML renderer present
+       local totext=()
+       test "${#totext[@]}" -eq 0 && \
+               command -v lynx &>/dev/null && \
+               totext=(lynx -dump "${output_dir}/index.html" -width 120 -nonumbers -nolist)
+       test "${#totext[@]}" -eq 0 && \
+               command -v links &>/dev/null && \
+               totext=(links -dump "${output_dir}/index.html" -width 120)
+       test "${#totext[@]}" -eq 0 && \
+               command -v elinks &>/dev/null && \
+               totext=(elinks -dump "${output_dir}/index.html" --dump-width 120 --no-numbering --no-references)
+
+       # Analyze kernel data
+       "${lcov[@]}" > "${output_dir}/gcov.stdout" 2> "${output_dir}/gcov.stderr"
+       test "${#genhtml[@]}" -ne 0 && \
+               "${genhtml[@]}" >> "${output_dir}/gcov.stdout" 2>> "${output_dir}/gcov.stderr"
+       test "${#totext[@]}" -ne 0 && \
+               "${totext[@]}" > "${output_dir}/index.txt" 2>> "${output_dir}/gcov.stderr"
+}
+
+# Reset gcov usage data
+_gcov_reset() {
+       echo 1 > "${GCOV_DIR}/reset"
+}
+
+# If the caller wanted us to capture gcov reports but the kernel doesn't
+# support it, turn it off.
+_gcov_check_report_gcov() {
+       test -z "$REPORT_GCOV" && return 0
+       test -w "${GCOV_DIR}/reset" && return 0
+
+       unset REPORT_GCOV
+       return 1
+}
diff --git a/common/metadump b/common/metadump
new file mode 100644 (file)
index 0000000..3373edf
--- /dev/null
@@ -0,0 +1,152 @@
+##/bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2024 Oracle.  All Rights Reserved.
+#
+# Filesystem metadata dump testing functions.
+#
+
+# Set up environment variables for a metadump test.  Requires the test and
+# scratch devices.  Sets XFS_METADUMP_{FILE,IMG} and MAX_XFS_METADUMP_VERSION.
+_xfs_setup_verify_metadump()
+{
+       XFS_METADUMP_FILE="$TEST_DIR/${seq}_metadump"
+       XFS_METADUMP_IMG="$TEST_DIR/${seq}_image"
+       MAX_XFS_METADUMP_VERSION="$(_xfs_metadump_max_version)"
+
+       rm -f "$XFS_METADUMP_FILE" "$XFS_METADUMP_IMG"*
+}
+
+_xfs_cleanup_verify_metadump()
+{
+       local img
+
+       _scratch_unmount &>> $seqres.full
+
+       test -n "$XFS_METADUMP_FILE" && rm -f "$XFS_METADUMP_FILE"
+
+       if [ -n "$XFS_METADUMP_IMG" ]; then
+               losetup -n -a -O BACK-FILE,NAME | grep "^$XFS_METADUMP_IMG" | while read backing ldev; do
+                       losetup -d "$ldev"
+               done
+
+               # Don't call rm directly with a globbed argument here to avoid
+               # issues issues with variable expansions.
+               for img in "$XFS_METADUMP_IMG"*; do
+                       test -e "$img" && rm -f "$img"
+               done
+       fi
+}
+
+# Can xfs_metadump snapshot the fs metadata to a v1 metadump file?
+_scratch_xfs_can_metadump_v1()
+{
+       # metadump v1 does not support log devices
+       [ "$USE_EXTERNAL" = yes ] && [ -n "$SCRATCH_LOGDEV" ] && return 1
+
+       # metadump v1 does not support realtime devices
+       [ "$USE_EXTERNAL" = yes ] && [ -n "$SCRATCH_RTDEV" ] && return 1
+
+       return 0
+}
+
+# Can xfs_metadump snapshot the fs metadata to a v2 metadump file?
+_scratch_xfs_can_metadump_v2()
+{
+       test "$MAX_XFS_METADUMP_VERSION" -ge 2
+}
+
+# Create a metadump in v1 format, restore it to fs image files, then mount the
+# images and fsck them.
+_xfs_verify_metadump_v1()
+{
+       local metadump_args="$1"
+       local extra_test="$2"
+
+       local metadump_file="$XFS_METADUMP_FILE"
+       local version=""
+       local data_img="$XFS_METADUMP_IMG.data"
+       local data_loop
+
+       # Force v1 if we detect v2 support
+       if [[ $MAX_XFS_METADUMP_FORMAT > 1 ]]; then
+               version="-v 1"
+       fi
+
+       # Capture metadump, which creates metadump_file
+       _scratch_xfs_metadump $metadump_file $metadump_args $version
+
+       # Restore metadump, which creates data_img
+       SCRATCH_DEV=$data_img _scratch_xfs_mdrestore $metadump_file
+
+       # Create loopdev for data device so we can mount the fs
+       data_loop=$(_create_loop_device $data_img)
+
+       # Mount fs, run an extra test, fsck, and unmount
+       SCRATCH_DEV=$data_loop _scratch_mount
+       if [ -n "$extra_test" ]; then
+               SCRATCH_DEV=$data_loop $extra_test
+       fi
+       SCRATCH_DEV=$data_loop _check_xfs_scratch_fs
+       SCRATCH_DEV=$data_loop _scratch_unmount
+
+       # Tear down what we created
+       _destroy_loop_device $data_loop
+       rm -f $data_img
+}
+
+# Create a metadump in v2 format, restore it to fs image files, then mount the
+# images and fsck them.
+_xfs_verify_metadump_v2()
+{
+       local metadump_args="$1"
+       local extra_test="$2"
+
+       local metadump_file="$XFS_METADUMP_FILE"
+       local version="-v 2"
+       local data_img="$XFS_METADUMP_IMG.data"
+       local data_loop
+       local log_img=""
+       local log_loop
+
+       # Capture metadump, which creates metadump_file
+       _scratch_xfs_metadump $metadump_file $metadump_args $version
+
+       #
+       # Metadump v2 files can contain contents dumped from an external log
+       # device. Use a temporary file to hold the log device contents restored
+       # from such a metadump file.
+       test -n "$SCRATCH_LOGDEV" && log_img="$XFS_METADUMP_IMG.log"
+
+       # Restore metadump, which creates data_img and log_img
+       SCRATCH_DEV=$data_img SCRATCH_LOGDEV=$log_img \
+               _scratch_xfs_mdrestore $metadump_file
+
+       # Create loopdev for data device so we can mount the fs
+       data_loop=$(_create_loop_device $data_img)
+
+       # Create loopdev for log device if we recovered anything
+       test -s "$log_img" && log_loop=$(_create_loop_device $log_img)
+
+       # Mount fs, run an extra test, fsck, and unmount
+       SCRATCH_DEV=$data_loop SCRATCH_LOGDEV=$log_loop _scratch_mount
+       if [ -n "$extra_test" ]; then
+               SCRATCH_DEV=$data_loop SCRATCH_LOGDEV=$log_loop $extra_test
+       fi
+       SCRATCH_DEV=$data_loop SCRATCH_LOGDEV=$log_loop _check_xfs_scratch_fs
+       SCRATCH_DEV=$data_loop _scratch_unmount
+
+       # Tear down what we created
+       if [ -b "$log_loop" ]; then
+               _destroy_loop_device $log_loop
+               rm -f $log_img
+       fi
+       _destroy_loop_device $data_loop
+       rm -f $data_img
+}
+
+# Verify both metadump formats if possible
+_xfs_verify_metadumps()
+{
+       _scratch_xfs_can_metadump_v1 && _xfs_verify_metadump_v1 "$@"
+       _scratch_xfs_can_metadump_v2 && _xfs_verify_metadump_v2 "$@"
+}
index 6efab71d348e8ad9c49313c04e74a31f3d49ac24..a8d5f492d3f4164ec8d12600c5cc124515523a6e 100644 (file)
@@ -48,12 +48,15 @@ _require_loadable_module()
        modprobe "${module}" || _notrun "${module} load failed"
 }
 
-# Check that the module for FSTYP can be loaded.
-_require_loadable_fs_module()
+# Test if the module for FSTYP can be unloaded and reloaded.
+#
+# If not, returns 1 if $FSTYP is not a loadable module; 2 if the module could
+# not be unloaded; or 3 if loading the module fails.
+_test_loadable_fs_module()
 {
        local module="$1"
 
-       modinfo "${module}" > /dev/null 2>&1 || _notrun "${module}: must be a module."
+       modinfo "${module}" > /dev/null 2>&1 || return 1
 
        # Unload test fs, try to reload module, remount
        local had_testfs=""
@@ -68,8 +71,28 @@ _require_loadable_fs_module()
        modprobe "${module}" || load_ok=0
        test -n "${had_scratchfs}" && _scratch_mount 2> /dev/null
        test -n "${had_testfs}" && _test_mount 2> /dev/null
-       test -z "${unload_ok}" || _notrun "Require module ${module} to be unloadable"
-       test -z "${load_ok}" || _notrun "${module} load failed"
+       test -z "${unload_ok}" || return 2
+       test -z "${load_ok}" || return 3
+       return 0
+}
+
+_require_loadable_fs_module()
+{
+       local module="$1"
+
+       _test_loadable_fs_module "${module}"
+       ret=$?
+       case "$ret" in
+       1)
+               _notrun "${module}: must be a module."
+               ;;
+       2)
+               _notrun "${module}: module could not be unloaded"
+               ;;
+       3)
+               _notrun "${module}: module reload failed"
+               ;;
+       esac
 }
 
 # Print the value of a filesystem module parameter
index c4e7ee5896b569e7f46ae197b39c4e88ee0141e1..faa9339a6477f71c30ca8a0e7e9f9afcbedf9e0e 100644 (file)
@@ -12,16 +12,30 @@ export OVL_XATTR_NLINK="trusted.overlay.nlink"
 export OVL_XATTR_UPPER="trusted.overlay.upper"
 export OVL_XATTR_METACOPY="trusted.overlay.metacopy"
 
+if [ -n "$OVL_BASE_FSTYP" ];then
+       _source_specific_fs $OVL_BASE_FSTYP
+fi
+
 # helper function to do the actual overlayfs mount operation
+# accepts "-" as upperdir for non-upper overlayfs
 _overlay_mount_dirs()
 {
        local lowerdir=$1
        local upperdir=$2
        local workdir=$3
        shift 3
+       local diropts="-olowerdir=$lowerdir"
+
+       [ -n "$upperdir" ] && [ "$upperdir" != "-" ] && \
+               diropts+=",upperdir=$upperdir,workdir=$workdir"
+
+       $MOUNT_PROG -t overlay $diropts `_common_dev_mount_options $*`
+}
 
-       $MOUNT_PROG -t overlay -o lowerdir=$lowerdir -o upperdir=$upperdir \
-                   -o workdir=$workdir `_common_dev_mount_options $*`
+# Mount with mnt/dev of scratch mount and custom mount options
+_overlay_scratch_mount_opts()
+{
+       $MOUNT_PROG -t overlay $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT $*
 }
 
 # Mount with same options/mnt/dev of scratch mount, but optionally
@@ -81,7 +95,12 @@ _overlay_base_mount()
                return 1
        fi
 
-       _mount -t $OVL_BASE_FSTYP $* $dev $mnt
+       if [ $OVL_BASE_FSTYP ]; then
+               _mount -t $OVL_BASE_FSTYP $* $dev $mnt
+       else
+               _mount $* $dev $mnt
+       fi
+
        _idmapped_mount $dev $mnt
 }
 
@@ -188,6 +207,67 @@ _require_scratch_overlay_features()
        _scratch_unmount
 }
 
+_check_scratch_overlay_xattr_escapes()
+{
+       local testfile=$1
+
+       touch $testfile
+       ! ($GETFATTR_PROG -n trusted.overlay.foo $testfile 2>&1 | grep -E -q "not (permitted|supported)")
+}
+
+_require_scratch_overlay_xattr_escapes()
+{
+       _scratch_mkfs > /dev/null 2>&1
+       _scratch_mount
+
+        _check_scratch_overlay_xattr_escapes $SCRATCH_MNT/file || \
+                  _notrun "xattr escaping is not supported by overlay"
+
+       _scratch_unmount
+}
+
+_require_scratch_overlay_verity()
+{
+       local lowerdirs="$OVL_BASE_SCRATCH_MNT/$OVL_UPPER:$OVL_BASE_SCRATCH_MNT/$OVL_LOWER"
+
+       _require_scratch_verity "$OVL_BASE_FSTYP" "$OVL_BASE_SCRATCH_MNT"
+
+       _scratch_mkfs > /dev/null 2>&1
+       _overlay_scratch_mount_dirs "$lowerdirs" "-" "-" \
+               -o ro,redirect_dir=follow,metacopy=on,verity=on > /dev/null 2>&1 || \
+               _notrun "overlay verity not supported on ${SCRATCH_DEV}"
+
+       _scratch_unmount
+}
+
+# Check kernel support for <lowerdirs>::<lowerdatadir> format
+_require_scratch_overlay_lowerdata_layers()
+{
+       local lowerdirs="$OVL_BASE_SCRATCH_MNT/$OVL_UPPER::$OVL_BASE_SCRATCH_MNT/$OVL_LOWER"
+
+       _scratch_mkfs > /dev/null 2>&1
+       _overlay_scratch_mount_dirs "$lowerdirs" "-" "-" \
+               -o ro,redirect_dir=follow,metacopy=on > /dev/null 2>&1 || \
+               _notrun "overlay data-only layers not supported on ${SCRATCH_DEV}"
+
+       _scratch_unmount
+}
+
+# Check kernel support for lowerdir+=<lowerdir>,datadir+=<lowerdatadir> format
+_require_scratch_overlay_lowerdir_add_layers()
+{
+       local lowerdir="$OVL_BASE_SCRATCH_MNT/$OVL_UPPER"
+       local datadir="$OVL_BASE_SCRATCH_MNT/$OVL_LOWER"
+
+       _scratch_mkfs > /dev/null 2>&1
+       _overlay_scratch_mount_opts \
+               -o"lowerdir+=$lowerdir,datadir+=$datadir" \
+               -o"redirect_dir=follow,metacopy=on" > /dev/null 2>&1 || \
+               _notrun "overlay lowerdir+,datadir+ not supported on ${SCRATCH_DEV}"
+
+       _scratch_unmount
+}
+
 # Helper function to check underlying dirs of overlay filesystem
 _overlay_fsck_dirs()
 {
index 867776cdd189bf6184cc058692f1cd5d52e3ff47..33f2db8d4ad8500f80871cddba670c41de9457d4 100644 (file)
@@ -11,7 +11,22 @@ _require_populate_commands() {
        _require_xfs_io_command "falloc"
        _require_xfs_io_command "fpunch"
        _require_test_program "punch-alternating"
-       _require_command "$XFS_DB_PROG" "xfs_db"
+       _require_test_program "popdir.pl"
+       if [ -n "${PYTHON3_PROG}" ]; then
+               _require_command $PYTHON3_PROG python3
+               _require_test_program "popattr.py"
+       fi
+       case "${FSTYP}" in
+       "xfs")
+               _require_command "$XFS_DB_PROG" "xfs_db"
+               _require_command "$WIPEFS_PROG" "wipefs"
+               _require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
+               ;;
+       ext*)
+               _require_command "$DUMPE2FS_PROG" "dumpe2fs"
+               _require_command "$E2IMAGE_PROG" "e2image"
+               ;;
+       esac
 }
 
 _require_xfs_db_blocktrash_z_command() {
@@ -30,6 +45,27 @@ __populate_create_file() {
        $XFS_IO_PROG -f -c "pwrite -S 0x62 -W -b 1m 0 $sz" "${fname}"
 }
 
+# Fail the test if we failed to create some kind of filesystem metadata.
+# Create a metadata dump of the failed filesystem so that we can analyze
+# how things went rong.
+__populate_fail() {
+       local flatdev="$(basename "$SCRATCH_DEV")"
+       local metadump="$seqres.$flatdev.populate.md"
+
+       case "$FSTYP" in
+       xfs)
+               _scratch_unmount
+               _scratch_xfs_metadump "$metadump" -a -o
+               ;;
+       ext4)
+               _scratch_unmount
+               _ext4_metadump "${SCRATCH_DEV}" "$metadump"
+               ;;
+       esac
+
+       _fail "$@"
+}
+
 # Punch out every other hole in this file, if it exists.
 #
 # The goal here is to force the creation of a large number of metadata records
@@ -44,21 +80,50 @@ __populate_fragment_file() {
 
 # Create a large directory
 __populate_create_dir() {
-       name="$1"
-       nr="$2"
-       missing="$3"
+       local name="$1"
+       local nr="$2"
+       local missing="$3"
+       shift; shift; shift
 
        mkdir -p "${name}"
-       seq 0 "${nr}" | while read d; do
-               creat=mkdir
-               test "$((d % 20))" -eq 0 && creat=touch
-               $creat "${name}/$(printf "%.08d" "$d")"
-       done
+       $here/src/popdir.pl --dir "${name}" --end "${nr}" "$@"
 
        test -z "${missing}" && return
-       seq 1 2 "${nr}" | while read d; do
-               rm -rf "${name}/$(printf "%.08d" "$d")"
+       $here/src/popdir.pl --dir "${name}" --start 1 --incr 19 --end "${nr}" --remove "$@"
+}
+
+# Create a large directory and ensure that it's a btree format
+__populate_xfs_create_btree_dir() {
+       local name="$1"
+       local isize="$2"
+       local dblksz="$3"
+       local missing="$4"
+       local icore_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
+       # We need enough extents to guarantee that the data fork is in
+       # btree format.  Cycling the mount to use xfs_db is too slow, so
+       # watch for when the extent count exceeds the space after the
+       # inode core.
+       local max_nextents="$(((isize - icore_size) / 16))"
+       local nr
+       local incr
+
+       # Add about one block's worth of dirents before we check the data fork
+       # format.
+       incr=$(( (dblksz / 8) / 100 * 100 ))
+
+       mkdir -p "${name}"
+       for ((nr = 0; ; nr += incr)); do
+               $here/src/popdir.pl --dir "${name}" --start "${nr}" --end "$((nr + incr - 1))"
+
+               # Extent count checks use data blocks only to avoid the removal
+               # step from removing dabtree index blocks and reducing the
+               # number of extents below the required threshold.
+               local nextents="$(xfs_bmap ${name} | grep -v hole | wc -l)"
+               [ "$((nextents - 1))" -gt $max_nextents ] && break
        done
+
+       test -z "${missing}" && return
+       $here/src/popdir.pl --dir "${name}" --start 1 --incr 19 --end "${nr}" --remove
 }
 
 # Add a bunch of attrs to a file
@@ -68,9 +133,21 @@ __populate_create_attr() {
        missing="$3"
 
        touch "${name}"
-       seq 0 "${nr}" | while read d; do
-               setfattr -n "user.$(printf "%.08d" "$d")" -v "$(printf "%.08d" "$d")" "${name}"
-       done
+
+       if [ -n "${PYTHON3_PROG}" ]; then
+               ${PYTHON3_PROG} $here/src/popattr.py --file "${name}" --end "${nr}"
+
+               test -z "${missing}" && return
+               ${PYTHON3_PROG} $here/src/popattr.py --file "${name}" --start 1 --incr 19 --end "${nr}" --remove
+               return
+       fi
+
+       # Simulate a getfattr dump file so we can bulk-add attrs.
+       (
+               echo "# file: ${name}";
+               seq --format "user.%08g=\"abcdefgh\"" 0 "${nr}"
+               echo
+       ) | setfattr --restore -
 
        test -z "${missing}" && return
        seq 1 2 "${nr}" | while read d; do
@@ -78,6 +155,57 @@ __populate_create_attr() {
        done
 }
 
+# Create an extended attr structure and ensure that the fork is btree format
+__populate_xfs_create_btree_attr() {
+       local name="$1"
+       local isize="$2"
+       local dblksz="$3"
+       local icore_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
+       # We need enough extents to guarantee that the attr fork is in btree
+       # format.  Cycling the mount to use xfs_db is too slow, so watch for
+       # when the number of holes that we can punch in the attr fork by
+       # deleting remote xattrs exceeds the number of extent mappings that can
+       # fit in the inode core.
+       local max_nextents="$(((isize - icore_size) / 16))"
+       local nr
+       local i
+       local incr
+       local bigval
+
+       # Add about one block's worth of attrs in betweeen creating punchable
+       # remote value blocks.
+       incr=$(( (dblksz / 16) / 100 * 100 ))
+       bigval="$(perl -e "print \"@\" x $dblksz;")"
+
+       touch "${name}"
+
+       # We cannot control the mapping behaviors of the attr fork leaf and
+       # dabtree blocks, but we do know that remote values are stored in a
+       # single extent, and that those mappings are removed if the xattr is
+       # deleted.
+       #
+       # The extended attribute structure tends to grow from offset zero
+       # upwards, so we try to set up a sparse attr fork mapping by
+       # iteratively creating at least one leaf block's worth of local attrs,
+       # and then one remote attr, until the number of remote xattrs exceeds
+       # the number of mappings that fit in the inode core...
+       for ((nr = 0; nr < (incr * max_nextents); nr += incr)); do
+               # Simulate a getfattr dump file so we can bulk-add attrs.
+               (
+                       echo "# file: ${name}";
+                       seq --format "user.%08g=\"abcdefgh\"" "${nr}" "$((nr + incr + 1))"
+                       echo "user.v$(printf "%.08d" "$nr")=\"${bigval}\""
+                       echo
+               ) | setfattr --restore -
+       done
+
+       # ... and in the second loop we delete all the remote attrs to
+       # fragment the attr fork mappings.
+       for ((i = 0; i < nr; i += incr)); do
+               setfattr -x "user.v$(printf "%.08d" "$i")" "${name}"
+       done
+}
+
 # Fill up some percentage of the remaining free space
 __populate_fill_fs() {
        dir="$1"
@@ -121,7 +249,7 @@ _populate_xfs_qmount_option()
        fi
 
        # Turn on all the quotas
-       if $XFS_INFO_PROG "${TEST_DIR}" | grep -q 'crc=1'; then
+       if _xfs_has_feature "$TEST_DIR" crc; then
                # v5 filesystems can have group & project quotas
                quota="usrquota,grpquota,prjquota"
        else
@@ -132,7 +260,7 @@ _populate_xfs_qmount_option()
        # Inject our quota mount options
        if echo "${MOUNT_OPTIONS}" | grep -q "${quota}"; then
                return
-       elif echo "${MOUNT_OPTIONS}" | egrep -q '(quota|noenforce)'; then
+       elif echo "${MOUNT_OPTIONS}" | grep -Eq '(quota|noenforce)'; then
                _qmount_option "${quota}"
        else
                export MOUNT_OPTIONS="$MOUNT_OPTIONS -o ${quota}"
@@ -165,8 +293,9 @@ _scratch_xfs_populate() {
        _xfs_force_bdev data $SCRATCH_MNT
 
        blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
-       dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
-       crc="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep crc= | sed -e 's/^.*crc=//g' -e 's/\([0-9]*\).*$/\1/g')"
+       dblksz="$(_xfs_get_dir_blocksize "$SCRATCH_MNT")"
+       isize="$(_xfs_get_inode_size "$SCRATCH_MNT")"
+       crc="$(_xfs_has_feature "$SCRATCH_MNT" crc -v)"
        if [ $crc -eq 1 ]; then
                leaf_hdr_size=64
        else
@@ -179,9 +308,7 @@ _scratch_xfs_populate() {
 
        # Fill up the root inode chunk
        echo "+ fill root ino chunk"
-       seq 1 64 | while read f; do
-               $XFS_IO_PROG -f -c "truncate 0" "${SCRATCH_MNT}/dummy${f}"
-       done
+       $here/src/popdir.pl --dir "${SCRATCH_MNT}" --start 1 --end 64 --format "dummy%u" --file-pct 100
 
        # Regular files
        # - FMT_EXTENTS
@@ -216,7 +343,7 @@ _scratch_xfs_populate() {
 
        # - BTREE
        echo "+ btree dir"
-       __populate_create_dir "${SCRATCH_MNT}/S_IFDIR.FMT_BTREE" "$((128 * dblksz / 40))" true
+       __populate_xfs_create_btree_dir "${SCRATCH_MNT}/S_IFDIR.FMT_BTREE" "$isize" "$dblksz" true
 
        # Symlinks
        # - FMT_LOCAL
@@ -251,7 +378,7 @@ _scratch_xfs_populate() {
 
        # BTREE
        echo "+ btree attr"
-       __populate_create_attr "${SCRATCH_MNT}/ATTR.FMT_BTREE" "$((64 * blksz / 40))" true
+       __populate_xfs_create_btree_attr "${SCRATCH_MNT}/ATTR.FMT_BTREE" "$isize" "$dblksz"
 
        # trusted namespace
        touch ${SCRATCH_MNT}/ATTR.TRUSTED
@@ -295,17 +422,10 @@ _scratch_xfs_populate() {
        local rec_per_btblock=16
        local nr="$(( 2 * (blksz / rec_per_btblock) * ino_per_rec ))"
        local dir="${SCRATCH_MNT}/INOBT"
-       mkdir -p "${dir}"
-       seq 0 "${nr}" | while read f; do
-               touch "${dir}/${f}"
-       done
-
-       seq 0 2 "${nr}" | while read f; do
-               rm -f "${dir}/${f}"
-       done
+       __populate_create_dir "${dir}" "${nr}" true --file-pct 100
 
        # Reverse-mapping btree
-       is_rmapbt="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'rmapbt=1')"
+       is_rmapbt="$(_xfs_has_feature "$SCRATCH_MNT" rmapbt -v)"
        if [ $is_rmapbt -gt 0 ]; then
                echo "+ rmapbt btree"
                nr="$((blksz * 2 / 24))"
@@ -313,7 +433,7 @@ _scratch_xfs_populate() {
        fi
 
        # Realtime Reverse-mapping btree
-       is_rt="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'rtextents=[1-9]')"
+       is_rt="$(_xfs_get_rtextents "$SCRATCH_MNT")"
        if [ $is_rmapbt -gt 0 ] && [ $is_rt -gt 0 ]; then
                echo "+ rtrmapbt btree"
                nr="$((blksz * 2 / 32))"
@@ -322,7 +442,7 @@ _scratch_xfs_populate() {
        fi
 
        # Reference-count btree
-       is_reflink="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'reflink=1')"
+       is_reflink="$(_xfs_has_feature "$SCRATCH_MNT" reflink -v)"
        if [ $is_reflink -gt 0 ]; then
                echo "+ reflink btree"
                nr="$((blksz * 2 / 12))"
@@ -459,7 +579,7 @@ __populate_check_xfs_dformat() {
        format="$2"
 
        fmt="$(_scratch_xfs_db -c "inode ${inode}" -c 'p core.format' | sed -e 's/^.*(\([a-z]*\)).*$/\1/g')"
-       test "${format}" = "${fmt}" || _fail "failed to create ino ${inode} dformat expected ${format} saw ${fmt}"
+       test "${format}" = "${fmt}" || __populate_fail "failed to create ino ${inode} dformat expected ${format} saw ${fmt}"
 }
 
 # Check attr fork format of XFS file
@@ -468,7 +588,7 @@ __populate_check_xfs_aformat() {
        format="$2"
 
        fmt="$(_scratch_xfs_db -c "inode ${inode}" -c 'p core.aformat' | sed -e 's/^.*(\([a-z]*\)).*$/\1/g')"
-       test "${format}" = "${fmt}" || _fail "failed to create ino ${inode} aformat expected ${format} saw ${fmt}"
+       test "${format}" = "${fmt}" || __populate_fail "failed to create ino ${inode} aformat expected ${format} saw ${fmt}"
 }
 
 # Check structure of XFS directory
@@ -487,21 +607,21 @@ __populate_check_xfs_dir() {
 
        case "${dtype}" in
        "shortform"|"inline"|"local")
-               (test "${datab}" -eq 0 && test "${leafb}" -eq 0 && test "${freeb}" -eq 0) || _fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
+               (test "${datab}" -eq 0 && test "${leafb}" -eq 0 && test "${freeb}" -eq 0) || __populate_fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
                ;;
        "block")
-               (test "${datab}" -eq 1 && test "${leafb}" -eq 0 && test "${freeb}" -eq 0) || _fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
+               (test "${datab}" -eq 1 && test "${leafb}" -eq 0 && test "${freeb}" -eq 0) || __populate_fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
                ;;
        "leaf")
-               (test "${datab}" -eq 1 && test "${leafb}" -eq 1 && test "${freeb}" -eq 0) || _fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
+               (test "${datab}" -eq 1 && test "${leafb}" -eq 1 && test "${freeb}" -eq 0) || __populate_fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
                ;;
        "leafn")
                _scratch_xfs_db -x -c "inode ${inode}" -c "dblock ${leaf_lblk}" -c "p lhdr.info.hdr.magic" | grep -q '0x3dff' && return
                _scratch_xfs_db -x -c "inode ${inode}" -c "dblock ${leaf_lblk}" -c "p lhdr.info.magic" | grep -q '0xd2ff' && return
-               _fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
+               __populate_fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
                ;;
        "node"|"btree")
-               (test "${datab}" -eq 1 && test "${leafb}" -eq 1 && test "${freeb}" -eq 1) || _fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
+               (test "${datab}" -eq 1 && test "${leafb}" -eq 1 && test "${freeb}" -eq 1) || __populate_fail "failed to create ${dtype} dir ino ${inode} datab ${datab} leafb ${leafb} freeb ${freeb}"
                ;;
        *)
                _fail "Unknown directory type ${dtype}"
@@ -521,13 +641,13 @@ __populate_check_xfs_attr() {
 
        case "${atype}" in
        "shortform"|"inline"|"local")
-               (test "${datab}" -eq 0 && test "${leafb}" -eq 0) || _fail "failed to create ${atype} attr ino ${inode} datab ${datab} leafb ${leafb}"
+               (test "${datab}" -eq 0 && test "${leafb}" -eq 0) || __populate_fail "failed to create ${atype} attr ino ${inode} datab ${datab} leafb ${leafb}"
                ;;
        "leaf")
-               (test "${datab}" -eq 1 && test "${leafb}" -eq 0) || _fail "failed to create ${atype} attr ino ${inode} datab ${datab} leafb ${leafb}"
+               (test "${datab}" -eq 1 && test "${leafb}" -eq 0) || __populate_fail "failed to create ${atype} attr ino ${inode} datab ${datab} leafb ${leafb}"
                ;;
        "node"|"btree")
-               (test "${datab}" -eq 1 && test "${leafb}" -eq 1) || _fail "failed to create ${atype} attr ino ${inode} datab ${datab} leafb ${leafb}"
+               (test "${datab}" -eq 1 && test "${leafb}" -eq 1) || __populate_fail "failed to create ${atype} attr ino ${inode} datab ${datab} leafb ${leafb}"
                ;;
        *)
                _fail "Unknown attribute type ${atype}"
@@ -536,8 +656,8 @@ __populate_check_xfs_attr() {
 
 # Check that there's at least one per-AG btree with multiple levels
 __populate_check_xfs_agbtree_height() {
-       bt_type="$1"
-       nr_ags=$(_scratch_xfs_db -c 'sb 0' -c 'p agcount' | awk '{print $3}')
+       local bt_type="$1"
+       local agcount=$(_scratch_xfs_db -c 'sb 0' -c 'p agcount' | awk '{print $3}')
 
        case "${bt_type}" in
        "bno"|"cnt"|"rmap"|"refcnt")
@@ -557,13 +677,14 @@ __populate_check_xfs_agbtree_height() {
                ;;
        esac
 
-       seq 0 $((nr_ags - 1)) | while read ag; do
-               bt_level=$(_scratch_xfs_db -c "${hdr} ${ag}" -c "p ${bt_prefix}level" | awk '{print $3}')
+       for ((agno = 0; agno < agcount; agno++)); do
+               bt_level=$(_scratch_xfs_db -c "${hdr} ${agno}" -c "p ${bt_prefix}level" | awk '{print $3}')
+               # "level" is really the btree height
                if [ "${bt_level}" -gt 1 ]; then
-                       return 100
+                       return 0
                fi
        done
-       test $? -eq 100 || _fail "Failed to create ${bt_type} of sufficient height!"
+       __populate_fail "Failed to create ${bt_type} of sufficient height!"
        return 1
 }
 
@@ -587,12 +708,12 @@ _scratch_xfs_populate_check() {
        leaf_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_LEAF")"
        node_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_NODE")"
        btree_attr="$(__populate_find_inode "${SCRATCH_MNT}/ATTR.FMT_BTREE")"
-       is_finobt=$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'finobt=1')
-       is_rmapbt=$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'rmapbt=1')
-       is_reflink=$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep -c 'reflink=1')
+       is_finobt=$(_xfs_has_feature "$SCRATCH_MNT" finobt -v)
+       is_rmapbt=$(_xfs_has_feature "$SCRATCH_MNT" rmapbt -v)
+       is_reflink=$(_xfs_has_feature "$SCRATCH_MNT" reflink -v)
 
        blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
-       dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+       dblksz="$(_xfs_get_dir_blocksize "$SCRATCH_MNT")"
        leaf_lblk="$((32 * 1073741824 / blksz))"
        node_lblk="$((64 * 1073741824 / blksz))"
        umount "${SCRATCH_MNT}"
@@ -631,18 +752,18 @@ __populate_check_ext4_dformat() {
        extents=0
        etree=0
        debugfs -R "stat <${inode}>" "${dev}" 2> /dev/null | grep 'ETB[0-9]' -q && etree=1
-       iflags="$(debugfs -R "stat <${inode}>" "${dev}" 2> /dev/null | grep 'Flags:' | sed -e 's/^.*Flags: \([0-9a-fx]*\).*$/\1/g')"
+       iflags="$(_ext4_get_inum_iflags "${dev}" "${inode}")"
        test "$(echo "${iflags}" | awk '{print and(strtonum($1), 0x80000);}')" -gt 0 && extents=1
 
        case "${format}" in
        "blockmap")
-               test "${extents}" -eq 0 || _fail "failed to create ino ${inode} with blockmap"
+               test "${extents}" -eq 0 || __populate_fail "failed to create ino ${inode} with blockmap"
                ;;
        "extent"|"extents")
-               test "${extents}" -eq 1 || _fail "failed to create ino ${inode} with extents"
+               test "${extents}" -eq 1 || __populate_fail "failed to create ino ${inode} with extents"
                ;;
        "etree")
-               (test "${extents}" -eq 1 && test "${etree}" -eq 1) || _fail "failed to create ino ${inode} with extent tree"
+               (test "${extents}" -eq 1 && test "${etree}" -eq 1) || __populate_fail "failed to create ino ${inode} with extent tree"
                ;;
        *)
                _fail "Unknown dformat ${format}"
@@ -660,10 +781,10 @@ __populate_check_ext4_aformat() {
 
        case "${format}" in
        "local"|"inline")
-               test "${ablock}" -eq 0 || _fail "failed to create inode ${inode} with ${format} xattr"
+               test "${ablock}" -eq 0 || __populate_fail "failed to create inode ${inode} with ${format} xattr"
                ;;
        "block")
-               test "${extents}" -eq 1 || _fail "failed to create inode ${inode} with ${format} xattr"
+               test "${extents}" -eq 1 || __populate_fail "failed to create inode ${inode} with ${format} xattr"
                ;;
        *)
                _fail "Unknown aformat ${format}"
@@ -678,19 +799,19 @@ __populate_check_ext4_dir() {
 
        htree=0
        inline=0
-       iflags="$(debugfs -R "stat <${inode}>" "${dev}" 2> /dev/null | grep 'Flags:' | sed -e 's/^.*Flags: \([0-9a-fx]*\).*$/\1/g')"
+       iflags="$(_ext4_get_inum_iflags "${dev}" "${inode}")"
        test "$(echo "${iflags}" | awk '{print and(strtonum($1), 0x1000);}')" -gt 0 && htree=1
        test "$(echo "${iflags}" | awk '{print and(strtonum($1), 0x10000000);}')" -gt 0 && inline=1
 
        case "${dtype}" in
        "inline")
-               (test "${inline}" -eq 1 && test "${htree}" -eq 0) || _fail "failed to create ${dtype} dir ino ${inode} htree ${htree} inline ${inline}"
+               (test "${inline}" -eq 1 && test "${htree}" -eq 0) || __populate_fail "failed to create ${dtype} dir ino ${inode} htree ${htree} inline ${inline}"
                ;;
        "block")
-               (test "${inline}" -eq 0 && test "${htree}" -eq 0) || _fail "failed to create ${dtype} dir ino ${inode} htree ${htree} inline ${inline}"
+               (test "${inline}" -eq 0 && test "${htree}" -eq 0) || __populate_fail "failed to create ${dtype} dir ino ${inode} htree ${htree} inline ${inline}"
                ;;
        "htree")
-               (test "${inline}" -eq 0 && test "${htree}" -eq 1) || _fail "failed to create ${dtype} dir ino ${inode} htree ${htree} inline ${inline}"
+               (test "${inline}" -eq 0 && test "${htree}" -eq 1) || __populate_fail "failed to create ${dtype} dir ino ${inode} htree ${htree} inline ${inline}"
                ;;
        *)
                _fail "Unknown directory type ${dtype}"
@@ -838,25 +959,37 @@ _scratch_populate_cache_tag() {
 _scratch_populate_restore_cached() {
        local metadump="$1"
 
-       # If we're configured for compressed dumps and there isn't already an
-       # uncompressed dump, see if we can use DUMP_COMPRESSOR to decompress
-       # something.
-       if [ -n "$DUMP_COMPRESSOR" ]; then
-               for compr in "$metadump".*; do
-                       [ -e "$compr" ] && $DUMP_COMPRESSOR -d -f -k "$compr" && break
-               done
-       fi
-
-       test -r "$metadump" || return 1
-
        case "${FSTYP}" in
        "xfs")
-               xfs_mdrestore "${metadump}" "${SCRATCH_DEV}" && return 0
+               _xfs_mdrestore "${metadump}" "${SCRATCH_DEV}"
+               res=$?
+               test $res -ne 0 && return $res
+
+               # Cached images should have been unmounted cleanly, so if
+               # there's an external log we need to wipe it and run repair to
+               # format it to match this filesystem.
+               if [ -n "${SCRATCH_LOGDEV}" ]; then
+                       $WIPEFS_PROG -a "${SCRATCH_LOGDEV}"
+                       _scratch_xfs_repair
+                       res=$?
+               fi
+               return $res
                ;;
        "ext2"|"ext3"|"ext4")
-               # ext4 cannot e2image external logs, so we cannot restore
-               test -n "${SCRATCH_LOGDEV}" && return 1
-               e2image -r "${metadump}" "${SCRATCH_DEV}" && return 0
+               _ext4_mdrestore "${metadump}" "${SCRATCH_DEV}"
+               ret=$?
+               test $ret -ne 0 && return $ret
+
+               # ext4 cannot e2image external logs, so we have to reformat
+               # the scratch device to match the restored fs
+               if [ -n "${SCRATCH_LOGDEV}" ]; then
+                       local fsuuid="$($DUMPE2FS_PROG -h "${SCRATCH_DEV}" 2>/dev/null | \
+                                       grep 'Journal UUID:' | \
+                                       sed -e 's/Journal UUID:[[:space:]]*//g')"
+                       $MKFS_EXT4_PROG -O journal_dev "${SCRATCH_LOGDEV}" \
+                                       -F -U "${fsuuid}"
+               fi
+               return 0
                ;;
        esac
        return 1
@@ -868,15 +1001,15 @@ _scratch_populate_cached() {
        local meta_tag="$(echo "${meta_descr}" | md5sum - | cut -d ' ' -f 1)"
        local metadump_stem="${TEST_DIR}/__populate.${FSTYP}.${meta_tag}"
 
-       # These variables are shared outside this function
+       # This variable is shared outside this function
        POPULATE_METADUMP="${metadump_stem}.metadump"
-       POPULATE_METADUMP_DESCR="${metadump_stem}.txt"
+       local populate_metadump_descr="${metadump_stem}.txt"
 
        # Don't keep metadata images cached for more 48 hours...
        rm -rf "$(find "${POPULATE_METADUMP}" -mtime +2 2>/dev/null)"
 
        # Throw away cached image if it doesn't match our spec.
-       cmp -s "${POPULATE_METADUMP_DESCR}" <(echo "${meta_descr}") || \
+       cmp -s "${populate_metadump_descr}" <(echo "${meta_descr}") || \
                rm -rf "${POPULATE_METADUMP}"
 
        # Try to restore from the metadump
@@ -885,19 +1018,18 @@ _scratch_populate_cached() {
 
        # Oh well, just create one from scratch
        _scratch_mkfs
-       echo "${meta_descr}" > "${POPULATE_METADUMP_DESCR}"
+       echo "${meta_descr}" > "${populate_metadump_descr}"
        case "${FSTYP}" in
        "xfs")
                _scratch_xfs_populate $@
                _scratch_xfs_populate_check
-               _scratch_xfs_metadump "${POPULATE_METADUMP}"
 
-               local logdev=
+               local logdev=none
                [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
                        logdev=$SCRATCH_LOGDEV
 
                _xfs_metadump "$POPULATE_METADUMP" "$SCRATCH_DEV" "$logdev" \
-                       compress
+                       compress -a -o
                ;;
        "ext2"|"ext3"|"ext4")
                _scratch_ext4_populate $@
index b6b8a0b9aae2b901e76ea4d760cd1ace53645c49..9e730404e24a2a3a4e4ac47c5b00b2fa155fe4b0 100644 (file)
@@ -109,7 +109,23 @@ _filter_fiemap()
 
 _filter_fiemap_flags()
 {
-       $AWK_PROG '
+       local include_encoded_flag=0
+
+       # Unless a first argument is passed and with a value of 1, the fiemap
+       # encoded flag is filtered out.
+       # This is because tests that use this filter's output in their golden
+       # output may get the encoded flag set or not depending on the filesystem
+       # and its configuration. For example, running btrfs with "-o compress"
+       # (or compress-force) set in MOUNT_OPTIONS, then extents that get
+       # compressed are reported with the encoded flag, otherwise that flag is
+       # not reported. Like this the fs configuration does not cause a mismatch
+       # with the golden output, and tests that exercise specific configurations
+       # can explicitly ask for the encoded flag to be printed.
+       if [ ! -z "$1" ] && [ $1 -eq 1 ]; then
+               include_encoded_flag=1
+       fi
+
+       local awk_script='
                $3 ~ /hole/ {
                        print $1, $2, $3;
                        next;
@@ -126,16 +142,34 @@ _filter_fiemap_flags()
                        if (and(flags, 0x2000)) {
                                flag_str = "shared";
                                set = 1;
-                       }
+                       }'
+
+       if [ $include_encoded_flag -eq 1 ]; then
+               awk_script=$awk_script'
+                       if (and(flags, 0x8)) {
+                               if (set) {
+                                       flag_str = flag_str"|";
+                               } else {
+                                       flag_str = "";
+                               }
+                               flag_str = flag_str"encoded";
+                               set = 1;
+                       }'
+       fi
+
+       awk_script=$awk_script'
                        if (and(flags, 0x1)) {
                                if (set) {
                                        flag_str = flag_str"|";
+                               } else {
+                                       flag_str = "";
                                }
                                flag_str = flag_str"last";
                        }
                        print $1, $2, flag_str
-               }' |
-       _coalesce_extents
+               }'
+
+       $AWK_PROG -e "$awk_script" | _coalesce_extents
 }
 
 # Filters fiemap output to only print the 
@@ -154,6 +188,7 @@ _filter_hole_fiemap()
        _coalesce_extents
 }
 
+# Column 7 for datadev files and column 5 for rtdev files
 #     10000 Unwritten preallocated extent
 #     01000 Doesn't begin on stripe unit
 #     00100 Doesn't end   on stripe unit
@@ -166,6 +201,13 @@ _filter_bmap()
                        print $1, $2, $3;
                        next;
                }
+               $5 ~ /1[01][01][01][01]/ {
+                       print $1, $2, "unwritten";
+                       next;
+               }
+               $5 ~ /0[01][01][01][01]/ {
+                       print $1, $2, "data"
+               }
                $7 ~ /1[01][01][01][01]/ {
                        print $1, $2, "unwritten";
                        next;
@@ -250,6 +292,7 @@ _test_generic_punch()
        _8k="$((multiple * 8))k"
        _12k="$((multiple * 12))k"
        _20k="$((multiple * 20))k"
+       _require_congruent_file_oplen "$(dirname "$testfile")" $((multiple * 4096))
 
        # initial test state must be defined, otherwise the first test can fail
        # due ot stale file state left from previous tests.
@@ -480,7 +523,7 @@ _test_generic_punch()
        if [ "$remove_testfile" ]; then
                rm -f $testfile
        fi
-       block_size=`_get_block_size $TEST_DIR`
+       block_size=`_get_file_block_size $TEST_DIR`
        $XFS_IO_PROG -f -c "truncate $block_size" \
                -c "pwrite 0 $block_size" $sync_cmd \
                -c "$zero_cmd 128 128" \
index 3a758e0f701b226169a617f8f43a072f1bc36262..6b529bf4b43190bde6009543c58cf82e9b4038d3 100644 (file)
@@ -53,6 +53,70 @@ _require_xfs_quota()
     [ -n "$XFS_QUOTA_PROG" ] || _notrun "XFS quota user tools not installed"
 }
 
+# Check that a mounted fs has a particular type of quota accounting turned on.
+#
+# The first argument must be the data device of a mounted fs.  It must not be
+# the actual mountpath.
+#
+# The second argument is the quota type ('usrquota', 'grpquota', 'prjquota',
+# 'any', or 'all').
+_xfs_quota_acct_enabled()
+{
+       local dev="$1"
+       local qtype="$2"
+       local f_args=()
+       local any=
+
+       case "$qtype" in
+       "usrquota"|"uquota")    f_args=("-U");;
+       "grpquota"|"gquota")    f_args=("-G");;
+       "prjquota"|"pquota")    f_args=("-P");;
+       "all")                  f_args=("-U" "-G" "-P");;
+       "any")                  f_args=("-U" "-G" "-P"); any=1;;
+       *)                      echo "$qtype: Unknown quota type."; return 1;;
+       esac
+
+       if [ "$any" = "1" ]; then
+               for arg in "$f_args"; do
+                       $here/src/feature "$arg" "$dev" && return 0
+               done
+               return 1
+       fi
+
+       $here/src/feature "${f_args[@]}" "$dev"
+}
+
+# Require that a mounted fs has a particular type of quota turned on.  This
+# takes the same arguments as _xfs_quota_acct_enabled.  If the third argument is
+# '-u' (or is empty and dev is $SCRATCH_DEV) the fs will be unmounted on
+# failure.
+_require_xfs_quota_acct_enabled()
+{
+       local dev="$1"
+       local qtype="$2"
+       local umount="$3"
+       local fsname="$dev"
+
+       _xfs_quota_acct_enabled "$dev" "$qtype" "$qmode" && return 0
+
+       if [ -z "$umount" ] && [ "$dev" = "$SCRATCH_DEV" ]; then
+               umount="-u"
+       fi
+       test "$umount" = "-u" && umount "$dev" &>/dev/null
+
+       case "$dev" in
+       "$TEST_DEV")    fsname="test";;
+       "$SCRATCH_DEV") fsname="scratch";;
+       esac
+
+       case "$qtype" in
+       "any")          qtype="any quotas";;
+       "all")          qtype="all quotas";;
+       esac
+
+       _notrun "$qtype: accounting not enabled on $fsname filesystem."
+}
+
 #
 # checks that xfs_quota can operate on foreign (non-xfs) filesystems
 # Skips check on xfs filesystems, old xfs_quota is fine there.
@@ -127,7 +191,7 @@ _scratch_enable_pquota()
 _require_setquota_project()
 {
        setquota --help 2>&1 | \
-               grep -q "\-P, \-\-project[[:space:]]*set limits for project"
+               grep -q -- "-P, --project[[:space:]]*set limits for project"
        if [ "$?" -ne 0 ];then
                _notrun "setquota doesn't support project quota (-P)"
        fi
@@ -141,7 +205,7 @@ _require_nobody()
     _cat_passwd | grep -q '^nobody'
     [ $? -ne 0 ] && _notrun "password file does not contain user nobody."
 
-    _cat_group | egrep -q '^no(body|group)'
+    _cat_group | grep -Eq '^no(body|group)'
     [ $? -ne 0 ] && _notrun "group file does not contain nobody/nogroup."
 }
 
@@ -192,7 +256,7 @@ _choose_uid()
 
 _choose_gid()
 {
-    _cat_group | egrep '^no(body|group)' | perl -ne '@a = split(/:/); END { printf "id=%d name=%s\n", $a[2],$a[0] }'
+    _cat_group | grep -E '^no(body|group)' | perl -ne '@a = split(/:/); END { printf "id=%d name=%s\n", $a[2],$a[0] }'
 }
 
 _choose_prid()
@@ -218,7 +282,7 @@ _qmount()
         quotacheck -ug $SCRATCH_MNT >>$seqres.full 2>&1
         quotaon -ug $SCRATCH_MNT >>$seqres.full 2>&1
         # try to turn on project quota if it's supported
-        if quotaon --help 2>&1 | grep -q '\-\-project'; then
+        if quotaon --help 2>&1 | grep -q -- '--project'; then
             quotaon --project $SCRATCH_MNT >>$seqres.full 2>&1
         fi
     fi
@@ -323,12 +387,12 @@ _check_quota_usage()
 
 # Report the block usage of root, $qa_user, and nobody
 _report_quota_blocks() {
-       repquota $1 | egrep "^($qa_user|root|nobody)" | awk '{print $1, $3, $4, $5}' | sort -r
+       repquota $1 | grep -E "^($qa_user|root|nobody)" | awk '{print $1, $3, $4, $5}' | sort -r
 }
 
 # Report the inode usage of root, $qa_user, and nobody
 _report_quota_inodes() {
-       repquota $1 | egrep "^($qa_user|root|nobody)" | awk '{print $1, $6, $7, $8}' | sort -r
+       repquota $1 | grep -E "^($qa_user|root|nobody)" | awk '{print $1, $6, $7, $8}' | sort -r
 }
 
 # Determine which type of quota we're using
index 70a15f9ca156706a01de25a6aea2fddf9d1cd209..b7b77ac1b46d33a7a5a6111da5fca4b49443a255 100644 (file)
--- a/common/rc
+++ b/common/rc
@@ -6,6 +6,11 @@
 
 BC="$(type -P bc)" || BC=
 
+_wallclock()
+{
+    date "+%s"
+}
+
 _require_math()
 {
        if [ -z "$BC" ]; then
@@ -96,54 +101,7 @@ _log_err()
 umask 022
 
 # check for correct setup and source the $FSTYP specific functions now
-case "$FSTYP" in
-    xfs)
-        [ "$XFS_LOGPRINT_PROG" = "" ] && _fatal "xfs_logprint not found"
-        [ "$XFS_REPAIR_PROG" = "" ] && _fatal "xfs_repair not found"
-        [ "$XFS_DB_PROG" = "" ] && _fatal "xfs_db not found"
-        [ "$MKFS_XFS_PROG" = "" ] && _fatal "mkfs_xfs not found"
-        [ "$XFS_INFO_PROG" = "" ] && _fatal "xfs_info not found"
-
-        . ./common/xfs
-        ;;
-    udf)
-        [ "$MKFS_UDF_PROG" = "" ] && _fatal "mkfs_udf/mkudffs not found"
-        ;;
-    btrfs)
-        [ "$MKFS_BTRFS_PROG" = "" ] && _fatal "mkfs.btrfs not found"
-
-        . ./common/btrfs
-        ;;
-    ext4)
-        [ "$MKFS_EXT4_PROG" = "" ] && _fatal "mkfs.ext4 not found"
-        ;;
-    f2fs)
-        [ "$MKFS_F2FS_PROG" = "" ] && _fatal "mkfs.f2fs not found"
-        ;;
-    nfs)
-        . ./common/nfs
-        ;;
-    cifs)
-        ;;
-    9p)
-        ;;
-    ceph)
-        . ./common/ceph
-        ;;
-    glusterfs)
-        ;;
-    overlay)
-        . ./common/overlay
-        ;;
-    reiser4)
-        [ "$MKFS_REISER4_PROG" = "" ] && _fatal "mkfs.reiser4 not found"
-        ;;
-    pvfs2)
-       ;;
-    ubifs)
-       [ "$UBIUPDATEVOL_PROG" = "" ] && _fatal "ubiupdatevol not found"
-       ;;
-esac
+_source_specific_fs $FSTYP
 
 if [ ! -z "$REPORT_LIST" ]; then
        . ./common/report
@@ -155,19 +113,22 @@ _get_filesize()
     stat -c %s "$1"
 }
 
+# Does this kernel support huge pages?
+_require_hugepages()
+{
+       awk '/Hugepagesize/ {print $2}' /proc/meminfo | grep -E -q ^[0-9]+$ || \
+               _notrun "Kernel does not report huge page size"
+}
+
 # Get hugepagesize in bytes
 _get_hugepagesize()
 {
-       local hugepgsz=$(awk '/Hugepagesize/ {print $2}' /proc/meminfo)
-       # Call _notrun if $hugepgsz is not a number
-       echo "$hugepgsz" | egrep -q ^[0-9]+$ || \
-               _notrun "Cannot get the value of Hugepagesize"
-       echo $((hugepgsz * 1024))
+       awk '/Hugepagesize/ {print $2 * 1024}' /proc/meminfo
 }
 
 _mount()
 {
-    $MOUNT_PROG `_mount_ops_filter $*`
+    $MOUNT_PROG $*
 }
 
 # Call _mount to do mount operation but also save mountpoint to
@@ -219,30 +180,16 @@ _clear_mount_stack()
 
 _scratch_options()
 {
-    local type=$1
-    local rt_opt=""
-    local log_opt=""
     SCRATCH_OPTIONS=""
 
-    if [ "$FSTYP" != "xfs" ]; then
-        return
-    fi
-
-    case $type in
-    mkfs)
-       SCRATCH_OPTIONS="$SCRATCH_OPTIONS -f"
-       rt_opt="-r"
-        log_opt="-l"
+    case "$FSTYP" in
+    "xfs")
+       _scratch_xfs_options "$@"
        ;;
-    mount)
-       rt_opt="-o"
-        log_opt="-o"
+    ext2|ext3|ext4|ext4dev)
+       _scratch_ext4_options "$@"
        ;;
     esac
-    [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
-       SCRATCH_OPTIONS="$SCRATCH_OPTIONS ${rt_opt}rtdev=$SCRATCH_RTDEV"
-    [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
-       SCRATCH_OPTIONS="$SCRATCH_OPTIONS ${log_opt}logdev=$SCRATCH_LOGDEV"
 }
 
 _test_options()
@@ -272,19 +219,6 @@ _test_options()
        TEST_OPTIONS="$TEST_OPTIONS ${log_opt}logdev=$TEST_LOGDEV"
 }
 
-_mount_ops_filter()
-{
-    local params="$*"
-    local last_index=$(( $# - 1 ))
-
-    [ $last_index -gt 0 ] && shift $last_index
-    local fs_escaped=$1
-
-    echo $params | \
-        $PERL_PROG -ne "s#mtpt=[^,|^\n|^\s]*#mtpt=$fs_escaped\1\2#; print;"
-
-}
-
 # Used for mounting non-scratch devices (e.g. loop, dm constructs)
 # with the safe set of scratch mount options (e.g. loop image may be
 # hosted on $SCRATCH_DEV, so can't use external scratch devices).
@@ -308,7 +242,7 @@ _supports_filetype()
        local fstyp=`$DF_PROG $dir | tail -1 | $AWK_PROG '{print $2}'`
        case "$fstyp" in
        xfs)
-               $XFS_INFO_PROG $dir | grep -q "ftype=1"
+               _xfs_has_feature $dir ftype
                ;;
        ext2|ext3|ext4)
                local dev=`$DF_PROG $dir | tail -1 | $AWK_PROG '{print $1}'`
@@ -335,7 +269,7 @@ _try_scratch_mount()
                _overlay_scratch_mount $*
                return $?
        fi
-       _mount -t $FSTYP `_scratch_mount_options $*`
+       _mount -t $FSTYP$FUSE_SUBTYP `_scratch_mount_options $*`
        mount_ret=$?
        [ $mount_ret -ne 0 ] && return $mount_ret
        _idmapped_mount $SCRATCH_DEV $SCRATCH_MNT
@@ -482,14 +416,18 @@ _idmapped_mount()
        local tmp=`mktemp -d`
 
        local mount_rec=`findmnt -rncv -S $dev -o OPTIONS`
+       # We create an idmapped mount where {g,u}id 0 writes to disk as
+       # {g,u}id 10000000 and $(id -u fsgqa) + 10000000. We change ownership
+       # of $mnt, provided it's not read-only, so {g,u} id 0 can actually
+       # create objects in there.
+       if [[ "$mount_rec" != *"ro,"* && "$mount_rec" != *",ro"* ]]; then
+               chown 10000000:10000000 $mnt || return 1
+       fi
+       # But if the mount is already idmapped, then there's nothing more to do.
        if [[ "$mount_rec" == *"idmapped"* ]]; then
                return 0
        fi
 
-       # We create an idmapped mount where {g,u}id 0 writes to disk as
-       # {g,u}id 10000000 and $(id -u fsgqa) + 10000000. We change ownership
-        # of $mnt so {g,u} id 0 can actually create objects in there.
-       chown 10000000:10000000 $mnt || return 1
        $here/src/vfs/mount-idmapped \
                --map-mount b:10000000:0:100000000000 \
                $mnt $tmp
@@ -519,7 +457,7 @@ _test_mount()
     fi
 
     _test_options mount
-    _mount -t $FSTYP $TEST_OPTIONS $TEST_FS_MOUNT_OPTS $SELINUX_MOUNT_OPTIONS $* $TEST_DEV $TEST_DIR
+    _mount -t $FSTYP$FUSE_SUBTYP $TEST_OPTIONS $TEST_FS_MOUNT_OPTS $SELINUX_MOUNT_OPTIONS $* $TEST_DEV $TEST_DIR
     mount_ret=$?
     [ $mount_ret -ne 0 ] && return $mount_ret
     _idmapped_mount $TEST_DEV $TEST_DIR
@@ -606,113 +544,6 @@ _scratch_do_mkfs()
        return $mkfs_status
 }
 
-_setup_large_ext4_fs()
-{
-       local fs_size=$1
-       local tmp_dir=/tmp/
-
-       [ "$LARGE_SCRATCH_DEV" != yes ] && return 0
-       [ -z "$SCRATCH_DEV_EMPTY_SPACE" ] && SCRATCH_DEV_EMPTY_SPACE=0
-       [ $SCRATCH_DEV_EMPTY_SPACE -ge $fs_size ] && return 0
-
-       # Default free space in the FS is 50GB, but you can specify more via
-       # SCRATCH_DEV_EMPTY_SPACE
-       local space_to_consume=$(($fs_size - 50*1024*1024*1024 - $SCRATCH_DEV_EMPTY_SPACE))
-
-       # mount the filesystem and create 16TB - 4KB files until we consume
-       # all the necessary space.
-       _try_scratch_mount 2>&1 >$tmp_dir/mnt.err
-       local status=$?
-       if [ $status -ne 0 ]; then
-               echo "mount failed"
-               cat $tmp_dir/mnt.err >&2
-               rm -f $tmp_dir/mnt.err
-               return $status
-       fi
-       rm -f $tmp_dir/mnt.err
-
-       local file_size=$((16*1024*1024*1024*1024 - 4096))
-       local nfiles=0
-       while [ $space_to_consume -gt $file_size ]; do
-
-               xfs_io -F -f \
-                       -c "truncate $file_size" \
-                       -c "falloc -k 0 $file_size" \
-                       $SCRATCH_MNT/.use_space.$nfiles 2>&1
-               status=$?
-               if [ $status -ne 0 ]; then
-                       break;
-               fi
-
-               space_to_consume=$(( $space_to_consume - $file_size ))
-               nfiles=$(($nfiles + 1))
-       done
-
-       # consume the remaining space.
-       if [ $space_to_consume -gt 0 ]; then
-               xfs_io -F -f \
-                       -c "truncate $space_to_consume" \
-                       -c "falloc -k 0 $space_to_consume" \
-                       $SCRATCH_MNT/.use_space.$nfiles 2>&1
-               status=$?
-       fi
-       export NUM_SPACE_FILES=$nfiles
-
-       _scratch_unmount
-       if [ $status -ne 0 ]; then
-               echo "large file prealloc failed"
-               cat $tmp_dir/mnt.err >&2
-               return $status
-       fi
-       return 0
-}
-
-_scratch_mkfs_ext4()
-{
-       local mkfs_cmd="$MKFS_EXT4_PROG -F"
-       local mkfs_filter="grep -v -e ^Warning: -e \"^mke2fs \" | grep -v \"^$\""
-       local tmp=`mktemp -u`
-       local mkfs_status
-
-       [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
-           $mkfs_cmd -O journal_dev $MKFS_OPTIONS $SCRATCH_LOGDEV && \
-           mkfs_cmd="$mkfs_cmd -J device=$SCRATCH_LOGDEV"
-
-       _scratch_do_mkfs "$mkfs_cmd" "$mkfs_filter" $* 2>$tmp.mkfserr 1>$tmp.mkfsstd
-       mkfs_status=$?
-
-       if [ $mkfs_status -eq 0 -a "$LARGE_SCRATCH_DEV" = yes ]; then
-               # manually parse the mkfs output to get the fs size in bytes
-               local fs_size=`cat $tmp.mkfsstd | awk ' \
-                       /^Block size/ { split($2, a, "="); bs = a[2] ; } \
-                       / inodes, / { blks = $3 } \
-                       /reserved for the super user/ { resv = $1 } \
-                       END { fssize = bs * blks - resv; print fssize }'`
-
-               _setup_large_ext4_fs $fs_size
-               mkfs_status=$?
-       fi
-
-       # output mkfs stdout and stderr
-       cat $tmp.mkfsstd
-       cat $tmp.mkfserr >&2
-       rm -f $tmp.mkfserr $tmp.mkfsstd
-
-       return $mkfs_status
-}
-
-_ext4_metadump()
-{
-       local device="$1"
-       local dumpfile="$2"
-       local compressopt="$3"
-
-       test -n "$E2IMAGE_PROG" || _fail "e2image not installed"
-       $E2IMAGE_PROG -Q "$device" "$dumpfile"
-       [ "$compressopt" = "compress" ] && [ -n "$DUMP_COMPRESSOR" ] &&
-               $DUMP_COMPRESSOR -f "$dumpfile" &>> "$seqres.full"
-}
-
 # Capture the metadata of a filesystem in a dump file for offline analysis.
 # This is not supported by all filesystem types, so this function should only
 # be used after a test has already failed.
@@ -746,12 +577,18 @@ _test_mkfs()
     nfs*)
        # do nothing for nfs
        ;;
+    afs*)
+       # do nothing for afs
+       ;;
     cifs)
        # do nothing for cifs
        ;;
     9p)
        # do nothing for 9p
        ;;
+    fuse)
+       # do nothing for fuse
+       ;;
     virtiofs)
        # do nothing for virtiofs
        ;;
@@ -776,22 +613,33 @@ _test_mkfs()
     ext2|ext3|ext4)
        $MKFS_PROG -t $FSTYP -- -F $MKFS_OPTIONS $* $TEST_DEV
        ;;
+    xfs)
+       $MKFS_PROG -t $FSTYP -- -f $MKFS_OPTIONS $* $TEST_DEV
+       ;;
+    bcachefs)
+       $MKFS_BCACHEFS_PROG $MKFS_OPTIONS $* $TEST_DEV > /dev/null
+       ;;
     *)
        yes | $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS $* $TEST_DEV
        ;;
     esac
 }
 
-_mkfs_dev()
+_try_mkfs_dev()
 {
-    local tmp=`mktemp -u`
     case $FSTYP in
     nfs*)
        # do nothing for nfs
        ;;
+    afs*)
+       # do nothing for afs
+       ;;
     9p)
        # do nothing for 9p
        ;;
+    fuse)
+       # do nothing for fuse
+       ;;
     virtiofs)
        # do nothing for virtiofs
        ;;
@@ -802,26 +650,27 @@ _mkfs_dev()
        # do nothing for pvfs2
        ;;
     udf)
-        $MKFS_UDF_PROG $MKFS_OPTIONS $* 2>$tmp.mkfserr 1>$tmp.mkfsstd
+        $MKFS_UDF_PROG $MKFS_OPTIONS $*
        ;;
     btrfs)
-        $MKFS_BTRFS_PROG $MKFS_OPTIONS $* 2>$tmp.mkfserr 1>$tmp.mkfsstd
+        $MKFS_BTRFS_PROG $MKFS_OPTIONS $*
        ;;
     ext2|ext3|ext4)
-       $MKFS_PROG -t $FSTYP -- -F $MKFS_OPTIONS $* \
-               2>$tmp.mkfserr 1>$tmp.mkfsstd
+       $MKFS_PROG -t $FSTYP -- -F $MKFS_OPTIONS $*
        ;;
     xfs)
-       $MKFS_PROG -t $FSTYP -- -f $MKFS_OPTIONS $* \
-               2>$tmp.mkfserr 1>$tmp.mkfsstd
+       $MKFS_PROG -t $FSTYP -- -f $MKFS_OPTIONS $*
        ;;
     *)
-       yes | $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS $* \
-               2>$tmp.mkfserr 1>$tmp.mkfsstd
+       yes | $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS $*
        ;;
     esac
+}
 
-    if [ $? -ne 0 ]; then
+_mkfs_dev()
+{
+    local tmp=`mktemp -u`
+    if ! _try_mkfs_dev "$@" 2>$tmp.mkfserr 1>$tmp.mkfsstd; then
        # output stored mkfs output
        cat $tmp.mkfserr >&2
        cat $tmp.mkfsstd
@@ -831,7 +680,7 @@ _mkfs_dev()
     rm -f $tmp.mkfserr $tmp.mkfsstd
 }
 
-# remove all files in $SCRATCH_MNT, useful when testing on NFS/CIFS
+# remove all files in $SCRATCH_MNT, useful when testing on NFS/AFS/CIFS
 _scratch_cleanup_files()
 {
        case $FSTYP in
@@ -859,7 +708,7 @@ _scratch_mkfs()
        local mkfs_status
 
        case $FSTYP in
-       nfs*|cifs|ceph|overlay|glusterfs|pvfs2|9p|virtiofs)
+       nfs*|afs|cifs|ceph|overlay|glusterfs|pvfs2|9p|fuse|virtiofs)
                # unable to re-create this fstyp, just remove all files in
                # $SCRATCH_MNT to avoid EEXIST caused by the leftover files
                # created in previous runs
@@ -912,6 +761,10 @@ _scratch_mkfs()
                mkfs_cmd="yes | $MKFS_PROG -t $FSTYP --"
                mkfs_filter="grep -v -e ^mkfs\.ocfs2"
                ;;
+    bcachefs)
+               mkfs_cmd="$MKFS_BCACHEFS_PROG"
+               mkfs_filter="cat"
+               ;;
        *)
                mkfs_cmd="yes | $MKFS_PROG -t $FSTYP --"
                mkfs_filter="cat"
@@ -987,8 +840,9 @@ _spare_dev_put()
 # to make sure it has the enough scratch devices including
 # replace-target and spare device. Now arg1 here is the
 # required number of scratch devices by a-test-case excluding
-# the replace-target and spare device. So this function will
-# set SCRATCH_DEV_POOL to the specified number of devices.
+# the replace-target and spare device. So, this function sets
+# SCRATCH_DEV_POOL to the specified number of devices and also
+# sets a SCRATCH_DEV_NAME array with the names of the devices.
 #
 # Usage:
 #  _scratch_dev_pool_get() <ndevs>
@@ -1019,19 +873,28 @@ _scratch_dev_pool_get()
        export SCRATCH_DEV_POOL_SAVED
        SCRATCH_DEV_POOL=${devs[@]:0:$test_ndevs}
        export SCRATCH_DEV_POOL
+       SCRATCH_DEV_NAME=( $SCRATCH_DEV_POOL )
+       export SCRATCH_DEV_NAME
 }
 
 _scratch_dev_pool_put()
 {
+       local ret1
+       local ret2
+
        typeset -p SCRATCH_DEV_POOL_SAVED >/dev/null 2>&1
-       if [ $? -ne 0 ]; then
+       ret1=$?
+       typeset -p SCRATCH_DEV_NAME >/dev/null 2>&1
+       ret2=$?
+       if [[ $ret1 -ne 0 || $ret2 -ne 0 ]]; then
                _fail "Bug: unset val, must call _scratch_dev_pool_get before _scratch_dev_pool_put"
        fi
 
-       if [ -z "$SCRATCH_DEV_POOL_SAVED" ]; then
+       if [[ -z "$SCRATCH_DEV_POOL_SAVED" || -z "${SCRATCH_DEV_NAME[@]}" ]]; then
                _fail "Bug: str empty, must call _scratch_dev_pool_get before _scratch_dev_pool_put"
        fi
 
+       export SCRATCH_DEV_NAME=()
        export SCRATCH_DEV_POOL=$SCRATCH_DEV_POOL_SAVED
        export SCRATCH_DEV_POOL_SAVED=""
 }
@@ -1075,6 +938,35 @@ _check_minimal_fs_size()
        fi
 }
 
+# Round a proposed filesystem size up to the minimium supported size.  The
+# input is in MB and so is the output.
+_small_fs_size_mb()
+{
+       local size="$1"
+       local runner_min_size=0
+       local fs_min_size=0
+
+       case "$FSTYP" in
+       xfs)
+               # xfs no longer supports filesystems smaller than 600m
+               fs_min_size=600
+               ;;
+       f2fs)
+               # f2fs-utils 1.9.0 needs at least 38 MB space for f2fs image.
+               # However, f2fs-utils 1.14.0 needs at least 52 MB. Not sure if
+               # it will change again. So just set it 128M.
+               fs_min_size=128
+               ;;
+       esac
+       (( size < fs_min_size )) && size="$fs_min_size"
+
+       # If the test runner wanted a minimum size, enforce that here.
+       test -n "$MIN_FSSIZE" && runner_min_size=$((MIN_FSSIZE / 1048576))
+       (( size < runner_min_size)) && size="$runner_min_size"
+
+       echo "$size"
+}
+
 # Create fs of certain size on scratch device
 # _scratch_mkfs_sized <size in bytes> [optional blocksize]
 _scratch_mkfs_sized()
@@ -1082,6 +974,7 @@ _scratch_mkfs_sized()
        local fssize=$1
        local blocksize=$2
        local def_blksz
+       local blocksize_opt
 
        case $FSTYP in
        xfs)
@@ -1090,12 +983,25 @@ _scratch_mkfs_sized()
        btrfs)
                def_blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*-s ?+([0-9]+).*/\1/p'`
                ;;
-       ext2|ext3|ext4|ext4dev|udf|reiser4|ocfs2|reiserfs)
+       ext2|ext3|ext4|ext4dev|reiser4|ocfs2|reiserfs)
                def_blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*-b ?+([0-9]+).*/\1/p'`
                ;;
+       udf)
+               def_blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*(-b|--blocksize)[ =]?+([0-9]+).*/\2/p'`
+               if [ -z "$def_blksz" ]; then
+                       def_blksz=512
+               fi
+               ;;
        jfs)
                def_blksz=4096
                ;;
+       bcachefs)
+               def_blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*(--block_size)[ =]?+([0-9]+).*/\2/p'`
+               [ -n "$blocksize" ] && blocksize_opt="--block_size=$blocksize"
+               [ -n "$def_blksize" ] && blocksize_opt="--block_size=$def_blksize"
+               # If no block size is given by local.confg or parameter, blocksize_opt is empty.
+               # Let MKFS_BCACHEFS_PROG decide block size on its own.
+               ;;
        esac
 
        [ -n "$def_blksz" ] && blocksize=$def_blksz
@@ -1127,7 +1033,7 @@ _scratch_mkfs_sized()
        case $FSTYP in
        xfs)
                # don't override MKFS_OPTIONS that set a block size.
-               echo $MKFS_OPTIONS |egrep -q "b?size="
+               echo $MKFS_OPTIONS |grep -E -q "b\s*size="
                if [ $? -eq 0 ]; then
                        _scratch_mkfs_xfs -d size=$fssize $rt_ops
                else
@@ -1135,6 +1041,13 @@ _scratch_mkfs_sized()
                fi
                ;;
        ext2|ext3|ext4|ext4dev)
+               # Can't use _scratch_mkfs_ext4 here because the block count has
+               # to come after the device path.
+               if [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ]; then
+                       ${MKFS_PROG} -F -O journal_dev $MKFS_OPTIONS $SCRATCH_LOGDEV || \
+                               _notrun "Could not make scratch logdev"
+                       MKFS_OPTIONS="$MKFS_OPTIONS -J device=$SCRATCH_LOGDEV"
+               fi
                ${MKFS_PROG} -t $FSTYP -F $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV $blocks
                ;;
        gfs2)
@@ -1190,7 +1103,7 @@ _scratch_mkfs_sized()
                export MOUNT_OPTIONS="-o size=$fssize $TMPFS_MOUNT_OPTIONS"
                ;;
        bcachefs)
-               $MKFS_PROG -t $FSTYP -- $MKFS_OPTIONS --fs_size=$fssize --block_size=$blocksize $SCRATCH_DEV
+               $MKFS_BCACHEFS_PROG $MKFS_OPTIONS --fs_size=$fssize $blocksize_opt $SCRATCH_DEV
                ;;
        *)
                _notrun "Filesystem $FSTYP not supported in _scratch_mkfs_sized"
@@ -1212,13 +1125,13 @@ _scratch_mkfs_geom()
 
     case $FSTYP in
     xfs)
-       if echo "$MKFS_OPTIONS" | egrep -q "b?size="; then
-               MKFS_OPTIONS=$(echo "$MKFS_OPTIONS" | sed -r "s/(b?size=)[0-9]+k?/\1$blocksize/")
+       if echo "$MKFS_OPTIONS" | grep -E -q "b\s*size="; then
+               MKFS_OPTIONS=$(echo "$MKFS_OPTIONS" | sed -r "s/(b\s*size=)[0-9]+k?/\1$blocksize/")
        else
                MKFS_OPTIONS+=" -b size=$blocksize"
        fi
 
-       if echo "$MKFS_OPTIONS" | egrep -q "(su|sunit|sw|swidth)="; then
+       if echo "$MKFS_OPTIONS" | grep -E -q "(su|sunit|sw|swidth)="; then
                MKFS_OPTIONS=$(echo "$MKFS_OPTIONS" | sed -r \
                        -e "s/(su|sunit)=[0-9kmg]+/su=$sunit_bytes/" \
                        -e "s/(sw|swidth)=[0-9kmg]+/sw=$swidth_mult/")
@@ -1246,6 +1159,9 @@ _scratch_mkfs_blocksized()
        if ! [[ $blocksize =~ $re ]] ; then
                _notrun "error: _scratch_mkfs_sized: block size \"$blocksize\" not an integer."
        fi
+       if [ $blocksize -lt $(_get_page_size) ]; then
+               _exclude_scratch_mount_option dax
+       fi
 
        case $FSTYP in
        btrfs)
@@ -1261,7 +1177,7 @@ _scratch_mkfs_blocksized()
                _scratch_mkfs_xfs $MKFS_OPTIONS -b size=$blocksize
                ;;
        ext2|ext3|ext4)
-               ${MKFS_PROG} -t $FSTYP -F $MKFS_OPTIONS -b $blocksize $SCRATCH_DEV
+               _scratch_mkfs_ext4 $MKFS_OPTIONS -b $blocksize
                ;;
        gfs2)
                ${MKFS_PROG} -t $FSTYP $MKFS_OPTIONS -O -b $blocksize $SCRATCH_DEV
@@ -1271,8 +1187,10 @@ _scratch_mkfs_blocksized()
                                                -C $blocksize $SCRATCH_DEV
                ;;
        bcachefs)
-               ${MKFS_PROG} -t $FSTYP $MKFS_OPTIONS --block_size=$blocksize \
-                                                               $SCRATCH_DEV
+               _scratch_mkfs --block_size=$blocksize
+               ;;
+       udf)
+               _scratch_mkfs -b $blocksize
                ;;
        *)
                _notrun "Filesystem $FSTYP not supported in _scratch_mkfs_blocksized"
@@ -1320,6 +1238,15 @@ _repair_scratch_fs()
        fi
        return $res
         ;;
+    btrfs)
+       echo "yes|$BTRFS_UTIL_PROG check --repair --force $SCRATCH_DEV"
+       yes | $BTRFS_UTIL_PROG check --repair --force $SCRATCH_DEV 2>&1
+       local res=$?
+       if [ $res -ne 0 ]; then
+               _dump_err2 "btrfs repair failed, err=$res"
+       fi
+       return $res
+       ;;
     bcachefs)
        # With bcachefs, if fsck detects any errors we consider it a bug and we
        # want the test to fail:
@@ -1351,6 +1278,54 @@ _repair_scratch_fs()
     esac
 }
 
+_repair_test_fs()
+{
+       case $FSTYP in
+       xfs)
+               _test_xfs_repair "$@" >$tmp.repair 2>&1
+               res=$?
+               if [ "$res" -ne 0 ]; then
+                       echo "xfs_repair returns $res; replay log?" >>$tmp.repair
+                       _test_mount
+                       res=$?
+                       if [ $res -gt 0 ]; then
+                               echo "mount returns $res; zap log?" >>$tmp.repair
+                               _test_xfs_repair -L >>$tmp.repair 2>&1
+                               echo "log zap returns $?" >> $tmp.repair
+                       else
+                               umount "$TEST_DEV"
+                       fi
+                       _test_xfs_repair "$@" >>$tmp.repair 2>&1
+                       res=$?
+               fi
+               ;;
+       btrfs)
+       echo 'yes|$BTRFS_UTIL_PROG check --repair --force "$TEST_DEV"' > \
+                                                               $tmp.repair 2>&1
+       yes | $BTRFS_UTIL_PROG check --repair --force "$TEST_DEV" >> \
+                                                               $tmp.repair 2>&1
+               res=$?
+               ;;
+       *)
+               # Let's hope fsck -y suffices...
+               fsck -t $FSTYP -y $TEST_DEV >$tmp.repair 2>&1
+               res=$?
+               if test "$res" -lt 4 ; then
+                       res=0
+               fi
+               ;;
+       esac
+       if [ $res -ne 0 ]; then
+               _log_err "_repair_test_fs: failed, err=$res"
+               echo "*** fsck.$FSTYP output ***"       >>$seqres.full
+               cat $tmp.repair                         >>$seqres.full
+               echo "*** end fsck.$FSTYP output"       >>$seqres.full
+
+       fi
+       rm -f $tmp.repair
+       return $res
+}
+
 _get_pids_by_name()
 {
     if [ $# -ne 1 ]
@@ -1660,6 +1635,26 @@ _fixed_by_kernel_commit()
        _fixed_by_git_commit kernel $*
 }
 
+# Compare with _fixed_by_* helpers, this helper is used for test cases
+# are not regression tests, e.g. functional tests or maintainer tests,
+# this helper suggests git commits that should be applied to source trees
+# to avoid test failures.
+_wants_git_commit()
+{
+       local pkg=$1
+       shift
+
+       echo "This test wants $pkg fix:" >> $seqres.hints
+       echo "      $*" >> $seqres.hints
+       echo >> $seqres.hints
+}
+
+# Refer to _wants_git_commit
+_wants_kernel_commit()
+{
+       _wants_git_commit kernel $*
+}
+
 _check_if_dev_already_mounted()
 {
        local dev=$1
@@ -1703,7 +1698,7 @@ _check_mounted_on()
 
        if [ -n "$type" -a "`_fs_type $dev`" != "$type" ]; then
                echo "$devname=$dev is mounted but not a type $type filesystem"
-               # raw $DF_PROG cannot handle NFS/CIFS/overlay correctly
+               # raw $DF_PROG cannot handle NFS/AFS/CIFS/overlay correctly
                _df_device $dev
                return 3 # 3 = mounted as wrong type
        fi
@@ -1717,7 +1712,7 @@ _require_scratch_nocheck()
 {
     case "$FSTYP" in
        glusterfs)
-               echo $SCRATCH_DEV | egrep -q ":/?" > /dev/null 2>&1
+               echo $SCRATCH_DEV | grep -E -q ":/?" > /dev/null 2>&1
                if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
                        _notrun "this test requires a valid \$SCRATCH_DEV"
                fi
@@ -1725,7 +1720,7 @@ _require_scratch_nocheck()
                        _notrun "this test requires a valid \$SCRATCH_MNT"
                fi
                ;;
-       9p|virtiofs)
+       9p|fuse|virtiofs)
                if [ -z "$SCRATCH_DEV" ]; then
                        _notrun "this test requires a valid \$SCRATCH_DEV"
                fi
@@ -1742,6 +1737,17 @@ _require_scratch_nocheck()
                        _notrun "this test requires a valid \$SCRATCH_MNT"
                fi
                ;;
+       afs)
+               # We only support RW volumes (marked with a '%')
+               # We don't support RO volumes (marked with a '#')
+               echo $SCRATCH_DEV | grep -q "^%" > /dev/null 2>&1
+               if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
+                       _notrun "this test requires a valid \$SCRATCH_DEV (must be a RW volume)"
+               fi
+               if [ ! -d "$SCRATCH_MNT" ]; then
+                       _notrun "this test requires a valid \$SCRATCH_MNT"
+               fi
+               ;;
        ceph)
                echo $SCRATCH_DEV | grep -qE "=/|:/" > /dev/null 2>&1
                if [ -z "$SCRATCH_DEV" -o "$?" != "0" ]; then
@@ -1823,7 +1829,7 @@ _require_scratch_nocheck()
             exit 1
         fi
     fi
-    rm -f ${RESULT_DIR}/require_scratch
+    rm -f ${RESULT_DIR}/require_scratch "$RESULT_DIR/.skip_orebuild" "$RESULT_DIR/.skip_rebuild"
 }
 
 # we need the scratch device and it needs to not be an lvm device
@@ -1862,7 +1868,7 @@ _require_scratch_size()
 
        _require_scratch
        local devsize=`_get_device_size $SCRATCH_DEV`
-       [ $devsize -lt $1 ] && _notrun "scratch dev too small"
+       [ $devsize -lt $1 ] && _notrun "scratch device too small, $devsize < $1"
 }
 
 # require a scratch dev of a minimum size (in kb) and should not be checked
@@ -1873,7 +1879,7 @@ _require_scratch_size_nocheck()
 
        _require_scratch_nocheck
        local devsize=`_get_device_size $SCRATCH_DEV`
-       [ $devsize -lt $1 ] && _notrun "scratch dev too small"
+       [ $devsize -lt $1 ] && _notrun "scratch device size too small, $devsize < $1"
 }
 
 # Require scratch fs which supports >16T of filesystem size.
@@ -1911,13 +1917,27 @@ _require_scratch_delalloc()
        _scratch_unmount
 }
 
+# Require test fs supports delay allocation.
+_require_test_delalloc()
+{
+       _require_command "$FILEFRAG_PROG" filefrag
+
+       rm -f $TEST_DIR/testy
+       $XFS_IO_PROG -f -c 'pwrite 0 64k' $TEST_DIR/testy &> /dev/null
+       $FILEFRAG_PROG -v $TEST_DIR/testy 2>&1 | grep -q delalloc
+       res=$?
+       rm -f $TEST_DIR/testy
+       test $res -eq 0 || \
+               _notrun "test requires delayed allocation buffered writes"
+}
+
 # this test needs a test partition - check we're ok & mount it
 #
 _require_test()
 {
     case "$FSTYP" in
        glusterfs)
-               echo $TEST_DEV | egrep -q ":/?" > /dev/null 2>&1
+               echo $TEST_DEV | grep -E -q ":/?" > /dev/null 2>&1
                if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
                        _notrun "this test requires a valid \$TEST_DEV"
                fi
@@ -1925,7 +1945,7 @@ _require_test()
                        _notrun "this test requires a valid \$TEST_DIR"
                fi
                ;;
-       9p|virtiofs)
+       9p|fuse|virtiofs)
                if [ -z "$TEST_DEV" ]; then
                        _notrun "this test requires a valid \$TEST_DEV"
                fi
@@ -1942,6 +1962,17 @@ _require_test()
                        _notrun "this test requires a valid \$TEST_DIR"
                fi
                ;;
+       afs)
+               # We only support RW volumes (marked with a '%')
+               # We don't support RO volumes (marked with a '#')
+               echo $TEST_DEV | grep -q "^%" > /dev/null 2>&1
+               if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
+                       _notrun "this test requires a valid \$TEST_DEV"
+               fi
+               if [ ! -d "$TEST_DIR" ]; then
+                       _notrun "this test requires a valid \$TEST_DIR"
+               fi
+               ;;
        ceph)
                echo $TEST_DEV | grep -qE "=/|:/" > /dev/null 2>&1
                if [ -z "$TEST_DEV" -o "$?" != "0" ]; then
@@ -2046,6 +2077,14 @@ _require_logdev()
     $UMOUNT_PROG $SCRATCH_LOGDEV 2>/dev/null
 }
 
+# This test requires that an external log device is not in use
+#
+_require_no_logdev()
+{
+       [ "$USE_EXTERNAL" = "yes" ] && [ -n "$SCRATCH_LOGDEV" ] && \
+               _notrun "Test not compatible with external logs, skipped this test"
+}
+
 # this test requires loopback device support
 #
 _require_loop()
@@ -2163,21 +2202,33 @@ _require_sane_bdev_flush()
 # Decide if the scratch filesystem is likely to be mounted in fsdax mode.
 # It goes 3 ways based on mount options::
 #      1. "dax" or "dax=always" means always test using DAX
-#      2. "dax=never" means we'll never use DAX
+#      2. "dax=never" means we'll never use DAX.
 #      3. "dax=inode" or nothing means "use scratch dev capability" to
 #          determine whether DAX is going to be used.
 #
-# Returns 0 if DAX will be used, 1 if DAX is not going to be used.
+# Case 2 and 3 basically mean the same thing for the purpose of
+# _require_dm_target(). If the fs is not forcing the use of DAX, then DAX
+# can only be enabled if the underlying block device supports it.
+#
+# Returns 0 if the filesytem will use DAX, 1 if it won't.
 __scratch_uses_fsdax()
 {
        local ops=$(_normalize_mount_options "$MOUNT_OPTIONS")
 
-       echo $ops | egrep -qw "dax(=always| |$)" && return 0
-       echo $ops | grep -qw "dax=never" && return 1
+       echo $ops | grep -E -qw "dax(=always| |$)" && return 0
+       return 1
+}
 
+# Determine if the scratch device is DAX capable. Even if the fs is not
+# using DAX, we still can't use certain device mapper targets if the block
+# device is DAX capable. Hence the check needs to be separat from the FS
+# capability.
+__scratch_dev_has_dax()
+{
        local sysfs="/sys/block/$(_short_dev $SCRATCH_DEV)"
        test -e "${sysfs}/dax" && return 0
        test "$(cat "${sysfs}/queue/dax" 2>/dev/null)" = "1" && return 0
+
        return 1
 }
 
@@ -2194,15 +2245,18 @@ _require_dm_target()
        _require_sane_bdev_flush $SCRATCH_DEV
        _require_command "$DMSETUP_PROG" dmsetup
 
-       if __scratch_uses_fsdax; then
-               case $target in
-               stripe|linear|log-writes)
-                       ;;
-               *)
-                       _notrun "Cannot run tests with DAX on $target devices."
-                       ;;
-               esac
-       fi
+       case $target in
+       stripe|linear|log-writes)
+               ;;
+       *)
+               if __scratch_uses_fsdax; then
+                       _notrun "Cannot run tests with fsdax on $target devices."
+               fi
+               if __scratch_dev_has_dax; then
+                       _notrun "Cannot use $target devices on DAX capable block devices."
+               fi
+               ;;
+       esac
 
        modprobe dm-$target >/dev/null 2>&1
 
@@ -2211,12 +2265,10 @@ _require_dm_target()
                _notrun "This test requires dm $target support"
        fi
 
-       # dm-error cannot handle the zone information
-       #
        # dm-snapshot and dm-thin-pool cannot ensure sequential writes on
        # the backing device
        case $target in
-       error|snapshot|thin-pool)
+       snapshot|thin-pool)
                _require_non_zoned_device ${SCRATCH_DEV}
                ;;
        esac
@@ -2266,33 +2318,6 @@ _require_non_zoned_device()
        fi
 }
 
-# this test requires the ext4 kernel support crc feature on scratch device
-#
-_require_scratch_ext4_crc()
-{
-       _scratch_mkfs_ext4 >/dev/null 2>&1
-       dumpe2fs -h $SCRATCH_DEV 2> /dev/null | grep -q metadata_csum || _notrun "metadata_csum not supported by this filesystem"
-       _try_scratch_mount >/dev/null 2>&1 \
-          || _notrun "Kernel doesn't support metadata_csum feature"
-       _scratch_unmount
-}
-
-# Check whether the specified feature whether it is supported by
-# mkfs.ext4 and the kernel.
-_require_scratch_ext4_feature()
-{
-    if [ -z "$1" ]; then
-        echo "Usage: _require_scratch_ext4_feature feature"
-        exit 1
-    fi
-    $MKFS_EXT4_PROG -F $MKFS_OPTIONS -O "$1" \
-                   $SCRATCH_DEV 512m >/dev/null 2>&1 \
-       || _notrun "mkfs.ext4 doesn't support $1 feature"
-    _try_scratch_mount >/dev/null 2>&1 \
-       || _notrun "Kernel doesn't support the ext4 feature(s): $1"
-    _scratch_unmount
-}
-
 # this test requires that external log/realtime devices are not in use
 #
 _require_nonexternal()
@@ -2338,6 +2363,8 @@ _require_aiodio()
 # this test requires that the kernel supports IO_URING
 _require_io_uring()
 {
+       local n
+
        $here/src/feature -R
        case $? in
        0)
@@ -2345,6 +2372,14 @@ _require_io_uring()
        1)
                _notrun "kernel does not support IO_URING"
                ;;
+       2)
+               n=$(sysctl -n kernel.io_uring_disabled 2>/dev/null)
+               if [ "$n" != "0" ];then
+                       _notrun "io_uring isn't enabled totally by admin"
+               else
+                       _fail "unexpected EPERM error, please check selinux or something else"
+               fi
+               ;;
        *)
                _fail "unexpected error testing for IO_URING support"
                ;;
@@ -2622,7 +2657,7 @@ _require_xfs_io_command()
                # Test xfs_io chattr support AND
                # filesystem FS_IOC_FSSETXATTR support
                # 'tPnE' flags are only valid for a directory so check them on a directory.
-               if echo "$param" | egrep -q 't|P|n|E'; then
+               if echo "$param" | grep -E -q 't|P|n|E'; then
                        testio=`$XFS_IO_PROG -F -c "chattr +$param" $testdir 2>&1`
                        attr_info=`$XFS_IO_PROG -F -r -c "lsattr" $testdir | awk '{print $1}'`
                        $XFS_IO_PROG -F -r -c "chattr -$param" $testdir 2>&1
@@ -2655,7 +2690,7 @@ _require_xfs_io_command()
                param_checked="$param"
                ;;
        "fpunch" | "fcollapse" | "zero" | "fzero" | "finsert" | "funshare")
-               local blocksize=$(_get_block_size $TEST_DIR)
+               local blocksize=$(_get_file_block_size $TEST_DIR)
                testio=`$XFS_IO_PROG -F -f -c "pwrite 0 $((5 * $blocksize))" \
                        -c "fsync" -c "$command $blocksize $((2 * $blocksize))" \
                        $testfile 2>&1`
@@ -2680,7 +2715,7 @@ _require_xfs_io_command()
        "-T")
                # Check O_TMPFILE support in xfs_io, kernel and fs
                testio=`$XFS_IO_PROG -T -c quit $TEST_DIR 2>&1`
-               echo $testio | egrep -q "invalid option|Is a directory" && \
+               echo $testio | grep -E -q "invalid option|Is a directory" && \
                        _notrun "xfs_io $command support is missing"
                echo $testio | grep -q "Operation not supported" && \
                        _notrun "O_TMPFILE is not supported"
@@ -2713,9 +2748,35 @@ _require_xfs_io_command()
                param_checked="$pwrite_opts $param"
                ;;
        "scrub"|"repair")
-               testio=`$XFS_IO_PROG -x -c "$command probe" $TEST_DIR 2>&1`
+               test -z "$param" && param="probe"
+               testio=`$XFS_IO_PROG -x -c "$command $param" $TEST_DIR 2>&1`
                echo $testio | grep -q "Inappropriate ioctl" && \
                        _notrun "xfs_io $command support is missing"
+               param_checked="$param"
+               ;;
+       "startupdate"|"commitupdate"|"cancelupdate")
+               $XFS_IO_PROG -f -c 'pwrite -S 0x58 0 128k -b 128k' $testfile > /dev/null
+               testio=$($XFS_IO_PROG -c "startupdate $param" \
+                               -c 'pwrite -S 0x59 0 192k' \
+                               -c 'commitupdate' $testfile 2>&1)
+               echo $testio | grep -q "Inappropriate ioctl" && \
+                       _notrun "xfs_io $command $param support is missing"
+               echo $testio | grep -q "Operation not supported" && \
+                       _notrun "xfs_io $command $param kernel support is missing"
+               param_checked="$param"
+               ;;
+       "swapext")
+               $XFS_IO_PROG -f -c 'pwrite -S 0x58 0 128k -b 128k' $testfile > /dev/null
+               $XFS_IO_PROG -f -c 'truncate 128k' $testfile.1 > /dev/null
+               testio=`$XFS_IO_PROG -c "$command $param $testfile.1" $testfile 2>&1`
+               echo $testio | grep -q "bad argument count" && \
+                       _notrun "xfs_io $command $param support is missing"
+               echo $testio | grep -q "Inappropriate ioctl" && \
+                       _notrun "xfs_io $command $param ioctl support is missing"
+               echo $testio | grep -q "Operation not supported" && \
+                       _notrun "xfs_io $command $param kernel support is missing"
+               rm -f $testfile.1
+               param_checked="$param"
                ;;
        "utimes" )
                testio=`$XFS_IO_PROG -f -c "utimes 0 0 0 0" $testfile 2>&1`
@@ -2745,7 +2806,7 @@ _require_xfs_io_command()
        [ -n "$param" ] || return
 
        if [ -z "$param_checked" ]; then
-               $XFS_IO_PROG -c "help $command" | grep -E -q "^ $param ([a-zA-Z_]+ )?--" || \
+               $XFS_IO_PROG -c "help $command" | grep -E -q "^ $param ([a-zA-Z0-9_]+ )?--" || \
                        _notrun "xfs_io $command doesn't support $param"
        else
                # xfs_io could result in "command %c not supported" if it was
@@ -2768,9 +2829,13 @@ _require_xfs_io_command()
        fi
 }
 
-# check that kernel and filesystem support direct I/O
+# check that kernel and filesystem support direct I/O, and check if "$1" size
+# aligned (optional) is supported
 _require_odirect()
 {
+       local blocksize=$1
+       local align_args=${1:+"-b $1"}
+
        if [ $FSTYP = "ext4" ] || [ $FSTYP = "f2fs" ] ; then
                if echo "$MOUNT_OPTIONS" | grep -q "test_dummy_encryption"; then
                        _notrun "$FSTYP encryption doesn't support O_DIRECT"
@@ -2782,9 +2847,13 @@ _require_odirect()
                fi
        fi
        local testfile=$TEST_DIR/$$.direct
-       $XFS_IO_PROG -F -f -d -c "pwrite 0 20k" $testfile > /dev/null 2>&1
+       $XFS_IO_PROG -F -f -d -c "pwrite ${align_args} 0 20k" $testfile > /dev/null 2>&1
        if [ $? -ne 0 ]; then
-               _notrun "O_DIRECT is not supported"
+               if [ -n "$blocksize" ]; then
+                       _notrun "O_DIRECT aligned to $blocksize bytes is not supported"
+               else
+                       _notrun "O_DIRECT is not supported"
+               fi
        fi
        rm -f $testfile 2>&1 > /dev/null
 }
@@ -2837,7 +2906,7 @@ _require_scratch_swapfile()
        _scratch_mount
 
        # Minimum size for mkswap is 10 pages
-       _format_swapfile "$SCRATCH_MNT/swap" $(($(get_page_size) * 10)) > /dev/null
+       _format_swapfile "$SCRATCH_MNT/swap" $(($(_get_page_size) * 10)) > /dev/null
 
        # ext* has supported all variants of swap files since their
        # introduction, so swapon should not fail.
@@ -2881,17 +2950,30 @@ _require_fs_space()
 #
 # Check if the filesystem supports sparse files.
 #
-# Unfortunately there is no better way to do this than a manual black list.
+# Filesystems (such as CIFS mounted from a Windows server) that generally
+# support sparse files but are tricked into creating a non-sparse file by one
+# of the tests are treated here as not supporting sparse files. This special
+# treatment is done due to media wear-out concerns -- e.g., generic/129 would
+# write multiple terabytes of zeros if allowed to run on a filesystem that
+# ignores the request to make a file sparse.
 #
 _require_sparse_files()
 {
-    case $FSTYP in
-    hfsplus|exfat)
-        _notrun "Sparse files not supported by this filesystem type: $FSTYP"
-       ;;
-    *)
-        ;;
-    esac
+    local testfile="$TEST_DIR/$$.sparsefiletest"
+    rm -f "$testfile"
+
+    # The write size and offset are specifically chosen to trick the Windows
+    # SMB server implementation into dishonoring the request to create a sparse
+    # file, while still fitting into the 64 kb SMB1 maximum request size.
+    # This also creates a non-sparse file on vfat, exfat, and hfsplus.
+    $XFS_IO_PROG -f -c 'pwrite -b 51200 -S 0x61 1638400 51200' "$testfile" >/dev/null
+
+    resulting_file_size_kb=$( du -sk "$testfile" | cut -f 1 )
+    rm -f "$testfile"
+
+    # The threshold of 1 MB allows for filesystems with such large clusters.
+    [ $resulting_file_size_kb -gt 1024 ] && \
+       _notrun "Sparse files are not supported or do not work as expected"
 }
 
 _require_debugfs()
@@ -2900,21 +2982,25 @@ _require_debugfs()
     [ -d "$DEBUGFS_MNT/boot_params" ] || _notrun "Debugfs not mounted"
 }
 
-_require_fail_make_request()
+#
+# Return the version of NFS in use on the mount on $1. Returns 0
+# if it's not NFS.
+#
+_nfs_version()
 {
-    [ -f "$DEBUGFS_MNT/fail_make_request/probability" ] \
-       || _notrun "$DEBUGFS_MNT/fail_make_request \
- not found. Seems that CONFIG_FAULT_INJECTION_DEBUG_FS kernel config option not enabled"
-}
+       local mountpoint=$1
+       local nfsvers=""
 
-# Disable extent zeroing for ext4 on the given device
-_ext4_disable_extent_zeroout()
-{
-       local dev=${1:-$TEST_DEV}
-       local sdev=`_short_dev $dev`
+       case "$FSTYP" in
+       nfs*)
+               nfsvers=`_mount | grep $1 | sed -n 's/^.*vers=\([0-9.]*\).*$/\1/p'`
+               ;;
+       *)
+               nfsvers="0"
+               ;;
+       esac
 
-       [ -f /sys/fs/ext4/$sdev/extent_max_zeroout_kb ] && \
-               echo 0 >/sys/fs/ext4/$sdev/extent_max_zeroout_kb
+       echo "$nfsvers"
 }
 
 # The default behavior of SEEK_HOLE is to always return EOF.
@@ -2940,7 +3026,7 @@ _fstyp_has_non_default_seek_data_hole()
        nfs*)
                # NFSv2, NFSv3, and NFSv4.0/4.1 only support default behavior of SEEK_HOLE,
                # while NFSv4.2 supports non-default behavior
-               local nfsvers=`_mount() | grep $TEST_DEV | sed -n 's/^.*vers=\([0-9.]*\).*$/\1/p'`
+               local nfsvers=$( _nfs_version "$TEST_DIR" )
                [ "$nfsvers" = "4.2" ]
                return $?
                ;;
@@ -3014,15 +3100,6 @@ _require_scratch_richacl_xfs()
        _scratch_unmount
 }
 
-_require_scratch_richacl_ext4()
-{
-       _scratch_mkfs -O richacl >/dev/null 2>&1 \
-               || _notrun "can't mkfs $FSTYP with option -O richacl"
-       _try_scratch_mount >/dev/null 2>&1 \
-               || _notrun "kernel doesn't support richacl feature on $FSTYP"
-       _scratch_unmount
-}
-
 _require_scratch_richacl_support()
 {
        _scratch_mount
@@ -3053,7 +3130,7 @@ _scratch_mkfs_richacl()
                ;;
        ext4)   _scratch_mkfs -O richacl
                ;;
-       nfs*|cifs|overlay)
+       nfs*|afs|cifs|overlay)
                _scratch_mkfs
                ;;
        esac
@@ -3146,7 +3223,7 @@ _mount_or_remount_rw()
 
        if [ $USE_REMOUNT -eq 0 ]; then
                if [ "$FSTYP" != "overlay" ]; then
-                       _mount -t $FSTYP $mount_opts $device $mountpoint
+                       _mount -t $FSTYP$FUSE_SUBTYP $mount_opts $device $mountpoint
                        _idmapped_mount $device $mountpoint
                else
                        _overlay_mount $device $mountpoint
@@ -3230,7 +3307,7 @@ _check_generic_filesystem()
 # Filter the knowen errors the UDF Verifier reports.
 _udf_test_known_error_filter()
 {
-       egrep -v "PVD  60  Error: Interchange Level: 1, Maximum Interchange Level: 0|FSD  28  Error: Interchange Level: 1, Maximum Interchange Level: 1,|PVD  72  Warning: Volume Set Identifier: \"\*IRIX UDF\",|Warning: [0-9]+ unused blocks NOT marked as unallocated."
+       grep -E -v "PVD  60  Error: Interchange Level: 1, Maximum Interchange Level: 0|FSD  28  Error: Interchange Level: 1, Maximum Interchange Level: 1,|PVD  72  Warning: Volume Set Identifier: \"\*IRIX UDF\",|Warning: [0-9]+ unused blocks NOT marked as unallocated."
 
 }
 
@@ -3255,17 +3332,32 @@ _check_udf_filesystem()
     fi
 
     local device=$1
-    local opt_arg=""
+    local blksz=`echo $MKFS_OPTIONS | sed -rn 's/.*(-b|--blocksize)[ =]?+([0-9]+).*/\2/p'`
+    if [ -z "$blksz" ]; then
+       blksz=512
+    fi
+    # Is the filesystem mounted?
+    local type=`_fs_type $device`
+    if [ "$type" = "$FSTYP" ]; then
+       blksz=`blockdev --getbsz $device`
+       local mountpoint=`_umount_or_remount_ro $device`
+    fi
+
+    local opt_arg="-ecclength 1 -blocksize $blksz"
     if [ $# -eq 2 ]; then
-       opt_arg="-lastvalidblock $(( $2 - 1 ))"
+       opt_arg+=" -lastvalidblock $(( $2 - 1 ))"
     fi
 
     rm -f $seqres.checkfs
     sleep 1 # Due to a problem with time stamps in udf_test
-    $here/src/udf_test $opt_arg $device | tee $seqres.checkfs | egrep "Error|Warning" | \
+    $here/src/udf_test $opt_arg $device | tee $seqres.checkfs | grep -E "Error|Warning" | \
        _udf_test_known_error_filter | \
-       egrep -iv "Error count:.*[0-9]+.*total occurrences:.*[0-9]+|Warning count:.*[0-9]+.*total occurrences:.*[0-9]+" && \
+       grep -E -iv "Error count:.*[0-9]+.*total occurrences:.*[0-9]+|Warning count:.*[0-9]+.*total occurrences:.*[0-9]+" && \
         echo "Warning UDF Verifier reported errors see $seqres.checkfs." && return 1
+    # Remount the filesystem
+    if [ "$type" = "$FSTYP" ]; then
+       _mount_or_remount_rw "$MOUNT_OPTIONS" $device $mountpoint
+    fi
     return 0
 }
 
@@ -3278,12 +3370,18 @@ _check_test_fs()
     nfs)
        # no way to check consistency for nfs
        ;;
+    afs)
+       # no way to check consistency for afs
+       ;;
     cifs)
        # no way to check consistency for cifs
        ;;
     9p)
        # no way to check consistency for 9p
        ;;
+    fuse)
+       # no way to check consistency for fuse
+       ;;
     virtiofs)
        # no way to check consistency for virtiofs
        ;;
@@ -3323,15 +3421,7 @@ _check_scratch_fs()
 
     case $FSTYP in
     xfs)
-       local scratch_log="none"
-       local scratch_rt="none"
-       [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
-           scratch_log="$SCRATCH_LOGDEV"
-
-       [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
-           scratch_rt="$SCRATCH_RTDEV"
-
-       _check_xfs_filesystem $device $scratch_log $scratch_rt
+       _check_xfs_scratch_fs $device
        ;;
     udf)
        _check_udf_filesystem $device $udf_fsize
@@ -3339,12 +3429,18 @@ _check_scratch_fs()
     nfs*)
        # Don't know how to check an NFS filesystem, yet.
        ;;
+    afs*)
+       # Don't know how to check an AFS filesystem, yet.
+       ;;
     cifs)
        # Don't know how to check a CIFS filesystem, yet.
        ;;
     9p)
        # no way to check consistency for 9p
        ;;
+    fuse)
+       # no way to check consistency for fuse
+       ;;
     virtiofs)
        # no way to check consistency for virtiofs
        ;;
@@ -3443,8 +3539,8 @@ _link_out_file_named()
                }
                print $result
                ' <$seqfull.cfg)
-       rm -f $1
-       ln -fs $(basename $1).$suffix $1
+       rm -f $1 || _fail "_link_out_file_named: failed to remove existing output file"
+       ln -fs $(basename $1).$suffix $1 || _fail "$(basename $1).$suffix: could not setup output file"
 }
 
 _link_out_file()
@@ -3626,9 +3722,15 @@ _require_scratch_dev_pool()
                            exit 1
                        fi
                fi
-               # to help better debug when something fails, we remove
-               # traces of previous btrfs FS on the dev.
-               dd if=/dev/zero of=$i bs=4096 count=100 > /dev/null 2>&1
+               # To help better debug when something fails, we remove
+               # traces of previous btrfs FS on the dev. For zoned devices we
+               # can't use dd as it'll lead to unaligned writes so simply
+               # reset the first two zones.
+               if [ "`_zone_type "$i"`" = "none" ]; then
+                       dd if=/dev/zero of=$i bs=4096 count=100 > /dev/null 2>&1
+               else
+                       $BLKZONE_PROG reset -c 2 $i
+               fi
        done
 }
 
@@ -3769,6 +3871,21 @@ _check_xflag()
        fi
 }
 
+# Make sure the given file access mode is set to use the pagecache.  If
+# userspace or kernel don't support statx or STATX_ATTR_DAX, we assume that
+# means pagecache.  The sole parameter must be a directory.
+_require_pagecache_access() {
+       local testfile="$1/testfile"
+
+       touch "$testfile"
+       if ! _check_s_dax "$testfile" 0 &>> $seqres.full; then
+               rm -f "$testfile"
+               _notrun 'test requires pagecache access'
+       fi
+
+       rm -f "$testfile"
+}
+
 # Check if dax mount options are supported
 #
 # $1 can be either 'dax=always' or 'dax'
@@ -3795,7 +3912,7 @@ _check_scratch_dax_mountopt()
 
        _try_scratch_mount "-o $option" > /dev/null 2>&1 || return 1
 
-       if _fs_options $SCRATCH_DEV | egrep -q "dax(=always|,|$)"; then
+       if _fs_options $SCRATCH_DEV | grep -E -q "dax(=always|,|$)"; then
                _scratch_unmount
                return 0
        else
@@ -4008,13 +4125,28 @@ _require_batched_discard()
        fi
        _require_fstrim
 
-       grep -q "not supported" <($FSTRIM_PROG $1 2>&1)
+       grep -q -E "not supported|not implemented" <($FSTRIM_PROG $1 2>&1)
        if [ "$?" = "0" ]
        then
                _notrun "FITRIM not supported on $1"
        fi
 }
 
+# Given a mountpoint and the device associated with that mountpoint, return the
+# maximum start offset that the FITRIM command will accept, in units of 1024
+# byte blocks.
+_discard_max_offset_kb()
+{
+       case "$FSTYP" in
+       xfs)
+               _xfs_discard_max_offset_kb "$1"
+               ;;
+       *)
+               $DF_PROG -k | awk -v dev="$2" -v mnt="$1" '$1 == dev && $7 == mnt { print $3 }'
+               ;;
+       esac
+}
+
 _require_dumpe2fs()
 {
        if [ -z "$DUMPE2FS_PROG" ]; then
@@ -4061,9 +4193,9 @@ _exclude_mount_option()
        shift
        while [ $# -gt 0 ]; do
                local pattern=$1
-               echo "$pattern" | egrep -q "dax(=always|$)" && \
+               echo "$pattern" | grep -E -q "dax(=always|$)" && \
                        pattern="dax(=always| |$)"
-               if echo $mnt_opts | egrep -q "$pattern"; then
+               if echo $mnt_opts | grep -E -q "$pattern"; then
                        _notrun "mount option \"$1\" not allowed in this test"
                fi
                shift
@@ -4084,9 +4216,12 @@ _require_atime()
 {
        _exclude_scratch_mount_option "noatime"
        case $FSTYP in
-       nfs|cifs|virtiofs)
+       nfs|afs|cifs|virtiofs)
                _notrun "atime related mount options have no effect on $FSTYP"
                ;;
+       ceph|ceph-fuse)
+               _notrun "atime not maintained by $FSTYP"
+               ;;
        esac
 
 }
@@ -4296,9 +4431,18 @@ _get_available_space()
                echo "Usage: _get_available_space <mnt>"
                exit 1
        fi
-       local avail_kb;
-       avail_kb=`$DF_PROG $1 | tail -n1 | awk '{ print $5 }'`
-       echo $((avail_kb * 1024))
+       $DF_PROG -B 1 $1 | tail -n1 | awk '{ print $5 }'
+}
+
+# get the total space in bytes
+#
+_get_total_space()
+{
+       if [ -z "$1" ]; then
+               echo "Usage: _get_total_space <mnt>"
+               exit 1
+       fi
+       $DF_PROG -B 1 $1 | tail -n1 | awk '{ print $3 }'
 }
 
 # return device size in kb
@@ -4329,7 +4473,7 @@ _dmesg_since_test_start()
 # _dmesg_since_test_start.
 _check_dmesg_for()
 {
-       _dmesg_since_test_start | egrep -q "$1"
+       _dmesg_since_test_start | grep -E -q "$1"
 }
 
 # Default filter for dmesg scanning.
@@ -4338,8 +4482,25 @@ _check_dmesg_for()
 # lockdep.
 _check_dmesg_filter()
 {
-       egrep -v -e "BUG: MAX_LOCKDEP_CHAIN_HLOCKS too low" \
-               -e "BUG: MAX_STACK_TRACE_ENTRIES too low"
+       local extra_filter=
+       local filter_file="${RESULT_DIR}/dmesg_filter"
+
+       test -e "$filter_file" && extra_filter="-f $filter_file"
+
+       grep -E -v -e "BUG: MAX_LOCKDEP_CHAIN_HLOCKS too low" \
+               -e "BUG: MAX_STACK_TRACE_ENTRIES too low" \
+               $extra_filter
+}
+
+# Add a simple expression to the default dmesg filter
+_add_dmesg_filter()
+{
+       local regexp="$1"
+       local filter_file="${RESULT_DIR}/dmesg_filter"
+
+       if [ ! -e "$filter_file" ] || ! grep -q "$regexp" "$filter_file"; then
+               echo "$regexp" >> "${RESULT_DIR}/dmesg_filter"
+       fi
 }
 
 # check dmesg log for WARNING/Oops/etc.
@@ -4356,7 +4517,7 @@ _check_dmesg()
        local filter=${1:-_check_dmesg_filter}
 
        _dmesg_since_test_start | $filter >$seqres.dmesg
-       egrep -q -e "kernel BUG at" \
+       grep -E -q -e "kernel BUG at" \
             -e "WARNING:" \
             -e "\bBUG:" \
             -e "Oops:" \
@@ -4379,12 +4540,62 @@ _check_dmesg()
        fi
 }
 
+# Log the arguments to the kernel log with userspace annotation, if possible.
+# Output is not sent to stdout.
+_kernlog()
+{
+       if [ -w /dev/ttyprintk ]; then
+               echo "$*" >> /dev/ttyprintk
+               return
+       fi
+
+       if [ -w /dev/kmsg ]; then
+               echo "[U] $*" >> /dev/kmsg
+               return
+       fi
+}
+
+# Convey stdin to the kernel log with userspace annotation, if possible.
+# Output will be appended to any file paths provided as arguments.
+_tee_kernlog()
+{
+       if [ -w /dev/ttyprintk ]; then
+               tee -a /dev/ttyprintk "$@"
+               return
+       fi
+
+       if [ -w /dev/kmsg ]; then
+               awk '{printf("[U] %s\n", $0) >> "/dev/kmsg"; printf("%s\n", $0);}' | tee -a "$@"
+               return
+       fi
+
+       tee -a "$@"
+}
+
+# Make whatever configuration changes we need ahead of testing fs shutdowns due
+# to unexpected IO errors while updating metadata.  The sole parameter should
+# be the fs device, e.g.  $SCRATCH_DEV.
+_prepare_for_eio_shutdown()
+{
+       local dev="$1"
+
+       case "$FSTYP" in
+       "xfs")
+               _xfs_prepare_for_eio_shutdown "$dev"
+               ;;
+       esac
+}
+
 # capture the kmemleak report
 _capture_kmemleak()
 {
        local kern_knob="$DEBUGFS_MNT/kmemleak"
        local leak_file="$1"
 
+       # Some callers pass in /dev/null when they want to clear the
+       # kernel's leak report file and do not care what was in that.
+       [ -f "$leak_file" ] && rm -f "$leak_file"
+
        # Tell the kernel to scan for memory leaks.  Apparently the write
        # returns before the scan is complete, so do it twice in the hopes
        # that twice is enough to capture all the leaks.
@@ -4565,6 +4776,32 @@ _get_file_block_size()
        esac
 }
 
+# Given a file path and a byte length of a file operation under test, ensure
+# that the length is an integer multiple of the file's allocation unit size.
+# In other words, skip the test unless (oplen ≡ alloc_unit mod 0).  This is
+# intended for file remapping operations.
+_require_congruent_file_oplen()
+{
+       local file="$1"
+       local alloc_unit=$(_get_file_block_size "$file")
+       local oplen="$2"
+
+       case $FSTYP in
+       nfs*|afs|cifs|9p|virtiofs|ceph|glusterfs|overlay|pvfs2)
+               # Network filesystems don't know about (or tell the client
+               # about) the underlying file allocation unit and they generally
+               # pass the file IO request to the underlying filesystem, so we
+               # don't have anything to check here.
+               return
+               ;;
+       esac
+
+       test $alloc_unit -gt $oplen && \
+               _notrun "$1: file alloc unit $alloc_unit larger than op length $oplen"
+       test $((oplen % alloc_unit)) -eq 0 || \
+               _notrun "$1: file alloc unit $alloc_unit not congruent with op length $oplen"
+}
+
 # Get the minimum block size of an fs.
 _get_block_size()
 {
@@ -4575,6 +4812,17 @@ _get_block_size()
        stat -f -c %S $1
 }
 
+# Obtain the directory block size of an fs.
+_get_dir_block_size()
+{
+       case "$FSTYP" in
+       xfs)
+               _xfs_get_dir_blocksize $1 ;;
+       *)
+               _get_block_size $1 ;;
+       esac
+}
+
 # Require that the fundamental allocation unit of a file is the same as the
 # filesystem block size.  The sole parameter must be the root dir of a
 # filesystem.
@@ -4586,7 +4834,7 @@ _require_file_block_size_equals_fs_block_size()
                _notrun "File allocation unit is larger than a filesystem block"
 }
 
-get_page_size()
+_get_page_size()
 {
        echo $(getconf PAGE_SIZE)
 }
@@ -4608,38 +4856,33 @@ run_fsx()
        rm -f $tmp.fsx
 }
 
-# Test for the existence of a sysfs entry at /sys/fs/$FSTYP/DEV/$ATTR
+_require_statx()
+{
+       $here/src/stat_test --check-statx ||
+       _notrun "This test requires the statx system call"
+}
+
+# Get the path to the sysfs directory for the fs on a device
 #
 # Only one argument is needed:
-#  - attr: path name under /sys/fs/$FSTYP/DEV
+#  - dev: mounted block device for the fs
 #
 # Usage example:
-#   _require_fs_sysfs error/fail_at_unmount
-_require_fs_sysfs()
+#   _fs_sysfs_dname /dev/mapper/scratch-dev
+_fs_sysfs_dname()
 {
-       local attr=$1
-       local dname
+       local dev=$1
+
+       if [ ! -b "$dev" ]; then
+               _fail "Usage: _fs_sysfs_dname <mounted_device>"
+       fi
 
        case "$FSTYP" in
        btrfs)
-               dname=$(findmnt -n -o UUID $TEST_DEV) ;;
+               _btrfs_get_fsid $dev ;;
        *)
-               dname=$(_short_dev $TEST_DEV) ;;
+               _short_dev $dev ;;
        esac
-
-       if [ -z "$attr" -o -z "$dname" ];then
-               _fail "Usage: _require_fs_sysfs <sysfs_attr_path>"
-       fi
-
-       if [ ! -e /sys/fs/${FSTYP}/${dname}/${attr} ];then
-               _notrun "This test requires /sys/fs/${FSTYP}/${dname}/${attr}"
-       fi
-}
-
-_require_statx()
-{
-       $here/src/stat_test --check-statx ||
-       _notrun "This test requires the statx system call"
 }
 
 # Write "content" into /sys/fs/$FSTYP/$DEV/$ATTR
@@ -4663,13 +4906,7 @@ _set_fs_sysfs_attr()
                _fail "Usage: _set_fs_sysfs_attr <mounted_device> <attr> <content>"
        fi
 
-       local dname
-       case "$FSTYP" in
-       btrfs)
-               dname=$(findmnt -n -o UUID ${dev}) ;;
-       *)
-               dname=$(_short_dev $dev) ;;
-       esac
+       local dname=$(_fs_sysfs_dname $dev)
 
        echo "$content" > /sys/fs/${FSTYP}/${dname}/${attr}
 }
@@ -4691,17 +4928,74 @@ _get_fs_sysfs_attr()
                _fail "Usage: _get_fs_sysfs_attr <mounted_device> <attr>"
        fi
 
-       local dname
-       case "$FSTYP" in
-       btrfs)
-               dname=$(findmnt -n -o UUID ${dev}) ;;
-       *)
-               dname=$(_short_dev $dev) ;;
-       esac
+       local dname=$(_fs_sysfs_dname $dev)
 
        cat /sys/fs/${FSTYP}/${dname}/${attr}
 }
 
+# Test for the existence of a sysfs entry at /sys/fs/$FSTYP/$DEV/$ATTR
+#
+# All arguments are necessary, and in this order:
+#  - dev: device name, e.g. $SCRATCH_DEV
+#  - attr: path name under /sys/fs/$FSTYP/$dev
+#
+# Usage example:
+#   _has_fs_sysfs_attr /dev/mapper/scratch-dev error/fail_at_unmount
+_has_fs_sysfs_attr()
+{
+       local dev=$1
+       local attr=$2
+
+       if [ ! -b "$dev" -o -z "$attr" ];then
+               _fail "Usage: _has_fs_sysfs_attr <mounted_device> <attr>"
+       fi
+
+       local dname=$(_fs_sysfs_dname $dev)
+
+       test -e /sys/fs/${FSTYP}/${dname}/${attr}
+}
+
+# Require the existence of a sysfs entry at /sys/fs/$FSTYP/$DEV/$ATTR
+# All arguments are necessary, and in this order:
+#  - dev: device name, e.g. $SCRATCH_DEV
+#  - attr: path name under /sys/fs/$FSTYP/$dev
+#
+# Usage example:
+#   _require_fs_sysfs_attr /dev/mapper/scratch-dev error/fail_at_unmount
+_require_fs_sysfs_attr()
+{
+       _has_fs_sysfs_attr "$@" && return
+
+       local dev=$1
+       local attr=$2
+       local dname=$(_fs_sysfs_dname $dev)
+
+       _notrun "This test requires /sys/fs/${FSTYP}/${dname}/${attr}"
+}
+
+# Test for the existence of a sysfs entry at /sys/fs/$FSTYP/DEV/$ATTR
+#
+# Only one argument is needed:
+#  - attr: path name under /sys/fs/$FSTYP/DEV
+#
+# Usage example:
+#   _has_fs_sysfs error/fail_at_unmount
+_has_fs_sysfs()
+{
+       _has_fs_sysfs_attr $TEST_DEV "$@"
+}
+
+# Require the existence of a sysfs entry at /sys/fs/$FSTYP/DEV/$ATTR
+_require_fs_sysfs()
+{
+       _has_fs_sysfs "$@" && return
+
+       local attr=$1
+       local dname=$(_short_dev $TEST_DEV)
+
+       _notrun "This test requires /sys/fs/${FSTYP}/${dname}/${attr}"
+}
+
 # Generic test for specific filesystem feature.
 # Currently only implemented to test overlayfs features.
 _require_scratch_feature()
@@ -4722,7 +5016,13 @@ _require_scratch_feature()
 # be UINT32_MAX * block_size, but other filesystems may allow up to LLONG_MAX.
 _get_max_file_size()
 {
-       local testfile=$TEST_DIR/maxfilesize.$seq
+       if [ -z $1 ] || [ ! -d $1 ]; then
+               echo "Missing mount point argument for _get_max_file_size"
+               exit 1
+       fi
+
+       local mnt=$1
+       local testfile=$mnt/maxfilesize.$seq
        local l=0
        local r=9223372036854775807 # LLONG_MAX
 
@@ -4738,6 +5038,7 @@ _get_max_file_size()
                        l=$m
                fi
        done
+       rm -f $testfile
        echo $l
 }
 
@@ -4797,6 +5098,11 @@ _dmsetup_remove()
 
 _dmsetup_create()
 {
+       # Wait for udev to settle so that the dm creation doesn't fail because
+       # some udev subprogram opened one of the block devices mentioned in the
+       # table string w/ O_EXCL.  Do it again at the end so that an immediate
+       # device open won't also fail.
+       $UDEV_SETTLE_PROG >/dev/null 2>&1
        $DMSETUP_PROG create "$@" >>$seqres.full 2>&1 || return 1
        $DMSETUP_PROG mknodes >/dev/null 2>&1
        $UDEV_SETTLE_PROG >/dev/null 2>&1
@@ -4927,6 +5233,16 @@ _require_negative_timestamps() {
        ceph|exfat)
                _notrun "$FSTYP does not support negative timestamps"
                ;;
+       nfs*)
+               #
+               # NFSv2/3 timestamps use 32-bit unsigned values, and so
+               # cannot represent values prior to the epoch
+               #
+               local nfsvers=$( _nfs_version "$TEST_DIR" )
+               if [ "$nfsvers" = "2" -o "$nfsvers" = "3" ]; then
+                       _notrun "$FSTYP version $nfsvers does not support negative timestamps"
+               fi
+               ;;
        esac
 }
 
@@ -5021,6 +5337,128 @@ hexdump()
        _fail "Use _hexdump(), please!"
 }
 
+# Try to create a file with inode->i_blocks >= (length / blocksize).
+# There may be some small overhead, e.g. ext2 filesystem allocates a
+# substantial number of blocks to store block mappings. Those are accounted
+# to i_blocks.
+_create_file_sized()
+{
+       local length=$1
+       local file=$2
+       local tmp=`mktemp -u`
+       local ret=0
+
+       $XFS_IO_PROG -ft -c "falloc 0 $length" $file >$tmp.out 2>&1
+       ret=$?
+       if (grep -Eq "Operation not supported|command .* not found" $tmp.out);then
+               # fallocate isn't supported, fallback to general buffer write
+               $XFS_IO_PROG -ft -c "pwrite 0 $length" $file >$tmp.out 2>&1
+               ret=$?
+       fi
+       [ $ret -ne 0 ] && cat $tmp.out
+       rm -f $tmp.out
+       return $ret
+}
+
+# Save an arbitrary coredump to the report directory.
+_save_coredump()
+{
+       local path="$1"
+
+       if [ -z "$seqres" ]; then
+               echo "$path: seqres is not defined; ignoring coredump!"
+               return 1
+       fi
+
+       local core_hash="$(_md5_checksum "$path")"
+       local out_file="${seqres}.core.${core_hash}"
+
+       for dump in "$out_file"*; do
+               if [ -s "$dump" ]; then
+                       rm -f "$path"
+                       return 0
+               fi
+       done
+
+       mv "$path" "$out_file"
+       test -z "$COREDUMP_COMPRESSOR" && return 0
+
+       $COREDUMP_COMPRESSOR -f "$out_file"
+}
+
+_require_sgid_inheritance()
+{
+       case $FSTYP in
+       afs)
+               _notrun "SGID-based group ID inheritance is not supported on $FSTYP"
+               ;;
+       esac
+}
+
+_require_use_local_uidgid()
+{
+       case $FSTYP in
+       afs)
+               _notrun "$FSTYP doesn't honour local uid and gid"
+               ;;
+       esac
+}
+
+_require_unix_perm_checking()
+{
+       case $FSTYP in
+       afs)
+               _notrun "$FSTYP doesn't perform traditional UNIX perm checking"
+               ;;
+       esac
+}
+
+# Decide if a soak test should continue looping.  The sole parameter is the
+# number of soak loops that the test wants to run by default.  The actual
+# loop iteration number is stored in SOAK_LOOPIDX until the loop completes.
+#
+# If the test runner set a SOAK_DURATION value, this predicate will keep
+# looping until it has run for at least that long.
+_soak_loop_running() {
+       local max_soak_loops="$1"
+
+       test -z "$SOAK_LOOPIDX" && SOAK_LOOPIDX=1
+
+       if [ -n "$SOAK_DURATION" ]; then
+               if [ -z "$SOAK_DEADLINE" ]; then
+                       SOAK_DEADLINE="$(( $(date +%s) + SOAK_DURATION))"
+               fi
+
+               local now="$(date +%s)"
+               if [ "$now" -gt "$SOAK_DEADLINE" ]; then
+                       unset SOAK_DEADLINE
+                       unset SOAK_LOOPIDX
+                       return 1
+               fi
+               SOAK_LOOPIDX=$((SOAK_LOOPIDX + 1))
+               return 0
+       fi
+
+       if [ "$SOAK_LOOPIDX" -gt "$max_soak_loops" ]; then
+               unset SOAK_LOOPIDX
+               return 1
+       fi
+       SOAK_LOOPIDX=$((SOAK_LOOPIDX + 1))
+       return 0
+}
+
+_require_unshare() {
+       unshare -f -r -m -p -U $@ true &>/dev/null || \
+               _notrun "unshare $*: command not found, should be in util-linux"
+}
+
+# Return a random file in a directory. A directory is *not* followed
+# recursively.
+_random_file() {
+       local basedir=$1
+       echo "$basedir/$(ls -U $basedir | shuf -n 1)"
+}
+
 init_rc
 
 ################################################################################
index 76e9cb7d327ab88ecd94b17e39cfb8fc7c2a11a0..22adc4449bc6f9ec7c9519d71cb5e0ce983b4d54 100644 (file)
@@ -325,6 +325,39 @@ _weave_reflink_regular() {
        done
 }
 
+# Create a file of interleaved holes, unwritten blocks, and regular blocks.
+_weave_file_rainbow() {
+       blksz=$1
+       nr=$2
+       dfile=$3
+
+       $XFS_IO_PROG -f -c "truncate $((blksz * nr))" $dfile
+       _pwrite_byte 0x00 0 $((blksz * nr)) $dfile.chk
+       # 0 blocks are unwritten
+       seq 1 5 $((nr - 1)) | while read i; do
+               $XFS_IO_PROG -f -c "falloc $((blksz * i)) $blksz" $dfile
+               _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk
+       done
+       # 1 blocks are holes
+       seq 2 5 $((nr - 1)) | while read i; do
+               _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk
+       done
+       # 2 blocks are regular
+       seq 3 5 $((nr - 1)) | while read i; do
+               _pwrite_byte 0x71 $((blksz * i)) $blksz $dfile
+               _pwrite_byte 0x71 $((blksz * i)) $blksz $dfile.chk
+       done
+       # 3 blocks are holes
+       seq 2 5 $((nr - 1)) | while read i; do
+               _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk
+       done
+       # 4 blocks are delalloc
+       seq 4 5 $((nr - 1)) | while read i; do
+               _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile
+               _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile.chk
+       done
+}
+
 # Create a file of interleaved holes, unwritten blocks, regular blocks, and
 # reflinked blocks
 _weave_reflink_rainbow() {
index 5a957f4ec81c51e0e62bf9c23c2922769731ad0e..8945d0028c47a4d16c4b64407398c01a1bab3efc 100644 (file)
@@ -90,6 +90,7 @@ s/(superblock) (\d+)/\1 AGNO/;
 s/(AG \#)(\d+)/\1AGNO/;
 s/(reset bad sb for ag) (\d+)/\1 AGNO/;
 s/(unknown block state, ag )(\d+)(, blocks? )(\d+)/\1AGNO\3AGBNO/;
+s/^Superblock has (bad magic number) 0x.*/\1/;
 /^Note - quota info will be regenerated on next quota mount.$/ && next;
        print;'
 }
@@ -104,7 +105,7 @@ _filter_repair_lostblocks() {
 
 _filter_dd()
 {
-       fgrep -v records        # lose records in/out lines
+       grep -F -v records      # lose records in/out lines
 }
 
 # do some controlled corrupting & ensure repair recovers us
index 6cac71fc865b7e48bf125e741d21dbeea656bd21..0e91e481f9725a9d566d2d48e6ccedf6afe50b14 100644 (file)
@@ -4,26 +4,15 @@
 
 # List of xfstests's enviroment variables to include reports
 ## TODO automate list population inside common/conf
-REPORT_ENV_LIST="$REPORT_ENV_LIST SECTION"
-REPORT_ENV_LIST="$REPORT_ENV_LIST FSTYP"
-REPORT_ENV_LIST="$REPORT_ENV_LIST PLATFORM"
-REPORT_ENV_LIST="$REPORT_ENV_LIST MKFS_OPTIONS"
-REPORT_ENV_LIST="$REPORT_ENV_LIST MOUNT_OPTIONS"
-
-REPORT_ENV_LIST="$REPORT_ENV_LIST HOST_OPTIONS"
-REPORT_ENV_LIST="$REPORT_ENV_LIST CHECK_OPTIONS"
-REPORT_ENV_LIST="$REPORT_ENV_LIST XFS_MKFS_OPTIONS"
-REPORT_ENV_LIST="$REPORT_ENV_LIST TIME_FACTOR"
-REPORT_ENV_LIST="$REPORT_ENV_LIST LOAD_FACTOR"
-
-REPORT_ENV_LIST="$REPORT_ENV_LIST TEST_DIR"
-REPORT_ENV_LIST="$REPORT_ENV_LIST TEST_DEV"
-REPORT_ENV_LIST="$REPORT_ENV_LIST SCRATCH_DEV"
-REPORT_ENV_LIST="$REPORT_ENV_LIST SCRATCH_MNT"
-
-REPORT_ENV_LIST="$REPORT_ENV_LIST OVL_UPPER"
-REPORT_ENV_LIST="$REPORT_ENV_LIST OVL_LOWER"
-REPORT_ENV_LIST="$REPORT_ENV_LIST OVL_WORK"
+REPORT_ENV_LIST=("SECTION" "FSTYP" "PLATFORM" "MKFS_OPTIONS" "MOUNT_OPTIONS" \
+                "HOST_OPTIONS" "CHECK_OPTIONS" "XFS_MKFS_OPTIONS" \
+                "TIME_FACTOR" "LOAD_FACTOR" "TEST_DIR" "TEST_DEV" \
+                "SCRATCH_DEV" "SCRATCH_MNT" "OVL_UPPER" "OVL_LOWER" "OVL_WORK")
+
+# Variables that are captured in the report /if/ they are set.
+REPORT_ENV_LIST_OPT=("TAPE_DEV" "RMT_TAPE_DEV" "FSSTRESS_AVOID" "FSX_AVOID"
+                    "KCONFIG_PATH" "PERF_CONFIGNAME" "MIN_FSSIZE"
+                    "IDMAPPED_MOUNTS")
 
 encode_xml()
 {
@@ -35,64 +24,158 @@ encode_xml()
                -e 's/"/\&quot;/g'
 }
 
+encode_cdata()
+{
+       cat -v | sed -e 's/]]>/]]]]><![CDATA[>/g'
+}
+
+# Fill out REPORT_VARS with information about the block device referred to by
+# the passed-in bash variable.
+__generate_blockdev_report_vars() {
+       local bdev_var="$1"
+       local bdev="${!bdev_var}"
+
+       test -z "$bdev" && return
+       test -b "$bdev" || return
+       local sysfs_bdev="$(_sysfs_dev "$bdev")"
+
+       REPORT_VARS["${bdev_var}_SIZE_KB"]="$(( "$(blockdev --getsz "$bdev")" / 2 ))"
+       REPORT_VARS["${bdev_var}_ROTATIONAL"]="$(cat "$sysfs_bdev/queue/rotational" 2>/dev/null)"
+       REPORT_VARS["${bdev_var}_DAX"]="$(cat "$sysfs_bdev/queue/dax" 2>/dev/null)"
+       REPORT_VARS["${bdev_var}_DISCARD"]="$(sed -e 's/[1-9][0-9]*/1/g' "$sysfs_bdev/queue/discard_max_bytes" 2>/dev/null)"
+       REPORT_VARS["${bdev_var}_WRITE_ZEROES"]="$(sed -e 's/[1-9][0-9]*/1/g' "$sysfs_bdev/queue/write_zeroes_max_bytes" 2>/dev/null)"
+       REPORT_VARS["${bdev_var}_PHYS_BLOCK_BYTES"]="$(cat "$sysfs_bdev/queue/physical_block_size" 2>/dev/null)"
+       REPORT_VARS["${bdev_var}_LBA_BYTES"]="$(cat "$sysfs_bdev/queue/logical_block_size" 2>/dev/null)"
+       REPORT_VARS["${bdev_var}_ZONES"]="$(cat "$sysfs_bdev/queue/nr_zones" 2>/dev/null)"
+}
+
+__import_report_vars() {
+       local fname="$1"
+
+       while IFS=':' read key value; do
+               REPORT_VARS["${key%% }"]="${value## }"
+       done < "$1"
+}
+
+# Fill out REPORT_VARS with tidbits about our test runner configuration.
+# Caller is required to declare REPORT_VARS to be an associative array.
+__generate_report_vars() {
+       test "$REPORT_VARS_FILE" && __import_report_vars "$REPORT_VARS_FILE"
+
+       REPORT_VARS["ARCH"]="$(uname -m)"
+       REPORT_VARS["KERNEL"]="$(uname -r)"
+       REPORT_VARS["CPUS"]="$(getconf _NPROCESSORS_ONLN 2>/dev/null)"
+       REPORT_VARS["MEM_KB"]="$(grep MemTotal: /proc/meminfo | awk '{print $2}')"
+       REPORT_VARS["SWAP_KB"]="$(grep SwapTotal: /proc/meminfo | awk '{print $2}')"
+       test -n "$SOAK_DURATION" && REPORT_VARS["SOAK_DURATION"]="$SOAK_DURATION"
+
+       test -e /sys/devices/system/node/possible && \
+               REPORT_VARS["NUMA_NODES"]="$(cat /sys/devices/system/node/possible 2>/dev/null)"
+
+       __generate_blockdev_report_vars "TEST_DEV"
+       __generate_blockdev_report_vars "SCRATCH_DEV"
+
+       # Add per-filesystem variables to the report variable list
+       test "$FSTYP" = "xfs" && __generate_xfs_report_vars
+       [[ "$FSTYP" == ext[0-9]* ]] && __generate_ext4_report_vars
+
+       # Optional environmental variables
+       for varname in "${REPORT_ENV_LIST_OPT[@]}"; do
+               test -n "${!varname}" && REPORT_VARS["${varname}"]="${!varname}"
+       done
+}
+
 #
 # Xunit format report functions
 _xunit_add_property()
 {
        local name="$1"
-       local value="${!name}"
+       local value="$2"
 
-       if [ ! -z "$value" ]; then
-               echo -e "\t\t<property name=\"$name\" value=\"$value\"/>" >> $REPORT_DIR/result.xml
-       fi
+       test -z "$value" && return
+
+       local xname="$(echo "$name" | encode_xml)"
+       local xvalue="$(echo "$value" | encode_xml)"
+
+       echo -e "\t\t<property name=\"$xname\" value=\"$xvalue\"/>"
 }
+
 _xunit_make_section_report()
 {
        # xfstest:section ==> xunit:testsuite
-       local sect_name=$section
-       local sect_time=`expr $sect_stop - $sect_start`
-       local n_total=`expr $n_try + $n_notrun`
+       local sect_name="$1"
+       local tests_count="$2"
+       local bad_count="$3"
+       local notrun_count="$4"
+       local sect_time="$5"
+       local timestamp
+       local tmp_fn="$REPORT_DIR/result.xml.new"
+       local out_fn="$REPORT_DIR/result.xml"
 
        if [ $sect_name == '-no-sections-' ]; then
                sect_name='global'
        fi
        local report=$tmp.report.xunit.$sect_name.xml
+       rm -f "$tmp_fn"
        # Header
-       echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > $REPORT_DIR/result.xml
-       if [ -z "$date_time" ]; then
-               date_time=$(date +"%F %T")
+       echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > "$tmp_fn"
+       if [ -n "$test_start_time" ]; then
+               timestamp="$(date -Iseconds --date="$test_start_time")"
+       else
+               timestamp="$(date -Iseconds)"
        fi
-       local dtime=`echo $date_time| tr  " " 'T'`
-       local stats="failures=\"$n_bad\" skipped=\"$n_notrun\" tests=\"$n_total\" time=\"$sect_time\""
-       local hw_info="hostname=\"$HOST\" timestamp=\"$dtime\" "
-       echo "<testsuite name=\"xfstests\" $stats  $hw_info >" >> $REPORT_DIR/result.xml
+
+       local fstests_ns="https://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git"
+       cat >> "$tmp_fn" << ENDL
+<testsuite
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="$fstests_ns $fstests_ns/tree/doc/xunit.xsd"
+
+ name="xfstests"
+ failures="$bad_count" skipped="$notrun_count" tests="$tests_count" time="$sect_time"
+ hostname="$HOST"
+  start_timestamp="$(date -Iseconds --date="$fstests_start_time")"
+        timestamp="$timestamp"
+ report_timestamp="$(date -Iseconds)"
+>
+ENDL
+
+       declare -A REPORT_VARS
+       __generate_report_vars
 
        # Properties
-       echo -e "\t<properties>" >> $REPORT_DIR/result.xml
-       for p in $REPORT_ENV_LIST;do
-               _xunit_add_property "$p"
-       done
-       echo -e "\t</properties>" >> $REPORT_DIR/result.xml
+       echo -e "\t<properties>" >> "$tmp_fn"
+       (for key in "${!REPORT_VARS[@]}"; do
+               _xunit_add_property "$key" "${REPORT_VARS["$key"]}"
+       done;
+       for p in "${REPORT_ENV_LIST[@]}"; do
+               _xunit_add_property "$p" "${!p}"
+       done) | sort >> "$tmp_fn"
+       echo -e "\t</properties>" >> "$tmp_fn"
        if [ -f $report ]; then
-               cat $report >> $REPORT_DIR/result.xml
+               cat $report >> "$tmp_fn"
        fi
-       echo "</testsuite>" >> $REPORT_DIR/result.xml
-       echo "Xunit report: $REPORT_DIR/result.xml"
+       echo "</testsuite>" >> "$tmp_fn"
+       sync "$tmp_fn" && mv "$tmp_fn" "$out_fn"
+       echo "Xunit report: $out_fn"
 }
 
 _xunit_make_testcase_report()
 {
-       local test_seq="$1"
-       local test_status="$2"
-       local test_time=`expr $stop - $start`
-       local strip="$SRC_DIR/"
-       local test_name=${test_seq#$strip}
-       local sect_name=$section
+       local sect_name="$1"
+       local test_name="$2"
+       local test_status="$3"
+       local test_time="$4"
+       local report_format="$5"
+       local quiet
+
+       if [ "$report_format" = xunit-quiet ]; then
+               quiet=yes
+       fi
 
        # TODO: other places may also win if no-section mode will be named like 'default/global'
        if [ $sect_name == '-no-sections-' ]; then
                sect_name='global'
-
        fi
        local report=$tmp.report.xunit.$sect_name.xml
 
@@ -101,8 +184,9 @@ _xunit_make_testcase_report()
        "pass")
                ;;
        "notrun")
-               if [ -f $seqres.notrun ]; then
-                       local msg=`cat $seqres.notrun | encode_xml`
+               local notrun_file="${REPORT_DIR}/${test_name}.notrun"
+               if [ -f "$notrun_file" ]; then
+                       local msg=`cat "$notrun_file" | encode_xml`
                        echo -e "\t\t<skipped message=\"$msg\" />" >> $report
                else
                        echo -e "\t\t<skipped/>" >> $report
@@ -112,27 +196,32 @@ _xunit_make_testcase_report()
                echo -e "\t\t<skipped/>" >> $report
                ;;
        "fail")
+               local out_src="${SRC_DIR}/${test_name}.out"
+               local full_file="${REPORT_DIR}/${test_name}.full"
+               local dmesg_file="${REPORT_DIR}/${test_name}.dmesg"
+               local outbad_file="${REPORT_DIR}/${test_name}.out.bad"
                if [ -z "$_err_msg" ]; then
-                       _err_msg="Test $sequm failed, reason unknown"
+                       _err_msg="Test $test_name failed, reason unknown"
                fi
                echo -e "\t\t<failure message=\"$_err_msg\" type=\"TestFail\" />" >> $report
-               if [ -s $seqres.full ]; then
+               if [ -z "$quiet" -a -s "$full_file" ]; then
                        echo -e "\t\t<system-out>" >> $report
                        printf  '<![CDATA[\n' >>$report
-                       cat $seqres.full | tr -dc '[:print:][:space:]' | encode_xml >>$report
+                       cat "$full_file" | tr -dc '[:print:][:space:]' | encode_cdata >>$report
                        printf ']]>\n'  >>$report
                        echo -e "\t\t</system-out>" >> $report
                fi
-               if [ -f $seqres.dmesg ]; then
-                       echo -e "\t\t<system-err>" >> $report
+               if [ -z "$quiet" -a -f "$dmesg_file" ]; then
+                       echo -e "\t\t<kernel-log>" >> $report
                        printf  '<![CDATA[\n' >>$report
-                       cat $seqres.dmesg | tr -dc '[:print:][:space:]' | encode_xml >>$report
+                       cat "$dmesg_file" | tr -dc '[:print:][:space:]' | encode_cdata >>$report
                        printf ']]>\n'  >>$report
-                       echo -e "\t\t</system-err>" >> $report
-               elif [ -s $seqres.out.bad ]; then
+                       echo -e "\t\t</kernel-log>" >> $report
+               fi
+               if [ -z "$quiet" -a -s "$outbad_file" ]; then
                        echo -e "\t\t<system-err>" >> $report
                        printf  '<![CDATA[\n' >>$report
-                       $diff $test_seq.out $seqres.out.bad | encode_xml >>$report
+                       $diff "$out_src" "$outbad_file" | encode_cdata >>$report
                        printf ']]>\n'  >>$report
                        echo -e "\t\t</system-err>" >> $report
                fi
@@ -149,10 +238,17 @@ _xunit_make_testcase_report()
 #  Common report generator entry points
 _make_section_report()
 {
+       local sect_name="$1"
+       local tests_count="$2"
+       local bad_count="$3"
+       local notrun_count="$4"
+       local sect_time="$5"
        for report in $REPORT_LIST; do
                case "$report" in
-               "xunit")
-                       _xunit_make_section_report "$test_status"
+               "xunit"|"xunit-quiet")
+                       _xunit_make_section_report "$sect_name" "$tests_count" \
+                                                  "$bad_count" "$notrun_count" \
+                                                  "$sect_time"
                        ;;
                *)
                        _dump_err "format '$report' is not supported"
@@ -163,12 +259,15 @@ _make_section_report()
 
 _make_testcase_report()
 {
-       local test_seq="$1"
-       local test_status="$2"
+       local sect_name="$1"
+       local test_seq="$2"
+       local test_status="$3"
+       local test_time="$4"
        for report in $REPORT_LIST; do
                case "$report" in
-               "xunit")
-                       _xunit_make_testcase_report "$test_seq" "$test_status"
+               "xunit"|"xunit-quiet")
+                       _xunit_make_testcase_report "$sect_name" "$test_seq" \
+                                                   "$test_status" "$test_time" "$report"
                        ;;
                *)
                        _dump_err "report format '$report' is not supported"
@@ -180,7 +279,7 @@ _make_testcase_report()
 _assert_report_list() {
        for report in $REPORT_LIST; do
                case "$report" in
-               "xunit")
+               "xunit"|"xunit-quiet")
                        ;;
                *)
                        _fatal "report format '$report' is not supported"
diff --git a/common/tracing b/common/tracing
new file mode 100644 (file)
index 0000000..b3051c2
--- /dev/null
@@ -0,0 +1,66 @@
+##/bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# Routines for dealing with ftrace (or any other tracing).
+
+FTRACE_INSTANCES_DIR="/sys/kernel/debug/tracing/instances/"
+
+_require_ftrace() {
+       test -d "$FTRACE_INSTANCES_DIR" || \
+               _notrun "kernel does not support ftrace"
+}
+
+_ftrace_cleanup() {
+       if [ -d "$FTRACE_DIR" ]; then
+               _ftrace_ignore_events
+               # Removing an ftrace buffer requires rmdir, even though the
+               # virtual directory contains children.
+               rmdir "$FTRACE_DIR"
+       fi
+}
+
+_ftrace_setup() {
+       test -n "$FTRACE_DIR" && _fail "_ftrace_setup already run?"
+
+       # Give this fstest its own ftrace buffer so that we don't mess up
+       # any other tracers that might be running.
+       FTRACE_DIR="$FTRACE_INSTANCES_DIR/fstests.$seq"
+
+       test -d "$FTRACE_DIR" && rmdir "$FTRACE_DIR"
+       mkdir "$FTRACE_DIR"
+}
+
+# Intercept the given events.  Arguments may be regular expressions.
+_ftrace_record_events() {
+       test -d "$FTRACE_DIR" || _fail "_ftrace_setup not run?"
+
+       for arg in "$@"; do
+               # Replace slashes with semicolons per ftrace convention
+               find "$FTRACE_DIR/events/" -type d -name "$arg" -printf '%P\n' | \
+                       tr '/' ':' >> "$FTRACE_DIR/set_event"
+       done
+}
+
+# Stop intercepting the given events.  If no arguments, stops all events.
+_ftrace_ignore_events() {
+       test -d "$FTRACE_DIR" || _fail "_ftrace_setup not run?"
+
+       if [ "$#" -eq 0 ]; then
+               echo > "$FTRACE_DIR/set_event"
+       else
+               for arg in "$@"; do
+                       # Replace slashes with semicolons per ftrace convention
+                       find "$FTRACE_DIR/events/" -type d -name "$arg" -printf '!%P\n' | \
+                               tr '/' ':' >> "$FTRACE_DIR/set_event"
+               done
+       fi
+}
+
+# Dump whatever was written to the ftrace buffer since the last time this
+# helper was called.
+_ftrace_dump() {
+       test -d "$FTRACE_DIR" || _fail "_ftrace_setup not run?"
+
+       cat "$FTRACE_DIR/trace"
+}
diff --git a/common/ubifs b/common/ubifs
new file mode 100644 (file)
index 0000000..0832326
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-only
+
+_get_leb_size()
+{
+       local ubivol=$1
+
+       cat /sys/class/ubi/`basename $ubivol`/usable_eb_size
+}
index 38eea157cbc53418e97aeb16a8e0314378d732ed..03d175ce1b7a18d406fa90bb0790c7afafe1cfa1 100644 (file)
@@ -3,6 +3,21 @@
 #
 # Functions for setting up and testing fs-verity
 
+# btrfs will return IO errors on corrupted data with or without fs-verity.
+# to really test fs-verity, use nodatasum.
+if [ "$FSTYP" == "btrfs" ]; then
+        if [ -z "$MOUNT_OPTIONS" ]; then
+                export MOUNT_OPTIONS="-o nodatasum"
+        else
+                export MOUNT_OPTIONS+=" -o nodatasum"
+        fi
+fi
+
+# Require fs-verity support on the scratch filesystem.
+#
+# FSV_BLOCK_SIZE will be set to a Merkle tree block size that is supported by
+# the filesystem.  Other sizes may be supported too, but FSV_BLOCK_SIZE is the
+# only size that is guaranteed to work without any additional checks.
 _require_scratch_verity()
 {
        _require_scratch
@@ -17,35 +32,50 @@ _require_scratch_verity()
 
        # Try to mount the filesystem.  If this fails then either the kernel
        # isn't aware of fs-verity, or the mkfs options were not compatible with
-       # verity (e.g. ext4 with block size != PAGE_SIZE).
+       # verity (e.g. ext4 with block size != PAGE_SIZE on old kernels).
        if ! _try_scratch_mount &>>$seqres.full; then
                _notrun "kernel is unaware of $FSTYP verity feature," \
                        "or mkfs options are not compatible with verity"
        fi
 
+       local fstyp=${1:-$FSTYP}
+       local scratch_mnt=${2:-$SCRATCH_MNT}
+
        # The filesystem may be aware of fs-verity but have it disabled by
        # CONFIG_FS_VERITY=n.  Detect support via sysfs.
-       if [ ! -e /sys/fs/$FSTYP/features/verity ]; then
-               _notrun "kernel $FSTYP isn't configured with verity support"
+       if [ ! -e /sys/fs/$fstyp/features/verity ]; then
+               _notrun "kernel $fstyp isn't configured with verity support"
+       fi
+
+       # Select a default Merkle tree block size for when tests don't
+       # explicitly specify one.
+       #
+       # For consistency reasons, all 'fsverity' subcommands, including
+       # 'fsverity enable', default to 4K Merkle tree blocks.  That's generally
+       # not ideal for tests, since it's possible that the filesystem doesn't
+       # support 4K blocks but does support another size.  Specifically, the
+       # kernel originally supported only merkle_tree_block_size ==
+       # fs_block_size == page_size, and later it was updated to support
+       # merkle_tree_block_size <= min(fs_block_size, page_size).
+       #
+       # Therefore, we default to merkle_tree_block_size == min(fs_block_size,
+       # page_size).  That maximizes the chance of verity actually working.
+       local fs_block_size=$(_get_block_size $scratch_mnt)
+       local page_size=$(_get_page_size)
+       if (( fs_block_size <= page_size )); then
+               FSV_BLOCK_SIZE=$fs_block_size
+       else
+               FSV_BLOCK_SIZE=$page_size
        fi
 
        # The filesystem may have fs-verity enabled but not actually usable by
        # default.  E.g., ext4 only supports verity on extent-based files, so it
        # doesn't work on ext3-style filesystems.  So, try actually using it.
-       echo foo > $SCRATCH_MNT/tmpfile
-       _disable_fsverity_signatures
-       if ! _fsv_enable $SCRATCH_MNT/tmpfile; then
-               _restore_fsverity_signatures
-               _notrun "$FSTYP verity isn't usable by default with these mkfs options"
+       if ! _fsv_can_enable $scratch_mnt/tmpfile; then
+               _notrun "$fstyp verity isn't usable by default with these mkfs options"
        fi
-       _restore_fsverity_signatures
-       rm -f $SCRATCH_MNT/tmpfile
 
        _scratch_unmount
-
-       # Merkle tree block size.  Currently all filesystems only support
-       # PAGE_SIZE for this.  This is also the default for 'fsverity enable'.
-       FSV_BLOCK_SIZE=$(get_page_size)
 }
 
 # Check for CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y, as well as the userspace
@@ -95,10 +125,7 @@ _fsv_load_cert()
 _disable_fsverity_signatures()
 {
        if [ -e /proc/sys/fs/verity/require_signatures ]; then
-               if [ -z "$FSVERITY_SIG_CTL_ORIG" ]; then
-                       FSVERITY_SIG_CTL_ORIG=$(</proc/sys/fs/verity/require_signatures)
-               fi
-               echo 0 > /proc/sys/fs/verity/require_signatures
+               _set_fsverity_require_signatures 0
        fi
 }
 
@@ -106,18 +133,36 @@ _disable_fsverity_signatures()
 # This assumes that _require_fsverity_builtin_signatures() was called.
 _enable_fsverity_signatures()
 {
-       if [ -z "$FSVERITY_SIG_CTL_ORIG" ]; then
-               FSVERITY_SIG_CTL_ORIG=$(</proc/sys/fs/verity/require_signatures)
-       fi
-       echo 1 > /proc/sys/fs/verity/require_signatures
+       _set_fsverity_require_signatures 1
 }
 
-# Restore the original signature verification setting.
+# Restore the original value of fs.verity.require_signatures, i.e. the value it
+# had at the beginning of the test.
 _restore_fsverity_signatures()
 {
-        if [ -n "$FSVERITY_SIG_CTL_ORIG" ]; then
-                echo "$FSVERITY_SIG_CTL_ORIG" > /proc/sys/fs/verity/require_signatures
-        fi
+       if [ -n "$FSVERITY_SIG_CTL_ORIG" ]; then
+               _set_fsverity_require_signatures "$FSVERITY_SIG_CTL_ORIG"
+       fi
+}
+
+# Restore the previous value of fs.verity.require_signatures, i.e. the value it
+# had just before it was last written to.
+_restore_prev_fsverity_signatures()
+{
+       if [ -n "$FSVERITY_SIG_CTL_PREV" ]; then
+               _set_fsverity_require_signatures "$FSVERITY_SIG_CTL_PREV"
+       fi
+}
+
+_set_fsverity_require_signatures()
+{
+       local newval=$1
+       local oldval=$(</proc/sys/fs/verity/require_signatures)
+       FSVERITY_SIG_CTL_PREV=$oldval
+       if [ -z "$FSVERITY_SIG_CTL_ORIG" ]; then
+               FSVERITY_SIG_CTL_ORIG=$oldval
+       fi
+       echo "$newval" > /proc/sys/fs/verity/require_signatures
 }
 
 # Require userspace and kernel support for 'fsverity dump_metadata'.
@@ -141,12 +186,27 @@ _require_fsverity_dump_metadata()
        _fail "Unexpected output from 'fsverity dump_metadata': $(<"$tmpfile")"
 }
 
+# Check for userspace tools needed to corrupt verity data or metadata.
+_require_fsverity_corruption()
+{
+       _require_xfs_io_command "fiemap"
+       if [ $FSTYP == "btrfs" ]; then
+               _require_btrfs_corrupt_block
+       fi
+}
+
 _scratch_mkfs_verity()
 {
        case $FSTYP in
        ext4|f2fs)
                _scratch_mkfs -O verity
                ;;
+       btrfs)
+               _scratch_mkfs
+               ;;
+       overlay)
+               _scratch_mkfs # This relies on the scratch fs supporting verity
+                ;;
        *)
                _notrun "No verity support for $FSTYP"
                ;;
@@ -195,7 +255,13 @@ _fsv_dump_signature()
 
 _fsv_enable()
 {
-       $FSVERITY_PROG enable "$@"
+       local args=("$@")
+       # If the caller didn't explicitly specify a Merkle tree block size, then
+       # use FSV_BLOCK_SIZE.
+       if ! [[ " $*" =~ " --block-size" ]]; then
+               args+=("--block-size=$FSV_BLOCK_SIZE")
+       fi
+       $FSVERITY_PROG enable "${args[@]}"
 }
 
 _fsv_measure()
@@ -203,9 +269,26 @@ _fsv_measure()
         $FSVERITY_PROG measure "$@" | awk '{print $1}'
 }
 
+_fsv_digest()
+{
+       local args=("$@")
+       # If the caller didn't explicitly specify a Merkle tree block size, then
+       # use FSV_BLOCK_SIZE.
+       if ! [[ " $*" =~ " --block-size" ]]; then
+               args+=("--block-size=$FSV_BLOCK_SIZE")
+       fi
+       $FSVERITY_PROG digest "${args[@]}" | awk '{print $1}'
+}
+
 _fsv_sign()
 {
-       $FSVERITY_PROG sign "$@"
+       local args=("$@")
+       # If the caller didn't explicitly specify a Merkle tree block size, then
+       # use FSV_BLOCK_SIZE.
+       if ! [[ " $*" =~ " --block-size" ]]; then
+               args+=("--block-size=$FSV_BLOCK_SIZE")
+       fi
+       $FSVERITY_PROG sign "${args[@]}"
 }
 
 # Generate a file, then enable verity on it.
@@ -218,19 +301,20 @@ _fsv_create_enable_file()
        _fsv_enable "$file" "$@"
 }
 
-_fsv_have_hash_algorithm()
+_fsv_can_enable()
 {
-       local hash_alg=$1
-       local test_file=$2
+       local test_file=$1
+       shift
+       local params=("$@")
 
+       _disable_fsverity_signatures
        rm -f $test_file
        head -c 4096 /dev/zero > $test_file
-       if ! _fsv_enable --hash-alg=$hash_alg $test_file &>> $seqres.full; then
-               # no kernel support
-               return 1
-       fi
+       _fsv_enable $test_file "${params[@]}" &>> $seqres.full
+       local status=$?
+       _restore_prev_fsverity_signatures
        rm -f $test_file
-       return 0
+       return $status
 }
 
 #
@@ -308,8 +392,42 @@ _fsv_scratch_corrupt_merkle_tree()
                (( offset += ($(_get_filesize $file) + 65535) & ~65535 ))
                _fsv_scratch_corrupt_bytes $file $offset
                ;;
+       btrfs)
+               local ino=$(stat -c '%i' $file)
+               _scratch_unmount
+               local byte=""
+               while read -n 1 byte; do
+                       local ascii=$(printf "%d" "'$byte'")
+                       # This command will find a Merkle tree item for the inode (-I $ino,37,0)
+                       # in the default filesystem tree (-r 5) and corrupt one byte (-b 1) at
+                       # $offset (-o $offset) with the ascii representation of the byte we read
+                       # (-v $ascii)
+                       $BTRFS_CORRUPT_BLOCK_PROG -r 5 -I $ino,37,0 -v $ascii -o $offset -b 1 $SCRATCH_DEV
+                       (( offset += 1 ))
+               done
+               _scratch_mount
+               ;;
        *)
                _fail "_fsv_scratch_corrupt_merkle_tree() unimplemented on $FSTYP"
                ;;
        esac
 }
+
+_require_fsverity_max_file_size_limit()
+{
+       case $FSTYP in
+       btrfs|ext4|f2fs)
+               ;;
+       *)
+               _notrun "$FSTYP does not store verity data past EOF; no special file size limit"
+               ;;
+       esac
+}
+
+# Replace fs-verity digests, as formatted by the 'fsverity' tool, with <digest>.
+# This function can be used by tests where fs-verity digests depend on the
+# default Merkle tree block size (FSV_BLOCK_SIZE).
+_filter_fsverity_digest()
+{
+       sed -E 's/\b(sha(256|512)):[a-f0-9]{64,}\b/\1:<digest>/'
+}
index 2123a4ab428195a34caba1a2d45574b5c5276bf0..65b509691b66edae98b8d24f4274cd04dc00edd0 100644 (file)
@@ -2,6 +2,17 @@
 # XFS specific common functions.
 #
 
+__generate_xfs_report_vars() {
+       __generate_blockdev_report_vars TEST_RTDEV
+       __generate_blockdev_report_vars TEST_LOGDEV
+       __generate_blockdev_report_vars SCRATCH_RTDEV
+       __generate_blockdev_report_vars SCRATCH_LOGDEV
+
+       REPORT_VARS["XFS_ALWAYS_COW"]="$(cat /sys/fs/xfs/debug/always_cow 2>/dev/null)"
+       REPORT_VARS["XFS_LARP"]="$(cat /sys/fs/xfs/debug/larp 2>/dev/null)"
+       REPORT_ENV_LIST_OPT+=("TEST_XFS_REPAIR_REBUILD" "TEST_XFS_SCRUB_REBUILD")
+}
+
 _setup_large_xfs_fs()
 {
        fs_size=$1
@@ -103,7 +114,7 @@ _scratch_find_xfs_min_logblocks()
        # try again without MKFS_OPTIONS because that's what _scratch_do_mkfs
        # will do if we pass in the log size option.
        if [ $mkfs_status -ne 0 ] &&
-          ! egrep -q '(log size.*too small, minimum|external log device.*too small, must be)' $tmp.mkfserr; then
+          ! grep -E -q '(log size.*too small, minimum|external log device.*too small, must be)' $tmp.mkfserr; then
                eval "$mkfs_cmd $extra_mkfs_options $SCRATCH_DEV" \
                        2>$tmp.mkfserr 1>$tmp.mkfsstd
                mkfs_status=$?
@@ -174,6 +185,22 @@ _scratch_mkfs_xfs()
        return $mkfs_status
 }
 
+# Get the number of realtime extents of a mounted filesystem.
+_xfs_get_rtextents()
+{
+       local path="$1"
+
+       $XFS_INFO_PROG "$path" | sed -n "s/^.*rtextents=\([[:digit:]]*\).*/\1/p"
+}
+
+# Get the realtime extent size of a mounted filesystem.
+_xfs_get_rtextsize()
+{
+       local path="$1"
+
+       $XFS_INFO_PROG "$path" | sed -n "s/^.*realtime.*extsz=\([[:digit:]]*\).*/\1/p"
+}
+
 # Get the size of an allocation unit of a file.  Normally this is just the
 # block size of the file, but for realtime files, this is the realtime extent
 # size.
@@ -181,7 +208,7 @@ _xfs_get_file_block_size()
 {
        local path="$1"
 
-       if ! ($XFS_IO_PROG -c "stat -v" "$path" 2>&1 | egrep -q '(rt-inherit|realtime)'); then
+       if ! ($XFS_IO_PROG -c "stat -v" "$path" 2>&1 | grep -E -q '(rt-inherit|realtime)'); then
                _get_block_size "$path"
                return
        fi
@@ -191,7 +218,24 @@ _xfs_get_file_block_size()
        while ! $XFS_INFO_PROG "$path" &>/dev/null && [ "$path" != "/" ]; do
                path="$(dirname "$path")"
        done
-       $XFS_INFO_PROG "$path" | grep realtime | sed -e 's/^.*extsz=\([0-9]*\).*$/\1/g'
+       _xfs_get_rtextsize "$path"
+}
+
+# Get the directory block size of a mounted filesystem.
+_xfs_get_dir_blocksize()
+{
+       local fs="$1"
+
+       $XFS_INFO_PROG "$fs" | sed -n "s/^naming.*bsize=\([[:digit:]]*\).*/\1/p"
+}
+
+# Decide if this path is a file on the realtime device
+_xfs_is_realtime_file()
+{
+       if [ "$USE_EXTERNAL" != "yes" ] || [ -z "$SCRATCH_RTDEV" ]; then
+               return 1
+       fi
+       $XFS_IO_PROG -c 'stat -v' "$1" | grep -q -w realtime
 }
 
 # Set or clear the realtime status of every supplied path.  The first argument
@@ -201,6 +245,9 @@ _xfs_get_file_block_size()
 # For each directory, each file subsequently created will target the given
 # device for file data allocations.  For each empty regular file, each
 # subsequent file data allocation will be on the given device.
+#
+# NOTE: If you call this on $TEST_DIR, you must reset the rtinherit flag state
+# before the end of the test to avoid polluting subsequent tests.
 _xfs_force_bdev()
 {
        local device="$1"
@@ -265,6 +312,29 @@ _xfs_check()
        return $status
 }
 
+_scratch_xfs_options()
+{
+    local type=$1
+    local rt_opt=""
+    local log_opt=""
+
+    case $type in
+    mkfs)
+       SCRATCH_OPTIONS="$SCRATCH_OPTIONS -f"
+       rt_opt="-r"
+        log_opt="-l"
+       ;;
+    mount)
+       rt_opt="-o"
+        log_opt="-o"
+       ;;
+    esac
+    [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
+       SCRATCH_OPTIONS="$SCRATCH_OPTIONS ${rt_opt}rtdev=$SCRATCH_RTDEV"
+    [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
+       SCRATCH_OPTIONS="$SCRATCH_OPTIONS ${log_opt}logdev=$SCRATCH_LOGDEV"
+}
+
 _scratch_xfs_db_options()
 {
        SCRATCH_OPTIONS=""
@@ -387,6 +457,65 @@ _require_xfs_crc()
        _scratch_unmount
 }
 
+# If the xfs_info output for the given XFS filesystem mount mentions the given
+# feature.  If so, return 0 for success.  If not, return 1 for failure.  If the
+# third option is -v, echo 1 for success and 0 for not.
+#
+# Starting with xfsprogs 4.17, this also works for unmounted filesystems.
+# The feature 'realtime' looks for rtextents > 0.
+_xfs_has_feature()
+{
+       local fs="$1"
+       local feat="$2"
+       local verbose="$3"
+       local feat_regex="1"
+
+       case "$feat" in
+       "realtime")
+               feat="rtextents"
+               feat_regex="[1-9][0-9]*"
+               ;;
+       esac
+
+       local answer="$($XFS_INFO_PROG "$fs" 2>&1 | grep -E -w -c "$feat=$feat_regex")"
+       if [ "$answer" -ne 0 ]; then
+               test "$verbose" = "-v" && echo 1
+               return 0
+       fi
+
+       test "$verbose" = "-v" && echo 0
+       return 1
+}
+
+# Require that the xfs_info output for the given XFS filesystem mount mentions
+# the given feature flag.  If the third argument is -u (or is empty and the
+# second argument is $SCRATCH_MNT), unmount the fs on failure.  If a fourth
+# argument is supplied, it will be used as the _notrun message.
+_require_xfs_has_feature()
+{
+       local fs="$1"
+       local feat="$2"
+       local umount="$3"
+       local message="$4"
+
+       if [ -z "$umount" ] && [ "$fs" = "$SCRATCH_MNT" ]; then
+               umount="-u"
+       fi
+
+       _xfs_has_feature "$1" "$2" && return 0
+
+       test "$umount" = "-u" && umount "$fs" &>/dev/null
+
+       test -n "$message" && _notrun "$message"
+
+       case "$fs" in
+       "$TEST_DIR"|"$TEST_DEV")        fsname="test";;
+       "$SCRATCH_MNT"|"$SCRATCH_DEV")  fsname="scratch";;
+       *)                              fsname="$fs";;
+       esac
+       _notrun "$2 not supported by $fsname filesystem type: $FSTYP"
+}
+
 # this test requires the xfs kernel support crc feature on scratch device
 #
 _require_scratch_xfs_crc()
@@ -394,7 +523,8 @@ _require_scratch_xfs_crc()
        _scratch_mkfs_xfs >/dev/null 2>&1
        _try_scratch_mount >/dev/null 2>&1 \
           || _notrun "Kernel doesn't support crc feature"
-       $XFS_INFO_PROG $SCRATCH_MNT | grep -q 'crc=1' || _notrun "crc feature not supported by this filesystem"
+       _require_xfs_has_feature $SCRATCH_MNT crc -u \
+               "crc feature not supported by this filesystem"
        _scratch_unmount
 }
 
@@ -444,6 +574,18 @@ _require_xfs_sparse_inodes()
        _scratch_unmount
 }
 
+# this test requires the xfs large extent counter feature
+#
+_require_xfs_nrext64()
+{
+       _scratch_mkfs_xfs_supported -m crc=1 -i nrext64 > /dev/null 2>&1 \
+               || _notrun "mkfs.xfs does not support nrext64"
+       _scratch_mkfs_xfs -m crc=1 -i nrext64 > /dev/null 2>&1
+       _try_scratch_mount >/dev/null 2>&1 \
+               || _notrun "kernel does not support nrext64"
+       _scratch_unmount
+}
+
 # check that xfs_db supports a specific command
 _require_xfs_db_command()
 {
@@ -458,6 +600,37 @@ _require_xfs_db_command()
                _notrun "xfs_db $command support is missing"
 }
 
+# Check the health of a mounted XFS filesystem.  Callers probably want to
+# ensure that xfs_scrub has been run first.  Returns 1 if unhealthy metadata
+# are found or 0 otherwise.
+_check_xfs_health() {
+       local mntpt="$1"
+       local ret=0
+       local t="$tmp.health_helper"
+
+       test -x "$XFS_SPACEMAN_PROG" || return 0
+
+       $XFS_SPACEMAN_PROG -c 'health -c -q' $mntpt > $t.out 2> $t.err
+       test $? -ne 0 && ret=1
+
+       # Don't return error if userspace or kernel don't support health
+       # reporting.
+       grep -q 'command.*health.*not found' $t.err && return 0
+       grep -q 'Inappropriate ioctl for device' $t.err && return 0
+
+       # Filter out the "please run scrub" message if nothing's been checked.
+       sed -e '/Health status has not been/d' -e '/Please run xfs_scrub/d' -i \
+                       $t.err
+
+       grep -q unhealthy $t.out && ret=1
+       test $(wc -l < $t.err) -gt 0 && ret=1
+       cat $t.out
+       cat $t.err 1>&2
+       rm -f $t.out $t.err
+
+       return $ret
+}
+
 # Does the filesystem mounted from a particular device support scrub?
 _supports_xfs_scrub()
 {
@@ -476,6 +649,9 @@ _supports_xfs_scrub()
        test "$FSTYP" = "xfs" || return 1
        test -x "$XFS_SCRUB_PROG" || return 1
 
+       mountpoint $mountpoint >/dev/null || \
+               _fail "$mountpoint is not mounted"
+
        # Probe for kernel support...
        $XFS_IO_PROG -c 'help scrub' 2>&1 | grep -q 'types are:.*probe' || return 1
        $XFS_IO_PROG -c "scrub probe" "$mountpoint" 2>&1 | grep -q "Inappropriate ioctl" && return 1
@@ -486,6 +662,13 @@ _supports_xfs_scrub()
        return 0
 }
 
+# Does the scratch file system support scrub?
+_require_scratch_xfs_scrub()
+{
+       _supports_xfs_scrub $SCRATCH_MNT $SCRATCH_DEV || \
+               _notrun "Scrub not supported"
+}
+
 # Save a snapshot of a corrupt xfs filesystem for later debugging.
 _xfs_metadump() {
        local metadump="$1"
@@ -494,7 +677,6 @@ _xfs_metadump() {
        local compressopt="$4"
        shift; shift; shift; shift
        local options="$@"
-       test -z "$options" && options="-a -o"
 
        if [ "$logdev" != "none" ]; then
                options="$options -l $logdev"
@@ -507,6 +689,63 @@ _xfs_metadump() {
        return $res
 }
 
+# What is the version of this metadump file?
+_xfs_metadumpfile_version() {
+       local file="$1"
+       local magic
+
+       magic="$($XFS_IO_PROG -c 'pread -q -v 0 4' "$file")"
+       case "$magic" in
+       "00000000:  58 4d 44 32  XMD2") echo 2;;
+       "00000000:  58 46 53 4d  XFSM") echo 1;;
+       esac
+}
+
+_xfs_mdrestore() {
+       local metadump="$1"
+       local device="$2"
+       local logdev="$3"
+       shift; shift; shift
+       local options="$@"
+       local dumpfile_ver
+
+       # If we're configured for compressed dumps and there isn't already an
+       # uncompressed dump, see if we can use DUMP_COMPRESSOR to decompress
+       # something.
+       if [ ! -e "$metadump" ] && [ -n "$DUMP_COMPRESSOR" ]; then
+               for compr in "$metadump".*; do
+                       [ -e "$compr" ] && $DUMP_COMPRESSOR -d -f -k "$compr" && break
+               done
+       fi
+       test -r "$metadump" || return 1
+       dumpfile_ver="$(_xfs_metadumpfile_version "$metadump")"
+
+       if [ "$logdev" != "none" ] && [[ $dumpfile_ver > 1 ]]; then
+               # metadump and mdrestore began capturing and restoring the
+               # contents of external log devices with the addition of the
+               # metadump v2 format.  Hence it only makes sense to specify -l
+               # here if the dump file itself is in v2 format.
+               #
+               # With a v1 metadump, the log device is not changed by the dump
+               # and restore process.  Historically, fstests either didn't
+               # notice or _notrun themselves when external logs were in use.
+               # Don't break that for people testing with xfsprogs < 6.5.
+               options="$options -l $logdev"
+       fi
+
+       $XFS_MDRESTORE_PROG $options "${metadump}" "${device}"
+}
+
+# What is the maximum metadump file format supported by xfs_metadump?
+_xfs_metadump_max_version()
+{
+       if $XFS_METADUMP_PROG --help 2>&1 | grep -q -- '-v version'; then
+               echo 2
+       else
+               echo 1
+       fi
+}
+
 # Snapshot the metadata on the scratch device
 _scratch_xfs_metadump()
 {
@@ -520,9 +759,40 @@ _scratch_xfs_metadump()
        _xfs_metadump "$metadump" "$SCRATCH_DEV" "$logdev" nocompress "$@"
 }
 
+# Restore snapshotted metadata on the scratch device
+_scratch_xfs_mdrestore()
+{
+       local metadump=$1
+       shift
+       local logdev=none
+       local options="$@"
+
+       # $SCRATCH_LOGDEV should have a non-zero length value only when all of
+       # the following conditions are met.
+       # 1. Metadump is in v2 format.
+       # 2. Metadump has contents dumped from an external log device.
+       if [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ]; then
+               logdev=$SCRATCH_LOGDEV
+       fi
+
+       _xfs_mdrestore "$metadump" "$SCRATCH_DEV" "$logdev" "$@"
+}
+
+# Do not use xfs_repair (offline fsck) to rebuild the filesystem
+_xfs_skip_offline_rebuild() {
+       touch "$RESULT_DIR/.skip_rebuild"
+}
+
+# Do not use xfs_scrub (online fsck) to rebuild the filesystem
+_xfs_skip_online_rebuild() {
+       touch "$RESULT_DIR/.skip_orebuild"
+}
+
 # run xfs_check and friends on a FS.
 _check_xfs_filesystem()
 {
+       local can_scrub=
+
        if [ $# -ne 3 ]; then
                echo "Usage: _check_xfs_filesystem device <logdev>|none <rtdev>|none" 1>&2
                exit 1
@@ -557,6 +827,8 @@ _check_xfs_filesystem()
        # Run online scrub if we can.
        mntpt="$(_is_dev_mounted $device)"
        if [ -n "$mntpt" ] && _supports_xfs_scrub "$mntpt" "$device"; then
+               can_scrub=1
+
                # Tests can create a scenario in which a call to syncfs() issued
                # at the end of the execution of the test script would return an
                # error code. xfs_scrub internally calls syncfs() before
@@ -577,6 +849,18 @@ _check_xfs_filesystem()
                        ok=0
                fi
                rm -f $tmp.scrub
+
+               # Does the health reporting notice anything?
+               _check_xfs_health $mntpt > $tmp.health 2>&1
+               res=$?
+               if [ $((res ^ ok)) -eq 0 ]; then
+                       _log_err "_check_xfs_filesystem: filesystem on $device failed health check"
+                       echo "*** xfs_spaceman -c 'health -c -q' output ***" >> $seqres.full
+                       cat $tmp.health >> $seqres.full
+                       echo "*** end xfs_spaceman output" >> $seqres.full
+                       ok=0
+               fi
+               rm -f $tmp.health
        fi
 
        if [ "$type" = "xfs" ]; then
@@ -628,11 +912,11 @@ _check_xfs_filesystem()
        if [ "$ok" -ne 1 ] && [ "$DUMP_CORRUPT_FS" = "1" ]; then
                local flatdev="$(basename "$device")"
                _xfs_metadump "$seqres.$flatdev.check.md" "$device" "$logdev" \
-                       compress >> $seqres.full
+                       compress -a -o >> $seqres.full
        fi
 
        # Optionally test the index rebuilding behavior.
-       if [ -n "$TEST_XFS_REPAIR_REBUILD" ]; then
+       if [ -n "$TEST_XFS_REPAIR_REBUILD" ] && [ ! -e "$RESULT_DIR/.skip_rebuild" ]; then
                rebuild_ok=1
                $XFS_REPAIR_PROG $extra_options $extra_log_options $extra_rt_options $device >$tmp.repair 2>&1
                if [ $? -ne 0 ]; then
@@ -661,7 +945,7 @@ _check_xfs_filesystem()
                if [ "$rebuild_ok" -ne 1 ] && [ "$DUMP_CORRUPT_FS" = "1" ]; then
                        local flatdev="$(basename "$device")"
                        _xfs_metadump "$seqres.$flatdev.rebuild.md" "$device" \
-                               "$logdev" compress >> $seqres.full
+                               "$logdev" compress -a -o >> $seqres.full
                fi
        fi
 
@@ -673,6 +957,82 @@ _check_xfs_filesystem()
                _mount_or_remount_rw "$extra_mount_options" $device $mountpoint
        fi
 
+       # If desired, test the online metadata rebuilding behavior if the
+       # filesystem was mounted when this function was called.
+       if [ -n "$TEST_XFS_SCRUB_REBUILD" ] && [ -n "$can_scrub" ] && [ ! -e "$RESULT_DIR/.skip_orebuild" ]; then
+               orebuild_ok=1
+
+               # Walk the entire directory tree to load directory blocks into
+               # memory and populate the dentry cache, which can speed up the
+               # repairs considerably when the directory tree is very large.
+               find $mntpt &>/dev/null &
+
+               XFS_SCRUB_FORCE_REPAIR=1 "$XFS_SCRUB_PROG" -v -d $mntpt 2>&1 | gzip > $tmp.scrub.gz
+               ret=$?
+               if [ $ret -ne 0 ]; then
+                       if zgrep -q 'No space left on device' $tmp.scrub.gz; then
+                               # It's not an error if the fs does not have
+                               # enough space to complete a repair.  We will
+                               # check everything, though.
+                               echo "*** XFS_SCRUB_FORCE_REPAIR=1 xfs_scrub -v -d ran out of space ret=$ret ***" >> $seqres.full
+                               echo "See $seqres.scrubout.gz for details." >> $seqres.full
+                               mv $tmp.scrub.gz $seqres.scrubout.gz
+                               echo "*** end xfs_scrub output" >> $seqres.full
+                       else
+                               _log_err "_check_xfs_filesystem: filesystem on $device failed scrub orebuild"
+                               echo "*** XFS_SCRUB_FORCE_REPAIR=1 xfs_scrub -v -d output ret=$ret ***" >> $seqres.full
+                               echo "See $seqres.scrubout.gz for details." >> $seqres.full
+                               mv $tmp.scrub.gz $seqres.scrubout.gz
+                               echo "*** end xfs_scrub output" >> $seqres.full
+                               ok=0
+                               orebuild_ok=0
+                       fi
+               fi
+               rm -f $tmp.scrub.gz
+
+               # Clear force_repair because xfs_scrub could have set it
+               $XFS_IO_PROG -x -c 'inject noerror' "$mntpt" >> $seqres.full
+
+               "$XFS_SCRUB_PROG" -v -d -n $mntpt > $tmp.scrub 2>&1
+               if [ $? -ne 0 ]; then
+                       _log_err "_check_xfs_filesystem: filesystem on $device failed scrub orebuild recheck"
+                       echo "*** xfs_scrub -v -d -n output ***" >> $seqres.full
+                       cat $tmp.scrub >> $seqres.full
+                       echo "*** end xfs_scrub output" >> $seqres.full
+                       ok=0
+                       orebuild_ok=0
+               fi
+               rm -f $tmp.scrub
+
+               mountpoint=`_umount_or_remount_ro $device`
+
+               $XFS_REPAIR_PROG -n $extra_options $extra_log_options $extra_rt_options $device >$tmp.repair 2>&1
+               if [ $? -ne 0 ]; then
+                       _log_err "_check_xfs_filesystem: filesystem on $device is inconsistent (orebuild-reverify)"
+                       echo "*** xfs_repair -n output ***"     >>$seqres.full
+                       cat $tmp.repair                         >>$seqres.full
+                       echo "*** end xfs_repair output"        >>$seqres.full
+
+                       ok=0
+                       orebuild_ok=0
+               fi
+               rm -f $tmp.repair
+
+               if [ $ok -eq 0 ]; then
+                       echo "*** mount output ***"             >>$seqres.full
+                       _mount                                  >>$seqres.full
+                       echo "*** end mount output"             >>$seqres.full
+               elif [ "$type" = "xfs" ]; then
+                       _mount_or_remount_rw "$extra_mount_options" $device $mountpoint
+               fi
+
+               if [ "$orebuild_ok" -ne 1 ] && [ "$DUMP_CORRUPT_FS" = "1" ]; then
+                       local flatdev="$(basename "$device")"
+                       _xfs_metadump "$seqres.$flatdev.orebuild.md" "$device" \
+                               "$logdev" compress -a -o >> $seqres.full
+               fi
+       fi
+
        if [ $ok -eq 0 ]; then
                status=1
                if [ "$iam" != "check" ]; then
@@ -698,13 +1058,36 @@ _check_xfs_test_fs()
        return $?
 }
 
+_check_xfs_scratch_fs()
+{
+       local device="${1:-$SCRATCH_DEV}"
+       local scratch_log="none"
+       local scratch_rt="none"
+       [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_LOGDEV" ] && \
+           scratch_log="$SCRATCH_LOGDEV"
+
+       [ "$USE_EXTERNAL" = yes -a ! -z "$SCRATCH_RTDEV" ] && \
+           scratch_rt="$SCRATCH_RTDEV"
+
+       _check_xfs_filesystem $device $scratch_log $scratch_rt
+}
+
+# modeled after _scratch_xfs_repair
+_test_xfs_repair()
+{
+       TEST_OPTIONS=""
+       [ "$USE_EXTERNAL" = yes -a ! -z "$TEST_LOGDEV" ] && \
+               TEST_OPTIONS="-l$TEST_LOGDEV"
+       [ "$USE_EXTERNAL" = yes -a ! -z "$TEST_RTDEV" ] && \
+               TEST_OPTIONS=$TEST_OPTIONS" -r$TEST_RTDEV"
+       [ "$LARGE_TEST_DEV" = yes ] && TEST_OPTIONS=$TEST_OPTIONS" -t"
+       $XFS_REPAIR_PROG $TEST_OPTIONS $* $TEST_DEV
+}
+
 _require_xfs_test_rmapbt()
 {
        _require_test
-
-       if [ "$($XFS_INFO_PROG "$TEST_DIR" | grep -c "rmapbt=1")" -ne 1 ]; then
-               _notrun "rmapbt not supported by test filesystem type: $FSTYP"
-       fi
+       _require_xfs_has_feature "$TEST_DIR" rmapbt
 }
 
 _require_xfs_scratch_rmapbt()
@@ -713,10 +1096,7 @@ _require_xfs_scratch_rmapbt()
 
        _scratch_mkfs > /dev/null
        _scratch_mount
-       if [ "$($XFS_INFO_PROG "$SCRATCH_MNT" | grep -c "rmapbt=1")" -ne 1 ]; then
-               _scratch_unmount
-               _notrun "rmapbt not supported by scratch filesystem type: $FSTYP"
-       fi
+       _require_xfs_has_feature "$SCRATCH_MNT" rmapbt
        _scratch_unmount
 }
 
@@ -769,7 +1149,7 @@ _reset_xfs_sysfs_error_handling()
                _get_fs_sysfs_attr $dev error/metadata/${e}/max_retries
 
                _set_fs_sysfs_attr $dev \
-                                  error/metadata/${e}/retry_timeout_seconds 0
+                                  error/metadata/${e}/retry_timeout_seconds -1
                echo -n "error/metadata/${e}/retry_timeout_seconds="
                _get_fs_sysfs_attr $dev \
                                   error/metadata/${e}/retry_timeout_seconds
@@ -788,6 +1168,42 @@ _scratch_xfs_unmount_dirty()
        _scratch_unmount
 }
 
+# Prepare a mounted filesystem for an IO error shutdown test by disabling retry
+# for metadata writes.  This prevents a (rare) log livelock when:
+#
+# - The log has given out all available grant space, preventing any new
+#   writers from tripping over IO errors (and shutting down the fs/log),
+# - All log buffers were written to disk, and
+# - The log tail is pinned because the AIL keeps hitting EIO trying to write
+#   committed changes back into the filesystem.
+#
+# Real users might want the default behavior of the AIL retrying writes forever
+# but for testing purposes we don't want to wait.
+#
+# The sole parameter should be the filesystem data device, e.g. $SCRATCH_DEV.
+_xfs_prepare_for_eio_shutdown()
+{
+       local dev="$1"
+       local ctlfile="error/fail_at_unmount"
+
+       # Once we enable IO errors, it's possible that a writer thread will
+       # trip over EIO, cancel the transaction, and shut down the system.
+       # This is expected behavior, so we need to remove the "Internal error"
+       # message from the list of things that can cause the test to be marked
+       # as failed.
+       _add_dmesg_filter "Internal error"
+
+       # Don't retry any writes during the (presumably) post-shutdown unmount
+       _has_fs_sysfs "$ctlfile" && _set_fs_sysfs_attr $dev "$ctlfile" 1
+
+       # Disable retry of metadata writes that fail with EIO
+       for ctl in max_retries retry_timeout_seconds; do
+               ctlfile="error/metadata/EIO/$ctl"
+
+               _has_fs_sysfs "$ctlfile" && _set_fs_sysfs_attr $dev "$ctlfile" 0
+       done
+}
+
 # Skip if we are running an older binary without the stricter input checks.
 # Make multiple checks to be sure that there is no regression on the one
 # selected feature check, which would skew the result.
@@ -919,6 +1335,15 @@ _require_no_xfs_bug_on_assert()
        fi
 }
 
+# Require that XFS is not configured in always_cow mode.
+_require_no_xfs_always_cow()
+{
+       if [ -f /sys/fs/xfs/debug/always_cow ]; then
+               grep -q "1" /sys/fs/xfs/debug/always_cow && \
+                  _notrun "test requires XFS always_cow to be off, turn it off to run the test"
+       fi
+}
+
 # Get a metadata field
 # The first arg is the field name
 # The rest of the arguments are xfs_db commands to find the metadata.
@@ -954,8 +1379,8 @@ _scratch_xfs_set_metadata_field()
        done
 
        local wr_cmd="write"
-       _scratch_xfs_db -x -c "help write" | egrep -q "(-c|-d)" && value="-- ${value}"
-       _scratch_xfs_db -x -c "help write" | egrep -q "(-d)" && wr_cmd="${wr_cmd} -d"
+       _scratch_xfs_db -x -c "help write" | grep -E -q "(-c|-d)" && value="-- ${value}"
+       _scratch_xfs_db -x -c "help write" | grep -E -q "(-d)" && wr_cmd="${wr_cmd} -d"
        _scratch_xfs_db -x "${cmds[@]}" -c "${wr_cmd} ${key} ${value}"
 }
 
@@ -1095,8 +1520,8 @@ _force_xfsv4_mount_options()
        local pquota=0
 
        # Can't have group and project quotas in XFS v4
-       echo "$MOUNT_OPTIONS" | egrep -q "(gquota|grpquota|grpjquota=|gqnoenforce)" && gquota=1
-       echo "$MOUNT_OPTIONS" | egrep -q "(\bpquota|prjquota|pqnoenforce)" && pquota=1
+       echo "$MOUNT_OPTIONS" | grep -E -q "(gquota|grpquota|grpjquota=|gqnoenforce)" && gquota=1
+       echo "$MOUNT_OPTIONS" | grep -E -q "(\bpquota|prjquota|pqnoenforce)" && pquota=1
 
        if [ $gquota -gt 0 ] && [ $pquota -gt 0 ]; then
                export MOUNT_OPTIONS=$(echo $MOUNT_OPTIONS \
@@ -1112,7 +1537,7 @@ _force_xfsv4_mount_options()
 # Find AG count of mounted filesystem
 _xfs_mount_agcount()
 {
-       $XFS_INFO_PROG "$1" | grep agcount= | sed -e 's/^.*agcount=\([0-9]*\),.*$/\1/g'
+       $XFS_INFO_PROG "$1" | sed -n "s/^.*agcount=\([[:digit:]]*\).*/\1/p"
 }
 
 # Wipe the superblock of each XFS AGs
@@ -1283,8 +1708,8 @@ _require_scratch_xfs_bigtime()
                _notrun "mkfs.xfs doesn't support bigtime feature"
        _try_scratch_mount || \
                _notrun "kernel doesn't support xfs bigtime feature"
-       $XFS_INFO_PROG "$SCRATCH_MNT" | grep -q -w "bigtime=1" || \
-               _notrun "bigtime feature not advertised on mount?"
+       _require_xfs_has_feature $SCRATCH_MNT bigtime -u \
+               "crc feature not supported by this filesystem"
        _scratch_unmount
 }
 
@@ -1328,3 +1753,137 @@ _xfs_filter_mkfs()
                print STDOUT "realtime =RDEV extsz=XXX blocks=XXX, rtextents=XXX\n";
        }'
 }
+
+_require_xfsrestore_xflag()
+{
+       $XFSRESTORE_PROG -h 2>&1 | grep -q -w -e '-x' || \
+                       _notrun 'xfsrestore does not support -x flag.'
+}
+
+# Number of bytes reserved for a full inode record, which includes the
+# immediate fork areas.
+_xfs_get_inode_size()
+{
+       local mntpoint="$1"
+
+       $XFS_INFO_PROG "$mntpoint" | sed -n '/meta-data=.*isize/s/^.*isize=\([0-9]*\).*$/\1/p'
+}
+
+# Number of bytes reserved for only the inode record, excluding the
+# immediate fork areas.
+_xfs_get_inode_core_bytes()
+{
+       local dir="$1"
+
+       if _xfs_has_feature "$dir" crc; then
+               # v5 filesystems
+               echo 176
+       else
+               # v4 filesystems
+               echo 96
+       fi
+}
+
+# Create a file with a lower inode number than the root inode number. For this
+# creation, this function runs mkfs and mount on the scratch device with
+# options. This function prints the root inode number and the created inode
+# number.
+_scratch_xfs_create_fake_root()
+{
+       local root_inum
+       local inum
+
+       # A large stripe unit will put the root inode out quite far
+       # due to alignment, leaving free blocks ahead of it.
+       _scratch_mkfs_xfs -d sunit=1024,swidth=1024 > $seqres.full 2>&1 || _fail "mkfs failed"
+
+       # Mounting /without/ a stripe should allow inodes to be allocated
+       # in lower free blocks, without the stripe alignment.
+       _scratch_mount -o sunit=0,swidth=0
+
+       local root_inum=$(stat -c %i $SCRATCH_MNT)
+
+       # Consume space after the root inode so that the blocks before
+       # root look "close" for the next inode chunk allocation
+       $XFS_IO_PROG -f -c "falloc 0 16m" $SCRATCH_MNT/fillfile
+
+       # And make a bunch of inodes until we (hopefully) get one lower
+       # than root, in a new inode chunk.
+       echo "root_inum: $root_inum" >> $seqres.full
+       for i in $(seq 0 4096) ; do
+               fname=$SCRATCH_MNT/$(printf "FILE_%03d" $i)
+               touch $fname
+               inum=$(stat -c "%i" $fname)
+               [[ $inum -lt $root_inum ]] && break
+       done
+
+       echo "created: $inum" >> $seqres.full
+
+       [[ $inum -lt $root_inum ]] || _notrun "Could not set up test"
+
+       echo "$root_inum $inum"
+}
+
+# Find us the path to the AG header containing a per-AG btree with a specific
+# height.
+_scratch_xfs_find_agbtree_height() {
+       local bt_type="$1"
+       local bt_height="$2"
+       local agcount=$(_xfs_mount_agcount $SCRATCH_DEV)
+
+       case "${bt_type}" in
+       "bno"|"cnt"|"rmap"|"refcnt")
+               hdr="agf"
+               bt_prefix="${bt_type}"
+               ;;
+       "ino")
+               hdr="agi"
+               bt_prefix=""
+               ;;
+       "fino")
+               hdr="agi"
+               bt_prefix="free_"
+               ;;
+       *)
+               _fail "Don't know about AG btree ${bt_type}"
+               ;;
+       esac
+
+       for ((agno = 0; agno < agcount; agno++)); do
+               bt_level=$(_scratch_xfs_db -c "${hdr} ${agno}" -c "p ${bt_prefix}level" | awk '{print $3}')
+               # "level" is really the btree height
+               if [ "${bt_level}" -eq "${bt_height}" ]; then
+                       echo "${hdr} ${agno}"
+                       return 0
+               fi
+       done
+
+       return 1
+}
+
+_require_xfs_mkfs_atomicswap()
+{
+       # atomicswap can be activated on rmap or reflink filesystems.
+       # reflink is newer (4.9 for reflink vs. 4.8 for rmap) so test that.
+       _scratch_mkfs_xfs_supported -m reflink=1 >/dev/null 2>&1 || \
+               _notrun "mkfs.xfs doesn't have atomicswap dependent features"
+}
+
+_require_xfs_scratch_atomicswap()
+{
+       _require_xfs_mkfs_atomicswap
+       _require_scratch
+       _require_xfs_io_command swapext '-v exchrange -a'
+       _scratch_mkfs -m reflink=1 > /dev/null
+       _try_scratch_mount || \
+               _notrun "atomicswap dependencies not supported by scratch filesystem type: $FSTYP"
+       _scratch_unmount
+}
+
+# Return the maximum start offset that the FITRIM command will accept, in units
+# of 1024 byte blocks.
+_xfs_discard_max_offset_kb()
+{
+       $XFS_IO_PROG -c 'statfs' "$1" | \
+               awk '{g[$1] = $3} END {print (g["geom.bsize"] * g["geom.datablocks"] / 1024)}'
+}
diff --git a/common/zoned b/common/zoned
new file mode 100644 (file)
index 0000000..eed0082
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# Common zoned block device specific functions
+#
+
+#
+# blkzone report added zone capacity to be printed from v2.37.
+# This filter will add an extra column 'cap' with the same value of
+# 'len'(zone size) for blkzone version < 2.37
+#
+# Before: start: 0x000100000, len 0x040000, wptr 0x000000 ..
+# After: start: 0x000100000, len 0x040000, cap 0x040000, wptr 0x000000 ..
+_filter_blkzone_report()
+{
+       $AWK_PROG -F "," 'BEGIN{OFS=",";} $3 !~ /cap/ {$2=$2","$2;} {print;}' |\
+       sed -e 's/len/cap/2'
+}
+
+_require_limited_active_zones() {
+    local dev=$1
+    local sysfs=$(_sysfs_dev ${dev})
+    local attr="${sysfs}/queue/max_active_zones"
+
+    [ -e "${attr}" ] || _notrun "cannot find queue/max_active_zones. Maybe non-zoned device?"
+    if [ $(cat "${attr}") == 0 ]; then
+       _notrun "this test requires limited active zones"
+    fi
+}
+
+_zone_capacity() {
+    local phy=$1
+    local dev=$2
+
+    [ -z "$dev" ] && dev=$SCRATCH_DEV
+
+    size=$($BLKZONE_PROG report -o $phy -l 1 $dev |\
+              _filter_blkzone_report |\
+              grep -Po "cap 0x[[:xdigit:]]+" | cut -d ' ' -f 2)
+    echo $((size << 9))
+}
index cbf8377988557ae5df846af0c4f271f74c7cac1f..4e567d3c96f5a93c12187dc341be9d48e5f8b6b4 100644 (file)
@@ -33,6 +33,7 @@ AC_CHECK_HEADERS([    assert.h                \
                        btrfs/ioctl.h           \
                        cifs/ioctl.h            \
                        sys/mman.h              \
+                       linux/ext4.h            \
 ])
 
 AC_CHECK_HEADERS([xfs/xfs_log_format.h],,,[
@@ -61,11 +62,17 @@ AC_PACKAGE_WANT_LIBCAP
 AC_PACKAGE_WANT_LINUX_FIEMAP_H
 AC_PACKAGE_WANT_FALLOCATE
 AC_PACKAGE_WANT_OPEN_BY_HANDLE_AT
-AC_PACKAGE_WANT_LINUX_PRCTL_H
 AC_PACKAGE_WANT_LINUX_FS_H
 AC_PACKAGE_WANT_LIBBTRFSUTIL
 
 AC_HAVE_COPY_FILE_RANGE
+AC_HAVE_SEEK_DATA
+AC_HAVE_BMV_OF_SHARED
+AC_HAVE_NFTW
+AC_HAVE_RLIMIT_NOFILE
+AC_HAVE_XFS_IOC_EXCHANGE_RANGE
+AC_HAVE_FICLONE
+
 AC_CHECK_FUNCS([renameat2])
 AC_CHECK_FUNCS([reallocarray])
 AC_CHECK_TYPES([struct mount_attr], [], [], [[#include <linux/mount.h>]])
@@ -102,6 +109,7 @@ AC_CHECK_MEMBERS([struct btrfs_ioctl_vol_args_v2.subvolid], [], [], [[
 #include <stddef.h>
 #include <linux/btrfs.h>
 ]])
+AC_CHECK_DECLS([BTRFS_IOC_SNAP_DESTROY_V2],,,[#include <linux/btrfs.h>])
 
 AC_CONFIG_HEADERS([include/config.h])
 AC_CONFIG_FILES([include/builddefs])
index ef411b5e3613e974b21f4ee427fcd09fae759fc4..50262e02f6810d6538dd6270eeb494bb3f9fb49f 100644 (file)
@@ -34,8 +34,11 @@ dangerous_bothrepair fuzzers to evaluate xfs_scrub + xfs_repair repair
 dangerous_fuzzers      fuzzers that can crash your computer
 dangerous_norepair     fuzzers to evaluate kernel metadata verifiers
 dangerous_online_repair        fuzzers to evaluate xfs_scrub online repair
+dangerous_fsstress_repair      race fsstress and xfs_scrub online repair
+dangerous_fsstress_scrub       race fsstress and xfs_scrub checking
 dangerous_repair       fuzzers to evaluate xfs_repair offline repair
 dangerous_scrub                fuzzers to evaluate xfs_scrub checking
+dangerous_selftest     selftests that crash/hang
 data                   data loss checkers
 dax                    direct access mode for persistent memory files
 db                     xfs_db functional tests
@@ -47,7 +50,9 @@ eio                   IO error reporting
 encrypt                        encrypted file contents
 enospc                 ENOSPC error reporting
 exportfs               file handles
+fiemap                 fiemap ioctl
 filestreams            XFS filestreams allocator
+fiexchange             XFS_IOC_EXCHANGE_RANGE ioctl
 freeze                 filesystem freeze tests
 fsck                   general fsck tests
 fsmap                  FS_IOC_GETFSMAP ioctl
@@ -65,6 +70,7 @@ label                 filesystem labelling
 limit                  resource limits
 locks                  file locking
 log                    metadata logging
+logical_resolve                btrfs logical to ino ioctl (logical-resolve command)
 logprint               xfs_logprint functional tests
 long_rw                        long-soak read write IO path exercisers
 metacopy               overlayfs metadata-only copy-up
@@ -88,6 +94,7 @@ punch                 fallocate FALLOC_FL_PUNCH_HOLE
 qgroup                 btrfs qgroup feature
 quota                  filesystem usage quotas
 raid                   btrfs RAID
+raid-stripe-tree       btrfs raid-stripe-tree feature
 read_repair            btrfs error correction on read failure
 realtime               XFS realtime volumes
 recoveryloop           crash recovery loops
@@ -108,18 +115,23 @@ samefs                    overlayfs when all layers are on the same fs
 scrub                  filesystem metadata scrubbers
 seed                   btrfs seeded filesystems
 seek                   llseek functionality
+selftest               tests with fixed results, used to validate testing setup
 send                   btrfs send/receive
 shrinkfs               decreasing the size of a filesystem
 shutdown               FS_IOC_SHUTDOWN ioctl
+smoketest              Simple smoke tests
 snapshot               btrfs snapshots
-soak                   long running soak tests of any kind
+soak                   long running soak tests whose runtime can be controlled
+                        directly by setting the SOAK_DURATION variable
 spaceman               xfs_spaceman functional tests
 splice                 splice system call
 stress                 fsstress filesystem exerciser
 subvol                 btrfs subvolumes
 swap                   swap files
+swapext                        XFS_IOC_SWAPEXT ioctl
 symlink                        symbolic links
 tape                   dump and restore with a tape
+tempfsid               temporary fsid
 thin                   thin provisioning
 trim                   FITRIM ioctl
 udf                    UDF functionality tests
index 45d2756bd2ceec07cb6b2ed5b4e6997dc0a14f60..802bf2a3fb22117d09a1b450065e02335d7e4a10 100644 (file)
@@ -16,6 +16,9 @@ they have.  This is done with _require_<xxx> macros, which may take parameters.
 
        _require_chattr <letters>
        _require_exportfs
+       _require_sgid_inheritance
+       _require_use_local_uidgid
+       _require_unix_perm_checking
 
  (3) System call requirements.
 
@@ -97,6 +100,26 @@ _require_exportfs
      The test also requires the use of the open_by_handle_at() system call and
      will be skipped if it isn't available in the kernel.
 
+_require_sgid_inheritance
+
+     The test required that the $TEST_DEV filesystem supports the inheritance
+     of the SGID bit and the GID from a marked directory.  The test will be
+     skipped if not supported.
+
+_require_use_local_uidgid
+
+     The test requires that the $TEST_DEV filesystem sets the uid and gid of a
+     newly created file to the creating process's fsuid and fsgid.  Remote
+     filesystems, for example, may choose other settings or not even have these
+     concepts available.  The test will be skipped if not supported.
+
+_require_unix_perm_checking
+
+     The test requires that the $TEST_DEV filesystem performs traditional UNIX
+     file permissions checking.  A remote filesystem, for example, might use
+     some alternative distributed permissions model involving authentication
+     tokens rather than the local fsuid/fsgid.
+
 
 ========================
 SYSTEM CALL REQUIREMENTS
diff --git a/doc/testing-afs.txt b/doc/testing-afs.txt
new file mode 100644 (file)
index 0000000..75ed133
--- /dev/null
@@ -0,0 +1,36 @@
+                                ===========
+                                TESTING AFS
+                                ===========
+
+xfstests can be used with the Linux kernel AFS filesystem (kafs).  kafs mounts
+each volume as a separate volume and, as such, allows them to be individually
+mounted on arbitrary paths.  This allows xfstests to be easily configured to
+use specific volumes.  This doesn't work with OpenAFS, say, as that operates
+with everything in a single superblock.
+
+xfstests requires that the volumes specified by forced to use RW volumes by
+prefixing the mount device with '%' rather than '#' as per the format of a
+mountpoint string:
+
+       %<volume>
+
+using the workstation cell, or:
+
+       %<cell>:<volume>
+
+using an explicit cell.
+
+For example:
+
+       FSTYP=afs
+       TEST_DEV=%example.com:xfstest.test
+       TEST_DIR=/mnt/xfstest.test
+       SCRATCH_DEV=%example.com:xfstest.scratch
+       SCRATCH_MNT=/mnt/xfstest.scratch
+
+will use the xfstest.test and xfstest.scratch volumes located in the
+example.com cell.
+
+
+Note that AFS in general and kafs in particular lack a number of features that
+can be tested for and so a lot of tests will be skipped.
diff --git a/doc/xunit.xsd b/doc/xunit.xsd
new file mode 100644 (file)
index 0000000..d287eaf
--- /dev/null
@@ -0,0 +1,246 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema
+ xmlns="https://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+
+ targetNamespace="https://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified">
+    <xs:annotation>
+        <xs:documentation xml:lang="en">fstests xunit test result schema, derived from https://github.com/windyroad/JUnit-Schema</xs:documentation>
+    </xs:annotation>
+    <xs:element name="testsuite" type="testsuite"/>
+    <xs:simpleType name="ISO8601_DATETIME_PATTERN">
+        <xs:restriction base="xs:dateTime">
+            <xs:pattern value="[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[+-][0-9]{2}:[0-9]{2}"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:element name="testsuites">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">Contains an aggregation of testsuite results</xs:documentation>
+        </xs:annotation>
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="testsuite" minOccurs="0" maxOccurs="unbounded">
+                    <xs:complexType>
+                        <xs:complexContent>
+                            <xs:extension base="testsuite">
+                                <xs:attribute name="package" type="xs:token" use="required">
+                                    <xs:annotation>
+                                        <xs:documentation xml:lang="en">Derived from testsuite/@name in the non-aggregated documents</xs:documentation>
+                                    </xs:annotation>
+                                </xs:attribute>
+                                <xs:attribute name="id" type="xs:int" use="required">
+                                    <xs:annotation>
+                                        <xs:documentation xml:lang="en">Starts at '0' for the first testsuite and is incremented by 1 for each following testsuite</xs:documentation>
+                                    </xs:annotation>
+                                </xs:attribute>
+                            </xs:extension>
+                        </xs:complexContent>
+                    </xs:complexType>
+                </xs:element>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:complexType name="testsuite">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">Contains the results of executing a testsuite</xs:documentation>
+        </xs:annotation>
+        <xs:sequence>
+            <xs:element name="properties">
+                <xs:annotation>
+                    <xs:documentation xml:lang="en">Properties (e.g., environment settings) set during test execution</xs:documentation>
+                </xs:annotation>
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:element name="property" minOccurs="0" maxOccurs="unbounded">
+                            <xs:complexType>
+                                <xs:attribute name="name" use="required">
+                                    <xs:simpleType>
+                                        <xs:restriction base="xs:token">
+                                            <xs:minLength value="1"/>
+                                        </xs:restriction>
+                                    </xs:simpleType>
+                                </xs:attribute>
+                                <xs:attribute name="value" type="xs:string" use="required"/>
+                            </xs:complexType>
+                        </xs:element>
+                    </xs:sequence>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="testcase" minOccurs="0" maxOccurs="unbounded">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:choice minOccurs="0" maxOccurs="1">
+                            <xs:element name="skipped" minOccurs="0" maxOccurs="1">
+                                <xs:annotation>
+                                    <xs:documentation xml:lang="en">Indicates that the test was skipped.</xs:documentation>
+                                </xs:annotation>
+                                <xs:complexType>
+                                    <xs:simpleContent>
+                                        <xs:extension base="pre-string">
+                                            <xs:attribute name="message" type="xs:string">
+                                                <xs:annotation>
+                                                    <xs:documentation xml:lang="en">The message specifying why the test case was skipped</xs:documentation>
+                                                </xs:annotation>
+                                            </xs:attribute>
+                                        </xs:extension>
+                                    </xs:simpleContent>
+                                </xs:complexType>
+                            </xs:element>
+                            <xs:element name="error" minOccurs="0" maxOccurs="1">
+                                <xs:annotation>
+                                    <xs:documentation xml:lang="en">Indicates that the test errored.  An errored test is one that had an unanticipated problem. e.g., an unchecked throwable; or a problem with the implementation of the test. Contains as a text node relevant data for the error, e.g., a stack trace</xs:documentation>
+                                </xs:annotation>
+                                <xs:complexType>
+                                    <xs:simpleContent>
+                                        <xs:extension base="pre-string">
+                                            <xs:attribute name="message" type="xs:string">
+                                                <xs:annotation>
+                                                    <xs:documentation xml:lang="en">The error message. e.g., if a java exception is thrown, the return value of getMessage()</xs:documentation>
+                                                </xs:annotation>
+                                            </xs:attribute>
+                                            <xs:attribute name="type" type="xs:string" use="required">
+                                                <xs:annotation>
+                                                    <xs:documentation xml:lang="en">The type of error that occured. e.g., if a java execption is thrown the full class name of the exception.</xs:documentation>
+                                                </xs:annotation>
+                                            </xs:attribute>
+                                        </xs:extension>
+                                    </xs:simpleContent>
+                                </xs:complexType>
+                            </xs:element>
+                            <xs:element name="failure">
+                                <xs:annotation>
+                                    <xs:documentation xml:lang="en">Indicates that the test failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals. Contains as a text node relevant data for the failure, e.g., a stack trace</xs:documentation>
+                                </xs:annotation>
+                                <xs:complexType>
+                                    <xs:simpleContent>
+                                        <xs:extension base="pre-string">
+                                            <xs:attribute name="message" type="xs:string">
+                                                <xs:annotation>
+                                                    <xs:documentation xml:lang="en">The message specified in the assert</xs:documentation>
+                                                </xs:annotation>
+                                            </xs:attribute>
+                                            <xs:attribute name="type" type="xs:string" use="required">
+                                                <xs:annotation>
+                                                    <xs:documentation xml:lang="en">The type of the assert.</xs:documentation>
+                                                </xs:annotation>
+                                            </xs:attribute>
+                                        </xs:extension>
+                                    </xs:simpleContent>
+                                </xs:complexType>
+                            </xs:element>
+                        </xs:choice>
+                        <xs:choice minOccurs="0" maxOccurs="3">
+                            <xs:element name="system-out" minOccurs="0" maxOccurs="1">
+                                <xs:annotation>
+                                    <xs:documentation xml:lang="en">Data that was written to the .full log file while the test was executed.</xs:documentation>
+                                </xs:annotation>
+                                <xs:simpleType>
+                                    <xs:restriction base="pre-string">
+                                        <xs:whiteSpace value="preserve"/>
+                                    </xs:restriction>
+                                </xs:simpleType>
+                            </xs:element>
+                            <xs:element name="system-err" minOccurs="0" maxOccurs="1">
+                                <xs:annotation>
+                                    <xs:documentation xml:lang="en">Data that was compared to the golden output file after the test was executed.</xs:documentation>
+                                </xs:annotation>
+                                <xs:simpleType>
+                                    <xs:restriction base="pre-string">
+                                        <xs:whiteSpace value="preserve"/>
+                                    </xs:restriction>
+                                </xs:simpleType>
+                            </xs:element>
+                            <xs:element name="kernel-log" minOccurs="0" maxOccurs="1">
+                                <xs:annotation>
+                                    <xs:documentation xml:lang="en">Kernel log recorded while the test was executed.</xs:documentation>
+                                </xs:annotation>
+                                <xs:simpleType>
+                                    <xs:restriction base="pre-string">
+                                        <xs:whiteSpace value="preserve"/>
+                                    </xs:restriction>
+                                </xs:simpleType>
+                            </xs:element>
+                        </xs:choice>
+                    </xs:sequence>
+                    <xs:attribute name="name" type="xs:token" use="required">
+                        <xs:annotation>
+                            <xs:documentation xml:lang="en">Name of the test method</xs:documentation>
+                        </xs:annotation>
+                    </xs:attribute>
+                    <xs:attribute name="classname" type="xs:token" use="required">
+                        <xs:annotation>
+                            <xs:documentation xml:lang="en">Full class name for the class the test method is in.</xs:documentation>
+                        </xs:annotation>
+                    </xs:attribute>
+                    <xs:attribute name="time" type="xs:decimal" use="required">
+                        <xs:annotation>
+                            <xs:documentation xml:lang="en">Time taken (in seconds) to execute the test</xs:documentation>
+                        </xs:annotation>
+                    </xs:attribute>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+        <xs:attribute name="name" use="required">
+            <xs:annotation>
+                <xs:documentation xml:lang="en">Full class name of the test for non-aggregated testsuite documents. Class name without the package for aggregated testsuites documents</xs:documentation>
+            </xs:annotation>
+            <xs:simpleType>
+                <xs:restriction base="xs:token">
+                    <xs:minLength value="1"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+        <xs:attribute name="timestamp" type="ISO8601_DATETIME_PATTERN" use="required">
+            <xs:annotation>
+                <xs:documentation xml:lang="en">Time that the last testcase was started. If no tests are started, this is the time the report was generated. Timezone must be specified as an offset from UTC.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="report_timestamp" type="ISO8601_DATETIME_PATTERN" use="required">
+            <xs:annotation>
+                <xs:documentation xml:lang="en">Time that the report was generated.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="start_timestamp" type="ISO8601_DATETIME_PATTERN" use="required">
+            <xs:annotation>
+                <xs:documentation xml:lang="en">Time that fstests was started.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="hostname" use="required">
+            <xs:annotation>
+                <xs:documentation xml:lang="en">Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined.</xs:documentation>
+            </xs:annotation>
+            <xs:simpleType>
+                <xs:restriction base="xs:token">
+                    <xs:minLength value="1"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+        <xs:attribute name="tests" type="xs:int" use="required">
+            <xs:annotation>
+                <xs:documentation xml:lang="en">The total number of tests in the suite</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="failures" type="xs:int" use="required">
+            <xs:annotation>
+                <xs:documentation xml:lang="en">The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed by using the mechanisms for that purpose. e.g., via an assertEquals</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="skipped" type="xs:int" use="optional">
+            <xs:annotation>
+                <xs:documentation xml:lang="en">The total number of ignored or skipped tests in the suite.</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+        <xs:attribute name="time" type="xs:decimal" use="required">
+            <xs:annotation>
+                <xs:documentation xml:lang="en">Time taken (in seconds) to execute the tests in the suite</xs:documentation>
+            </xs:annotation>
+        </xs:attribute>
+    </xs:complexType>
+    <xs:simpleType name="pre-string">
+        <xs:restriction base="xs:string">
+            <xs:whiteSpace value="preserve"/>
+        </xs:restriction>
+    </xs:simpleType>
+</xs:schema>
index 6641209f81a1311c51d423a5fa29a95daa067de6..ce95fe7d4b2e14672f724e0392367141b4bdf3b4 100644 (file)
@@ -68,6 +68,12 @@ HAVE_FIEMAP = @have_fiemap@
 HAVE_FALLOCATE = @have_fallocate@
 HAVE_COPY_FILE_RANGE = @have_copy_file_range@
 HAVE_LIBBTRFSUTIL = @have_libbtrfsutil@
+HAVE_SEEK_DATA = @have_seek_data@
+HAVE_NFTW = @have_nftw@
+HAVE_BMV_OF_SHARED = @have_bmv_of_shared@
+HAVE_RLIMIT_NOFILE = @have_rlimit_nofile@
+HAVE_XFS_IOC_EXCHANGE_RANGE = @have_xfs_ioc_exchange_range@
+HAVE_FICLONE = @have_ficlone@
 
 GCCFLAGS = -funsigned-char -fno-strict-aliasing -Wall
 
diff --git a/lsqa.pl b/lsqa.pl
index 8ca1167f845c79d671ae667b7cfe1bc1a397a714..63bee58234f99028c9174c82b8bcc4b43f84d3c2 100755 (executable)
--- a/lsqa.pl
+++ b/lsqa.pl
@@ -99,7 +99,7 @@ sub get_qa_header($) {
        m/^# SPDX/i             and next; # SPDX tags
        m/^# Copyright/i        and next; # Copyright tags
        m/^#\s*\-{10}/          and last; # dashed lines
-       m/^seq/i                and last; # test start
+       m/^\. \.\/common\/preamble/ and last; # test start
 
        s/^# *//;
 
index 85f634145c67f4c9ebf8907d300b0357500b0b4a..c0b28240764b1f6dbf02bc7325797f7021d6db5e 100644 (file)
@@ -36,6 +36,10 @@ ifeq ($(HAVE_COPY_FILE_RANGE),yes)
 LCFLAGS += -DHAVE_COPY_FILE_RANGE
 endif
 
+ifeq ($(HAVE_XFS_IOC_EXCHANGE_RANGE),yes)
+LCFLAGS += -DHAVE_XFS_IOC_EXCHANGE_RANGE
+endif
+
 default: depend $(TARGETS)
 
 depend: .dep
index 83f8cf556325574a84ae1b0aaa4817f665bd7398..fd64df0f674eba1cbc120ee484a230d7550fbf67 100644 (file)
@@ -3106,6 +3106,7 @@ char      *opts;
 
                case 'C':
                        C_opt++;
+                       tok = strtok(optarg, ",");
                        for(s=checkmap; s->string != NULL; s++)
                                if(!strcmp(s->string, optarg))
                                        break;
index b395bc4da24675954a07aa0aca66657ab9b8dc85..9d2631f7f91b0faa121f2743b9dd327a00931984 100644 (file)
@@ -143,6 +143,7 @@ typedef enum {
        OP_URING_WRITE,
        OP_WRITE,
        OP_WRITEV,
+       OP_XCHGRANGE,
        OP_LAST
 } opty_t;
 
@@ -272,6 +273,8 @@ void        uring_read_f(opnum_t, long);
 void   uring_write_f(opnum_t, long);
 void   write_f(opnum_t, long);
 void   writev_f(opnum_t, long);
+void   xchgrange_f(opnum_t, long);
+
 char   *xattr_flag_to_string(int);
 
 struct opdesc  ops[OP_LAST]    = {
@@ -340,6 +343,7 @@ struct opdesc       ops[OP_LAST]    = {
        [OP_URING_WRITE]   = {"uring_write",   uring_write_f,   1, 1 },
        [OP_WRITE]         = {"write",         write_f,         4, 1 },
        [OP_WRITEV]        = {"writev",        writev_f,        4, 1 },
+       [OP_XCHGRANGE]     = {"xchgrange",     xchgrange_f,     2, 1 },
 }, *ops_end;
 
 flist_t        flist[FT_nft] = {
@@ -382,6 +386,8 @@ char                *execute_cmd = NULL;
 int            execute_freq = 1;
 struct print_string    flag_str = {0};
 
+struct timespec deadline = { 0 };
+
 void   add_to_flist(int, int, int, int);
 void   append_pathname(pathname_t *, char *);
 int    attr_list_path(pathname_t *, char *, const int);
@@ -426,6 +432,7 @@ int symlink_path(const char *, pathname_t *);
 int    truncate64_path(pathname_t *, off64_t);
 int    unlink_path(pathname_t *);
 void   usage(void);
+void   read_freq(void);
 void   write_freq(void);
 void   zero_freq(void);
 void   non_btrfs_freq(const char *);
@@ -454,6 +461,34 @@ void sg_handler(int signum)
        }
 }
 
+bool
+keep_looping(int i, int loops)
+{
+       int ret;
+
+       if (deadline.tv_nsec) {
+               struct timespec now;
+
+               ret = clock_gettime(CLOCK_MONOTONIC, &now);
+               if (ret) {
+                       perror("CLOCK_MONOTONIC");
+                       return false;
+               }
+
+               return now.tv_sec <= deadline.tv_sec;
+       }
+
+       if (!loops)
+               return true;
+
+       return i < loops;
+}
+
+static struct option longopts[] = {
+       {"duration", optional_argument, 0, 256},
+       { }
+};
+
 int main(int argc, char **argv)
 {
        char            buf[10];
@@ -472,14 +507,15 @@ int main(int argc, char **argv)
        xfs_error_injection_t           err_inj;
        struct sigaction action;
        int             loops = 1;
-       const char      *allopts = "cd:e:f:i:l:m:M:n:o:p:rs:S:vVwx:X:zH";
+       const char      *allopts = "cd:e:f:i:l:m:M:n:o:p:rRs:S:vVwx:X:zH";
+       long long       duration;
 
        errrange = errtag = 0;
        umask(0);
        nops = sizeof(ops) / sizeof(ops[0]);
        ops_end = &ops[nops];
        myprog = argv[0];
-       while ((c = getopt(argc, argv, allopts)) != -1) {
+       while ((c = getopt_long(argc, argv, allopts, longopts, NULL)) != -1) {
                switch (c) {
                case 'c':
                        cleanup = 1;
@@ -538,6 +574,9 @@ int main(int argc, char **argv)
                case 'r':
                        namerand = 1;
                        break;
+               case 'R':
+                       read_freq();
+                       break;
                case 's':
                        seed = strtoul(optarg, NULL, 0);
                        break;
@@ -571,6 +610,26 @@ int main(int argc, char **argv)
                case 'X':
                        execute_freq = strtoul(optarg, NULL, 0);
                        break;
+               case 256:  /* --duration */
+                       if (!optarg) {
+                               fprintf(stderr, "Specify time with --duration=\n");
+                               exit(87);
+                       }
+                       duration = strtoll(optarg, NULL, 0);
+                       if (duration < 1) {
+                               fprintf(stderr, "%lld: invalid duration\n", duration);
+                               exit(88);
+                       }
+
+                       i = clock_gettime(CLOCK_MONOTONIC, &deadline);
+                       if (i) {
+                               perror("CLOCK_MONOTONIC");
+                               exit(89);
+                       }
+
+                       deadline.tv_sec += duration;
+                       deadline.tv_nsec = 1;
+                       break;
                case '?':
                        fprintf(stderr, "%s - invalid parameters\n",
                                myprog);
@@ -703,17 +762,33 @@ int main(int argc, char **argv)
 #endif
 #ifdef URING
                        have_io_uring = true;
-                       /* If ENOSYS, just ignore uring, other errors are fatal. */
-                       if (io_uring_queue_init(URING_ENTRIES, &ring, 0)) {
-                               if (errno == ENOSYS) {
-                                       have_io_uring = false;
-                               } else {
-                                       fprintf(stderr, "io_uring_queue_init failed\n");
-                                       exit(1);
-                               }
+                       /*
+                        * If ENOSYS, just ignore uring, due to kernel doesn't support it.
+                        * If EPERM, maybe due to sysctl kernel.io_uring_disabled isn't 0,
+                        *           or some selinux policies, etc.
+                        * Other errors are fatal.
+                        */
+                       c = io_uring_queue_init(URING_ENTRIES, &ring, 0);
+                       switch(c){
+                       case 0:
+                               have_io_uring = true;
+                               break;
+                       case -ENOSYS:
+                               have_io_uring = false;
+                               if (verbose)
+                                       printf("io_uring isn't supported by kernel\n");
+                               break;
+                       case -EPERM:
+                               have_io_uring = false;
+                               if (verbose)
+                                       printf("io_uring isn't allowed, check io_uring_disabled sysctl or selinux policy\n");
+                               break;
+                       default:
+                               fprintf(stderr, "io_uring_queue_init failed, errno=%d\n", -c);
+                               exit(1);
                        }
 #endif
-                       for (i = 0; !loops || (i < loops); i++)
+                       for (i = 0; keep_looping(i, loops); i++)
                                doproc();
 #ifdef AIO
                        if(io_destroy(io_ctx) != 0) {
@@ -1113,6 +1188,26 @@ again:
        return NULL;
 }
 
+bool
+keep_running(opnum_t opno, opnum_t operations)
+{
+       int ret;
+
+       if (deadline.tv_nsec) {
+               struct timespec now;
+
+               ret = clock_gettime(CLOCK_MONOTONIC, &now);
+               if (ret) {
+                       perror("CLOCK_MONOTONIC");
+                       return false;
+               }
+
+               return now.tv_sec <= deadline.tv_sec;
+       }
+
+       return opno < operations;
+}
+
 void
 doproc(void)
 {
@@ -1141,7 +1236,7 @@ doproc(void)
        srandom(seed);
        if (namerand)
                namerand = random();
-       for (opno = 0; opno < operations; opno++) {
+       for (opno = 0; keep_running(opno, operations); opno++) {
                if (execute_cmd && opno && opno % dividend == 0) {
                        if (verbose)
                                printf("%lld: execute command %s\n", opno,
@@ -1917,6 +2012,7 @@ usage(void)
        printf("   -o logfile       specifies logfile name\n");
        printf("   -p nproc         specifies the no. of processes (default 1)\n");
        printf("   -r               specifies random name padding\n");
+       printf("   -R               zeros frequencies of write operations\n");
        printf("   -s seed          specifies the seed for the random generator (default random)\n");
        printf("   -v               specifies verbose mode\n");
        printf("   -w               zeros frequencies of non-write operations\n");
@@ -1926,6 +2022,18 @@ usage(void)
        printf("   -V               specifies verifiable logging mode (omitting inode numbers)\n");
        printf("   -X ncmd          number of calls to the -x command (default 1)\n");
        printf("   -H               prints usage and exits\n");
+       printf("   --duration=s     ignore any -n setting and run for this many seconds\n");
+}
+
+void
+read_freq(void)
+{
+       opdesc_t        *p;
+
+       for (p = ops; p < ops_end; p++) {
+               if (p->iswrite)
+                       p->freq = 0;
+       }
 }
 
 void
@@ -1980,6 +2088,22 @@ void inode_info(char *str, size_t sz, struct stat64 *s, int verbose)
                         (long long) s->st_blocks, (long long) s->st_size);
 }
 
+#ifdef AIO
+static int io_get_single_event(struct io_event *event)
+{
+       int ret;
+
+       /*
+        * We can get -EINTR if competing with io_uring using signal
+        * based notifications. For that case, just retry the wait.
+        */
+       do {
+               ret = io_getevents(io_ctx, 1, 1, event, NULL);
+       } while (ret == -EINTR);
+       return ret;
+}
+#endif
+
 void
 afsync_f(opnum_t opno, long r)
 {
@@ -2019,7 +2143,7 @@ afsync_f(opnum_t opno, long r)
                close(fd);
                return;
        }
-       if ((e = io_getevents(io_ctx, 1, 1, &event, NULL)) != 1) {
+       if ((e = io_get_single_event(&event)) != 1) {
                if (v)
                        printf("%d/%lld: afsync - io_getevents failed %d\n",
                               procid, opno, e);
@@ -2131,7 +2255,7 @@ do_aio_rw(opnum_t opno, long r, int flags)
                               procid, opno, iswrite ? "awrite" : "aread", e);
                goto aio_out;
        }
-       if ((e = io_getevents(io_ctx, 1, 1, &event, NULL)) != 1) {
+       if ((e = io_get_single_event(&event)) != 1) {
                if (v)
                        printf("%d/%lld: %s - io_getevents failed %d\n",
                               procid, opno, iswrite ? "awrite" : "aread", e);
@@ -2478,6 +2602,170 @@ chown_f(opnum_t opno, long r)
        free_pathname(&f);
 }
 
+/* exchange some arbitrary range of f1 to f2...fn. */
+void
+xchgrange_f(
+       opnum_t                 opno,
+       long                    r)
+{
+#ifdef XFS_IOC_EXCHANGE_RANGE
+       struct xfs_exch_range   fxr = { 0 };
+       static __u64            swap_flags = 0;
+       struct pathname         fpath1;
+       struct pathname         fpath2;
+       struct stat64           stat1;
+       struct stat64           stat2;
+       char                    inoinfo1[1024];
+       char                    inoinfo2[1024];
+       off64_t                 lr;
+       off64_t                 off1;
+       off64_t                 off2;
+       off64_t                 max_off2;
+       size_t                  len;
+       int                     v1;
+       int                     v2;
+       int                     fd1;
+       int                     fd2;
+       int                     ret;
+       int                     tries = 0;
+       int                     e;
+
+       /* Load paths */
+       init_pathname(&fpath1);
+       if (!get_fname(FT_REGm, r, &fpath1, NULL, NULL, &v1)) {
+               if (v1)
+                       printf("%d/%lld: xchgrange read - no filename\n",
+                               procid, opno);
+               goto out_fpath1;
+       }
+
+       init_pathname(&fpath2);
+       if (!get_fname(FT_REGm, random(), &fpath2, NULL, NULL, &v2)) {
+               if (v2)
+                       printf("%d/%lld: xchgrange write - no filename\n",
+                               procid, opno);
+               goto out_fpath2;
+       }
+
+       /* Open files */
+       fd1 = open_path(&fpath1, O_RDONLY);
+       e = fd1 < 0 ? errno : 0;
+       check_cwd();
+       if (fd1 < 0) {
+               if (v1)
+                       printf("%d/%lld: xchgrange read - open %s failed %d\n",
+                               procid, opno, fpath1.path, e);
+               goto out_fpath2;
+       }
+
+       fd2 = open_path(&fpath2, O_WRONLY);
+       e = fd2 < 0 ? errno : 0;
+       check_cwd();
+       if (fd2 < 0) {
+               if (v2)
+                       printf("%d/%lld: xchgrange write - open %s failed %d\n",
+                               procid, opno, fpath2.path, e);
+               goto out_fd1;
+       }
+
+       /* Get file stats */
+       if (fstat64(fd1, &stat1) < 0) {
+               if (v1)
+                       printf("%d/%lld: xchgrange read - fstat64 %s failed %d\n",
+                               procid, opno, fpath1.path, errno);
+               goto out_fd2;
+       }
+       inode_info(inoinfo1, sizeof(inoinfo1), &stat1, v1);
+
+       if (fstat64(fd2, &stat2) < 0) {
+               if (v2)
+                       printf("%d/%lld: xchgrange write - fstat64 %s failed %d\n",
+                               procid, opno, fpath2.path, errno);
+               goto out_fd2;
+       }
+       inode_info(inoinfo2, sizeof(inoinfo2), &stat2, v2);
+
+       if (stat1.st_size < (stat1.st_blksize * 2) ||
+           stat2.st_size < (stat2.st_blksize * 2)) {
+               if (v2)
+                       printf("%d/%lld: xchgrange - files are too small\n",
+                               procid, opno);
+               goto out_fd2;
+       }
+
+       /* Never let us swap more than 1/4 of the files. */
+       len = (random() % FILELEN_MAX) + 1;
+       if (len > stat1.st_size / 4)
+               len = stat1.st_size / 4;
+       if (len > stat2.st_size / 4)
+               len = stat2.st_size / 4;
+       len = rounddown_64(len, stat1.st_blksize);
+       if (len == 0)
+               len = stat1.st_blksize;
+
+       /* Calculate offsets */
+       lr = ((int64_t)random() << 32) + random();
+       if (stat1.st_size == len)
+               off1 = 0;
+       else
+               off1 = (off64_t)(lr % MIN(stat1.st_size - len, MAXFSIZE));
+       off1 %= maxfsize;
+       off1 = rounddown_64(off1, stat1.st_blksize);
+
+       /*
+        * If srcfile == destfile, randomly generate destination ranges
+        * until we find one that doesn't overlap the source range.
+        */
+       max_off2 = MIN(stat2.st_size  - len, MAXFSIZE);
+       do {
+               lr = ((int64_t)random() << 32) + random();
+               if (stat2.st_size == len)
+                       off2 = 0;
+               else
+                       off2 = (off64_t)(lr % max_off2);
+               off2 %= maxfsize;
+               off2 = rounddown_64(off2, stat2.st_blksize);
+       } while (stat1.st_ino == stat2.st_ino &&
+                llabs(off2 - off1) < len &&
+                tries++ < 10);
+
+       /* Swap data blocks */
+       fxr.file1_fd = fd1;
+       fxr.file1_offset = off1;
+       fxr.length = len;
+       fxr.file2_offset = off2;
+       fxr.flags = swap_flags;
+
+retry:
+       ret = ioctl(fd2, XFS_IOC_EXCHANGE_RANGE, &fxr);
+       e = ret < 0 ? errno : 0;
+       if (e == EOPNOTSUPP && !(swap_flags & XFS_EXCH_RANGE_NONATOMIC)) {
+               swap_flags = XFS_EXCH_RANGE_NONATOMIC;
+               fxr.flags |= swap_flags;
+               goto retry;
+       }
+       if (v1 || v2) {
+               printf("%d/%lld: xchgrange %s%s [%lld,%lld] -> %s%s [%lld,%lld]",
+                       procid, opno,
+                       fpath1.path, inoinfo1, (long long)off1, (long long)len,
+                       fpath2.path, inoinfo2, (long long)off2, (long long)len);
+
+               if (ret < 0)
+                       printf(" error %d", e);
+               printf("\n");
+       }
+
+out_fd2:
+       close(fd2);
+out_fd1:
+       close(fd1);
+out_fpath2:
+       free_pathname(&fpath2);
+out_fpath1:
+       free_pathname(&fpath1);
+#endif
+}
+
 /* reflink some arbitrary range of f1 to f2. */
 void
 clonerange_f(
@@ -3538,7 +3826,7 @@ do_fallocate(opnum_t opno, long r, int mode)
        mode |= FALLOC_FL_KEEP_SIZE & random();
        e = fallocate(fd, mode, (loff_t)off, (loff_t)len) < 0 ? errno : 0;
        if (v)
-               printf("%d/%lld: fallocate(%s) %s %st %lld %lld %d\n",
+               printf("%d/%lld: fallocate(%s) %s%s [%lld,%lld] %d\n",
                       procid, opno, translate_falloc_flags(mode),
                       f.path, st, (long long)off, (long long)len, e);
        free_pathname(&f);
@@ -3660,7 +3948,7 @@ fiemap_f(opnum_t opno, long r)
 
        e = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
        if (v)
-               printf("%d/%lld: ioctl(FIEMAP) %s%s %lld %lld (%s) %d\n",
+               printf("%d/%lld: ioctl(FIEMAP) %s%s [%lld,%lld,%s] %d\n",
                       procid, opno, f.path, st, (long long)fiemap->fm_start,
                       (long long) fiemap->fm_length,
                       translate_fiemap_flags(fiemap->fm_flags), e);
@@ -4594,7 +4882,7 @@ resvsp_f(opnum_t opno, long r)
        fl.l_len = (off64_t)(random() % (1024 * 1024));
        e = xfsctl(f.path, fd, XFS_IOC_RESVSP64, &fl) < 0 ? errno : 0;
        if (v)
-               printf("%d/%lld: xfsctl(XFS_IOC_RESVSP64) %s%s %lld %lld %d\n",
+               printf("%d/%lld: xfsctl(XFS_IOC_RESVSP64) %s%s [%lld,%lld] %d\n",
                       procid, opno, f.path, st,
                        (long long)off, (long long)fl.l_len, e);
        free_pathname(&f);
@@ -5062,7 +5350,7 @@ unresvsp_f(opnum_t opno, long r)
        fl.l_len = (off64_t)(random() % (1 << 20));
        e = xfsctl(f.path, fd, XFS_IOC_UNRESVSP64, &fl) < 0 ? errno : 0;
        if (v)
-               printf("%d/%lld: xfsctl(XFS_IOC_UNRESVSP64) %s%s %lld %lld %d\n",
+               printf("%d/%lld: xfsctl(XFS_IOC_UNRESVSP64) %s%s [%lld,%lld] %d\n",
                       procid, opno, f.path, st,
                        (long long)off, (long long)fl.l_len, e);
        free_pathname(&f);
index 12c2cc33bfe3414d33ebc6ada8fc204b73207109..777ba0de5d3ec9aef828acf5d80a5988b41c818b 100644 (file)
--- a/ltp/fsx.c
+++ b/ltp/fsx.c
@@ -111,6 +111,7 @@ enum {
        OP_CLONE_RANGE,
        OP_DEDUPE_RANGE,
        OP_COPY_RANGE,
+       OP_EXCHANGE_RANGE,
        OP_MAX_FULL,
 
        /* integrity operations */
@@ -175,6 +176,7 @@ int check_file = 0;                 /* -X flag enables */
 int    clone_range_calls = 1;          /* -J flag disables */
 int    dedupe_range_calls = 1;         /* -B flag disables */
 int    copy_range_calls = 1;           /* -E flag disables */
+int    xchg_range_calls = 1;           /* -0 flag disables */
 int    integrity = 0;                  /* -i flag */
 int    fsxgoodfd = 0;
 int    o_direct;                       /* -Z */
@@ -191,6 +193,8 @@ int fsx_rw(int rw, int fd, char *buf, unsigned len, unsigned offset);
 #define fsxread(a,b,c,d)       fsx_rw(READ, a,b,c,d)
 #define fsxwrite(a,b,c,d)      fsx_rw(WRITE, a,b,c,d)
 
+struct timespec deadline;
+
 const char *replayops = NULL;
 const char *recordops = NULL;
 FILE * fsxlogf = NULL;
@@ -268,6 +272,7 @@ static const char *op_names[] = {
        [OP_DEDUPE_RANGE] = "dedupe_range",
        [OP_COPY_RANGE] = "copy_range",
        [OP_FSYNC] = "fsync",
+       [OP_EXCHANGE_RANGE] = "xchg_range",
 };
 
 static const char *op_name(int operation)
@@ -452,6 +457,20 @@ logdump(void)
                        if (overlap)
                                prt("\t******IIII");
                        break;
+               case OP_EXCHANGE_RANGE:
+                       prt("XCHG 0x%x thru 0x%x\t(0x%x bytes) to 0x%x thru 0x%x",
+                           lp->args[0], lp->args[0] + lp->args[1] - 1,
+                           lp->args[1],
+                           lp->args[2], lp->args[2] + lp->args[1] - 1);
+                       overlap2 = badoff >= lp->args[2] &&
+                                 badoff < lp->args[2] + lp->args[1];
+                       if (overlap && overlap2)
+                               prt("\tXXXX**XXXX");
+                       else if (overlap)
+                               prt("\tXXXX******");
+                       else if (overlap2)
+                               prt("\t******XXXX");
+                       break;
                case OP_CLONE_RANGE:
                        prt("CLONE 0x%x thru 0x%x\t(0x%x bytes) to 0x%x thru 0x%x",
                            lp->args[0], lp->args[0] + lp->args[1] - 1,
@@ -650,17 +669,18 @@ check_buffers(char *buf, unsigned offset, unsigned size)
        if (memcmp(good_buf + offset, buf, size) != 0) {
                prt("READ BAD DATA: offset = 0x%x, size = 0x%x, fname = %s\n",
                    offset, size, fname);
-               prt("OFFSET\tGOOD\tBAD\tRANGE\n");
+               prt("%-10s  %-6s  %-6s  %s\n", "OFFSET", "GOOD", "BAD", "RANGE");
                while (size > 0) {
                        c = good_buf[offset];
                        t = buf[i];
                        if (c != t) {
                                if (n < 16) {
                                        bad = short_at(&buf[i]);
-                                       prt("0x%05x\t0x%04x\t0x%04x", offset,
-                                           short_at(&good_buf[offset]), bad);
+                                       prt("0x%-8x  0x%04x  0x%04x  0x%x\n",
+                                           offset,
+                                           short_at(&good_buf[offset]), bad,
+                                           n);
                                        op = buf[offset & 1 ? i+1 : i];
-                                       prt("\t0x%05x\n", n);
                                        if (op)
                                                prt("operation# (mod 256) for "
                                                  "the bad data may be %u\n",
@@ -1369,6 +1389,116 @@ do_insert_range(unsigned offset, unsigned length)
 }
 #endif
 
+#ifdef XFS_IOC_EXCHANGE_RANGE
+static __u64 swap_flags = 0;
+
+int
+test_xchg_range(void)
+{
+       struct xfs_exch_range   fsr = {
+               .file1_fd = fd,
+               .flags = XFS_EXCH_RANGE_DRY_RUN | swap_flags,
+       };
+       int ret, e;
+
+retry:
+       ret = ioctl(fd, XFS_IOC_EXCHANGE_RANGE, &fsr);
+       e = ret < 0 ? errno : 0;
+       if (e == EOPNOTSUPP && !(swap_flags & XFS_EXCH_RANGE_NONATOMIC)) {
+               /*
+                * If the call fails with atomic mode, try again with non
+                * atomic mode.
+                */
+               swap_flags = XFS_EXCH_RANGE_NONATOMIC;
+               fsr.flags |= swap_flags;
+               goto retry;
+       }
+       if (e == EOPNOTSUPP || errno == ENOTTY) {
+               if (!quiet)
+                       fprintf(stderr,
+                               "main: filesystem does not support "
+                               "exchange range, disabling!\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+void
+do_xchg_range(unsigned offset, unsigned length, unsigned dest)
+{
+       struct xfs_exch_range   fsr = {
+               .file1_fd = fd,
+               .file1_offset = offset,
+               .file2_offset = dest,
+               .length = length,
+               .flags = swap_flags,
+       };
+       void *p;
+
+       if (length == 0) {
+               if (!quiet && testcalls > simulatedopcount)
+                       prt("skipping zero length exchange range\n");
+               log5(OP_EXCHANGE_RANGE, offset, length, dest, FL_SKIPPED);
+               return;
+       }
+
+       if ((loff_t)offset >= file_size || (loff_t)dest >= file_size) {
+               if (!quiet && testcalls > simulatedopcount)
+                       prt("skipping exchange range behind EOF\n");
+               log5(OP_EXCHANGE_RANGE, offset, length, dest, FL_SKIPPED);
+               return;
+       }
+
+       p = malloc(length);
+       if (!p) {
+               if (!quiet && testcalls > simulatedopcount)
+                       prt("skipping exchange range due to ENOMEM\n");
+               log5(OP_EXCHANGE_RANGE, offset, length, dest, FL_SKIPPED);
+               return;
+       }
+
+       log5(OP_EXCHANGE_RANGE, offset, length, dest, FL_NONE);
+
+       if (testcalls <= simulatedopcount)
+               goto out_free;
+
+       if ((progressinterval && testcalls % progressinterval == 0) ||
+           (debug && (monitorstart == -1 || monitorend == -1 ||
+                      dest <= monitorstart || dest + length <= monitorend))) {
+               prt("%lu swap\tfrom 0x%x to 0x%x, (0x%x bytes) at 0x%x\n",
+                       testcalls, offset, offset+length, length, dest);
+       }
+
+       if (ioctl(fd, XFS_IOC_EXCHANGE_RANGE, &fsr) == -1) {
+               prt("exchange range: 0x%x to 0x%x at 0x%x\n", offset,
+                               offset + length, dest);
+               prterr("do_xchg_range: XFS_IOC_EXCHANGE_RANGE");
+               report_failure(161);
+               goto out_free;
+       }
+
+       memcpy(p, good_buf + offset, length);
+       memcpy(good_buf + offset, good_buf + dest, length);
+       memcpy(good_buf + dest, p, length);
+out_free:
+       free(p);
+}
+
+#else
+int
+test_xchg_range(void)
+{
+       return 0;
+}
+
+void
+do_xchg_range(unsigned offset, unsigned length, unsigned dest)
+{
+       return;
+}
+#endif
+
 #ifdef FICLONERANGE
 int
 test_clone_range(void)
@@ -1856,6 +1986,7 @@ static int
 op_args_count(int operation)
 {
        switch (operation) {
+       case OP_EXCHANGE_RANGE:
        case OP_CLONE_RANGE:
        case OP_DEDUPE_RANGE:
        case OP_COPY_RANGE:
@@ -2053,6 +2184,9 @@ test(void)
        case OP_COPY_RANGE:
                generate_dest_range(true, maxfilelen, &offset, &size, &offset2);
                break;
+       case OP_EXCHANGE_RANGE:
+               generate_dest_range(false, file_size, &offset, &size, &offset2);
+               break;
        }
 
 have_op:
@@ -2096,6 +2230,12 @@ have_op:
                        goto out;
                }
                break;
+       case OP_EXCHANGE_RANGE:
+               if (!xchg_range_calls) {
+                       log5(op, offset, size, offset2, FL_SKIPPED);
+                       goto out;
+               }
+               break;
        case OP_CLONE_RANGE:
                if (!clone_range_calls) {
                        log5(op, offset, size, offset2, FL_SKIPPED);
@@ -2180,6 +2320,18 @@ have_op:
 
                do_insert_range(offset, size);
                break;
+       case OP_EXCHANGE_RANGE:
+               if (size == 0) {
+                       log5(OP_EXCHANGE_RANGE, offset, size, offset2, FL_SKIPPED);
+                       goto out;
+               }
+               if (offset2 + size > maxfilelen) {
+                       log5(OP_EXCHANGE_RANGE, offset, size, offset2, FL_SKIPPED);
+                       goto out;
+               }
+
+               do_xchg_range(offset, size, offset2);
+               break;
        case OP_CLONE_RANGE:
                if (size == 0) {
                        log5(OP_CLONE_RANGE, offset, size, offset2, FL_SKIPPED);
@@ -2241,11 +2393,17 @@ void
 usage(void)
 {
        fprintf(stdout, "usage: %s",
-               "fsx [-dknqxBEFJLOWZ][-A|-U] [-b opnum] [-c Prob] [-g filldata] [-i logdev] [-j logid] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] fname\n\
+               "fsx [-dfknqxyzBEFHIJKLORWXZ0]\n\
+          [-b opnum] [-c Prob] [-g filldata] [-i logdev] [-j logid]\n\
+          [-l flen] [-m start:end] [-o oplen] [-p progressinterval]\n\
+          [-r readbdy] [-s style] [-t truncbdy] [-w writebdy]\n\
+          [-A|-U] [-D startingop] [-N numops] [-P dirpath] [-S seed]\n\
+          [--replay-ops=opsfile] [--record-ops[=opsfile]] [--duration=seconds]\n\
+          ... fname\n\
        -b opnum: beginning operation number (default 1)\n\
        -c P: 1 in P chance of file close+open at each op (default infinity)\n\
        -d: debug output for all operations\n\
-       -f flush and invalidate cache after I/O\n\
+       -f: flush and invalidate cache after I/O\n\
        -g X: write character X instead of random generated data\n\
        -i logdev: do integrity testing, logdev is the dm log writes device\n\
        -j logid: prefix debug log messsages with this id\n\
@@ -2260,15 +2418,15 @@ usage(void)
        -s style: 1 gives smaller truncates (default 0)\n\
        -t truncbdy: 4096 would make truncates page aligned (default 1)\n\
        -w writebdy: 4096 would make writes page aligned (default 1)\n\
-       -x: preallocate file space before starting, XFS only (default 0)\n\
-       -y synchronize changes to a file\n"
+       -x: preallocate file space before starting, XFS only\n\
+       -y: synchronize changes to a file\n"
 
 #ifdef AIO
 "      -A: Use the AIO system calls, -A excludes -U\n"
 #endif
 #ifdef URING
 "      -U: Use the IO_URING system calls, -U excludes -A\n"
- #endif
+#endif
 "      -D startingop: debug output starting at specified operation\n"
 #ifdef HAVE_LINUX_FALLOC_H
 "      -F: Do not use fallocate (preallocation) calls\n"
@@ -2294,17 +2452,22 @@ usage(void)
 #ifdef HAVE_COPY_FILE_RANGE
 "      -E: Do not use copy range calls\n"
 #endif
-"      -L: fsxLite - no file creations & no file size changes\n\
+#ifdef XFS_IOC_EXCHANGE_RANGE
+"      -0: Do not use exchange range calls\n"
+#endif
+"      -K: Do not use keep size\n\
+       -L: fsxLite - no file creations & no file size changes\n\
        -N numops: total # operations to do (default infinity)\n\
        -O: use oplen (see -o flag) for every op (default random)\n\
-       -P: save .fsxlog .fsxops and .fsxgood files in dirpath (default ./)\n\
+       -P dirpath: save .fsxlog .fsxops and .fsxgood files in dirpath (default ./)\n\
+       -R: read() system calls only (mapped reads disabled)\n\
        -S seed: for random # generator (default 1) 0 gets timestamp\n\
        -W: mapped write operations DISabled\n\
-       -X: Read file and compare to good buffer after every operation.\n\
-        -R: read() system calls only (mapped reads disabled)\n\
-        -Z: O_DIRECT (use -R, -W, -r and -w too)\n\
-       --replay-ops opsfile: replay ops from recorded .fsxops file\n\
+       -X: Read file and compare to good buffer after every operation\n\
+       -Z: O_DIRECT (use -R, -W, -r and -w too)\n\
+       --replay-ops=opsfile: replay ops from recorded .fsxops file\n\
        --record-ops[=opsfile]: dump ops file also on success. optionally specify ops file name\n\
+       --duration=seconds: ignore any -N setting and run for this many seconds\n\
        fname: this filename is REQUIRED (no default)\n");
        exit(90);
 }
@@ -2429,6 +2592,7 @@ out_error:
        return -1;
 }
 #else
+int
 aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
 {
        fprintf(stderr, "io_rw: need AIO support!\n");
@@ -2587,9 +2751,33 @@ __test_fallocate(int mode, const char *mode_str)
 #endif
 }
 
+bool
+keep_running(void)
+{
+       int ret;
+
+       if (deadline.tv_nsec) {
+               struct timespec now;
+
+               ret = clock_gettime(CLOCK_MONOTONIC, &now);
+               if (ret) {
+                       perror("CLOCK_MONOTONIC");
+                       return false;
+               }
+
+               return now.tv_sec <= deadline.tv_sec;
+       }
+
+       if (numops == -1)
+               return true;
+
+       return numops-- != 0;
+}
+
 static struct option longopts[] = {
        {"replay-ops", required_argument, 0, 256},
        {"record-ops", optional_argument, 0, 255},
+       {"duration", optional_argument, 0, 254},
        { }
 };
 
@@ -2601,6 +2789,7 @@ main(int argc, char **argv)
        char logfile[PATH_MAX];
        struct stat statbuf;
        int o_flags = O_RDWR|O_CREAT|O_TRUNC;
+       long long duration;
 
        logfile[0] = 0;
        dname[0] = 0;
@@ -2608,12 +2797,11 @@ main(int argc, char **argv)
        page_size = getpagesize();
        page_mask = page_size - 1;
        mmap_mask = page_mask;
-       
 
        setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
 
        while ((ch = getopt_long(argc, argv,
-                                "b:c:dfg:i:j:kl:m:no:p:qr:s:t:w:xyABD:EFJKHzCILN:OP:RS:UWXZ",
+                                "0b:c:dfg:i:j:kl:m:no:p:qr:s:t:w:xyABD:EFJKHzCILN:OP:RS:UWXZ",
                                 longopts, NULL)) != EOF)
                switch (ch) {
                case 'b':
@@ -2747,6 +2935,9 @@ main(int argc, char **argv)
                case 'I':
                        insert_range_calls = 0;
                        break;
+               case '0':
+                       xchg_range_calls = 0;
+                       break;
                case 'J':
                        clone_range_calls = 0;
                        break;
@@ -2796,6 +2987,26 @@ main(int argc, char **argv)
                        o_direct = O_DIRECT;
                        o_flags |= O_DIRECT;
                        break;
+               case 254:  /* --duration */
+                       if (!optarg) {
+                               fprintf(stderr, "Specify time with --duration=\n");
+                               exit(87);
+                       }
+                       duration = strtoll(optarg, NULL, 0);
+                       if (duration < 1) {
+                               fprintf(stderr, "%lld: invalid duration\n", duration);
+                               exit(88);
+                       }
+
+                       i = clock_gettime(CLOCK_MONOTONIC, &deadline);
+                       if (i) {
+                               perror("CLOCK_MONOTONIC");
+                               exit(89);
+                       }
+
+                       deadline.tv_sec += duration;
+                       deadline.tv_nsec = 1;
+                       break;
                case 255:  /* --record-ops */
                        if (optarg)
                                snprintf(opsfile, sizeof(opsfile), "%s", optarg);
@@ -2988,8 +3199,10 @@ main(int argc, char **argv)
                dedupe_range_calls = test_dedupe_range();
        if (copy_range_calls)
                copy_range_calls = test_copy_range();
+       if (xchg_range_calls)
+               xchg_range_calls = test_xchg_range();
 
-       while (numops == -1 || numops--)
+       while (keep_running())
                if (!test())
                        break;
 
diff --git a/m4/manual_format.m4 b/m4/manual_format.m4
deleted file mode 100644 (file)
index 50c6a91..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-# 
-# Find format of installed man pages.
-# Always gzipped on Debian, but not Redhat pre-7.0.
-# We don't deal with bzip2'd man pages, which Mandrake uses,
-# someone will send us a patch sometime hopefully. :-)
-# 
-AC_DEFUN([AC_MANUAL_FORMAT],
-  [ have_zipped_manpages=false
-    for d in ${prefix}/share/man ${prefix}/man ; do
-        if test -f $d/man1/man.1.gz
-        then
-            have_zipped_manpages=true
-            break
-        fi
-    done
-    AC_SUBST(have_zipped_manpages)
-  ])
index 5c76c0f73e2abaa49f114ac25c732bfcea8454c3..a0d50f4d9b68e47e8acb6adedf6159fe177e6d9c 100644 (file)
-# 
-# Check if we have a working fadvise system call
-# 
-AC_DEFUN([AC_HAVE_FADVISE],
-  [ AC_MSG_CHECKING([for fadvise ])
-    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#
+#
+# Check if we have a copy_file_range system call (Linux)
+#
+AC_DEFUN([AC_HAVE_COPY_FILE_RANGE],
+  [ AC_MSG_CHECKING([for copy_file_range])
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([[
 #define _GNU_SOURCE
-#define _FILE_OFFSET_BITS 64
-#include <fcntl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
     ]], [[
-       posix_fadvise(0, 1, 0, POSIX_FADV_NORMAL);
-    ]])],[have_fadvise=yes
-       AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
-    AC_SUBST(have_fadvise)
+         syscall(__NR_copy_file_range, 0, 0, 0, 0, 0, 0);
+    ]])],[have_copy_file_range=yes
+       AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
+    AC_SUBST(have_copy_file_range)
   ])
 
-# 
-# Check if we have a working madvise system call
-# 
-AC_DEFUN([AC_HAVE_MADVISE],
-  [ AC_MSG_CHECKING([for madvise ])
-    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+# Check if we have SEEK_DATA
+AC_DEFUN([AC_HAVE_SEEK_DATA],
+  [ AC_MSG_CHECKING([for SEEK_DATA])
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([[
 #define _GNU_SOURCE
-#define _FILE_OFFSET_BITS 64
-#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
     ]], [[
-       posix_madvise(0, 0, MADV_NORMAL);
-    ]])],[have_madvise=yes
-       AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
-    AC_SUBST(have_madvise)
+         lseek(-1, 0, SEEK_DATA);
+    ]])],[have_seek_data=yes
+       AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
+    AC_SUBST(have_seek_data)
   ])
 
-# 
-# Check if we have a working mincore system call
-# 
-AC_DEFUN([AC_HAVE_MINCORE],
-  [ AC_MSG_CHECKING([for mincore ])
-    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+# Check if we have nftw
+AC_DEFUN([AC_HAVE_NFTW],
+  [ AC_MSG_CHECKING([for nftw])
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([[
 #define _GNU_SOURCE
-#define _FILE_OFFSET_BITS 64
-#include <sys/mman.h>
+#include <stddef.h>
+#include <ftw.h>
     ]], [[
-       mincore(0, 0, 0);
-    ]])],[have_mincore=yes
-       AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
-    AC_SUBST(have_mincore)
+         nftw("/", (int (*)(const char *, const struct stat *, int, struct FTW *))1, 0, FTW_ACTIONRETVAL);
+    ]])],[have_nftw=yes
+       AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
+    AC_SUBST(have_nftw)
   ])
 
-# 
-# Check if we have a working sendfile system call
-# 
-AC_DEFUN([AC_HAVE_SENDFILE],
-  [ AC_MSG_CHECKING([for sendfile ])
-    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+# Check if we have RLIMIT_NOFILE
+AC_DEFUN([AC_HAVE_RLIMIT_NOFILE],
+  [ AC_MSG_CHECKING([for RLIMIT_NOFILE])
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([[
 #define _GNU_SOURCE
-#define _FILE_OFFSET_BITS 64
-#include <sys/sendfile.h>
-    ]], [[
-         sendfile(0, 0, 0, 0);
-    ]])],[have_sendfile=yes
-       AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
-    AC_SUBST(have_sendfile)
-  ])
-
-#
-# Check if we have a getmntent libc call (Linux)
-#
-AC_DEFUN([AC_HAVE_GETMNTENT],
-  [ AC_MSG_CHECKING([for getmntent ])
-    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-#include <stdio.h>
-#include <mntent.h>
+#include <sys/time.h>
+#include <sys/resource.h>
     ]], [[
-         getmntent(0);
-    ]])],[have_getmntent=yes
-       AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
-    AC_SUBST(have_getmntent)
-  ])
+         struct rlimit rlimit;
 
-#
-# Check if we have a getmntinfo libc call (FreeBSD, Mac OS X)
-#
-AC_DEFUN([AC_HAVE_GETMNTINFO],
-  [ AC_MSG_CHECKING([for getmntinfo ])
-    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-#include <sys/param.h>
-#include <sys/ucred.h>
-#include <sys/mount.h>
-    ]], [[
-         getmntinfo(0, 0);
-    ]])],[have_getmntinfo=yes
+         rlimit.rlim_cur = 0;
+         getrlimit(RLIMIT_NOFILE, &rlimit);
+    ]])],[have_rlimit_nofile=yes
        AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
-    AC_SUBST(have_getmntinfo)
+    AC_SUBST(have_rlimit_nofile)
   ])
 
-#
-#
-# Check if we have a copy_file_range system call (Linux)
-#
-AC_DEFUN([AC_HAVE_COPY_FILE_RANGE],
-  [ AC_MSG_CHECKING([for copy_file_range])
+# Check if we have FICLONE
+AC_DEFUN([AC_HAVE_FICLONE],
+  [ AC_MSG_CHECKING([for FICLONE])
     AC_LINK_IFELSE([AC_LANG_PROGRAM([[
-#define _GNU_SOURCE
-#include <sys/syscall.h>
-#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
     ]], [[
-         syscall(__NR_copy_file_range, 0, 0, 0, 0, 0, 0);
-    ]])],[have_copy_file_range=yes
+        ioctl(-1, FICLONE, -1);
+    ]])],[have_ficlone=yes
        AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
-    AC_SUBST(have_copy_file_range)
+    AC_SUBST(have_ficlone)
   ])
-
diff --git a/m4/package_ncurses.m4 b/m4/package_ncurses.m4
deleted file mode 100644 (file)
index b220dd6..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-AC_DEFUN([AC_PACKAGE_NEED_NCURSES_H],
-  [ AC_CHECK_HEADERS([ncurses.h])
-    if test "$ac_cv_header_ncurses_h" != yes; then
-        echo
-        echo 'FATAL ERROR: could not find a valid ncurses header.'
-        echo 'Install the ncurses development package.'
-        exit 1
-    fi
-  ])
-
-AC_DEFUN([AC_PACKAGE_WANT_WORKING_LIBNCURSES],
-  [ AC_CHECK_LIB(ncurses, initscr,, [
-        echo
-        echo 'FATAL ERROR: could not find a valid ncurses library.'
-        echo 'Install the ncurses library package.'
-        exit 1
-    ])
-    AC_MSG_CHECKING([for bad glibc/ncurses header interaction])
-    libcurses="-lncurses"
-    LIBS="$LIBS $libcurses"
-    CFLAGS="$CFLAGS -D_GNU_SOURCE"
-    AC_LINK_IFELSE([AC_LANG_PROGRAM([[
-#include <ncurses.h>
-#include <signal.h>]], [[wgetch(stdscr);]])],[enable_curses=yes; AC_MSG_RESULT([ok])],[enable_curses=no; libcurses=""; AC_MSG_RESULT([disabling curses])])
-    AC_SUBST(enable_curses)
-    AC_SUBST(libcurses)
-  ])
diff --git a/m4/package_pthread.m4 b/m4/package_pthread.m4
deleted file mode 100644 (file)
index be21d29..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-AC_DEFUN([AC_PACKAGE_NEED_PTHREAD_H],
-  [ AC_CHECK_HEADERS(pthread.h)
-    if test $ac_cv_header_pthread_h = no; then
-       AC_CHECK_HEADERS(pthread.h,, [
-       echo
-       echo 'FATAL ERROR: could not find a valid pthread header.'
-       exit 1])
-    fi
-  ])
-
-AC_DEFUN([AC_PACKAGE_NEED_PTHREADMUTEXINIT],
-  [ AC_CHECK_LIB(pthread, pthread_mutex_init,, [
-       echo
-       echo 'FATAL ERROR: could not find a valid pthread library.'
-       exit 1
-    ])
-    libpthread=-lpthread
-    AC_SUBST(libpthread)
-  ])
diff --git a/m4/package_types.m4 b/m4/package_types.m4
deleted file mode 100644 (file)
index 91db64e..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-# 
-# Check if we have a type for the pointer's size integer (__psint_t)
-# 
-AC_DEFUN([AC_TYPE_PSINT],
-  [ AC_MSG_CHECKING([for __psint_t ])
-    AC_TRY_COMPILE([
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stddef.h>
-    ], [
-         __psint_t  psint;
-    ], AC_DEFINE(HAVE___PSINT_T) AC_MSG_RESULT(yes) , AC_MSG_RESULT(no))
-  ])
-
-# 
-# Check if we have a type for the pointer's size unsigned (__psunsigned_t)
-# 
-AC_DEFUN([AC_TYPE_PSUNSIGNED],
-  [ AC_MSG_CHECKING([for __psunsigned_t ])
-    AC_TRY_COMPILE([
-#include <sys/types.h>
-#include <stdlib.h>
-#include <stddef.h>
-    ], [
-        __psunsigned_t  psuint;
-    ], AC_DEFINE(HAVE___PSUNSIGNED_T) AC_MSG_RESULT(yes) , AC_MSG_RESULT(no))
-  ])
-
-# 
-# Check type sizes
-# 
-AC_DEFUN([AC_SIZEOF_POINTERS_AND_LONG],
-  [ if test "$cross_compiling" = yes -a -z "$ac_cv_sizeof_long"; then
-      AC_MSG_WARN([Cross compiling; assuming 32bit long and 32bit pointers])
-    fi
-    AC_CHECK_SIZEOF(long, 4)
-    AC_CHECK_SIZEOF(char *, 4)
-    if test $ac_cv_sizeof_long -eq 4 -o $ac_cv_sizeof_long -eq 0; then
-      AC_DEFINE(HAVE_32BIT_LONG)
-    fi
-    if test $ac_cv_sizeof_long -eq 8; then
-      AC_DEFINE(HAVE_64BIT_LONG)
-    fi
-    if test $ac_cv_sizeof_char_p -eq 4 -o $ac_cv_sizeof_char_p -eq 0; then
-      AC_DEFINE(HAVE_32BIT_PTR)
-    fi
-    if test $ac_cv_sizeof_char_p -eq 8; then
-      AC_DEFINE(HAVE_64BIT_PTR)
-    fi
-  ])
index 0746cd1dc5d203ceda00f7007ed770b51e3c43c4..2f1dbc6951864dc1d04dc9597c65396df59f0b47 100644 (file)
@@ -50,34 +50,6 @@ AC_DEFUN([AC_PACKAGE_NEED_XFS_HANDLE_H],
     fi
   ])
 
-AC_DEFUN([AC_PACKAGE_NEED_LIBXFSINIT_LIBXFS],
-  [ AC_CHECK_LIB(xfs, libxfs_init,, [
-        echo
-        echo 'FATAL ERROR: could not find a valid XFS base library.'
-        echo 'Install or upgrade the XFS library package.'
-        echo 'Alternatively, run "make install-dev" from the xfsprogs source.'
-        exit 1
-    ])
-    libxfs="-lxfs"
-    test -f ${libexecdir}${libdirsuffix}/libxfs.la && \
-       libxfs="${libexecdir}${libdirsuffix}/libxfs.la"
-    AC_SUBST(libxfs)
-  ])
-
-AC_DEFUN([AC_PACKAGE_NEED_OPEN_BY_FSHANDLE],
-  [ AC_CHECK_LIB(handle, open_by_fshandle,, [
-        echo
-        echo 'FATAL ERROR: could not find a current XFS handle library.'
-        echo 'Install or upgrade the XFS library package.'
-        echo 'Alternatively, run "make install-dev" from the xfsprogs source.'
-        exit 1
-    ])
-    libhdl="-lhandle"
-    test -f ${libexecdir}${libdirsuffix}/libhandle.la && \
-       libhdl="${libexecdir}${libdirsuffix}/libhandle.la"
-    AC_SUBST(libhdl)
-  ])
-
 AC_DEFUN([AC_PACKAGE_NEED_ATTRLIST_LIBHANDLE],
   [ AC_CHECK_LIB(handle, attr_list_by_handle,, [
         echo
@@ -104,3 +76,32 @@ AC_DEFUN([AC_PACKAGE_NEED_XFSCTL_MACRO],
         exit 1
       ])
   ])
+
+# Check if we have BMV_OF_SHARED from the GETBMAPX ioctl
+AC_DEFUN([AC_HAVE_BMV_OF_SHARED],
+  [ AC_MSG_CHECKING([for BMV_OF_SHARED])
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#define _GNU_SOURCE
+#include <xfs/xfs.h>
+    ]], [[
+         struct getbmapx obj;
+         ioctl(-1, XFS_IOC_GETBMAPX, &obj);
+         obj.bmv_oflags |= BMV_OF_SHARED;
+    ]])],[have_bmv_of_shared=yes
+       AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
+    AC_SUBST(have_bmv_of_shared)
+  ])
+
+# Check if we have XFS_IOC_EXCHANGE_RANGE
+AC_DEFUN([AC_HAVE_XFS_IOC_EXCHANGE_RANGE],
+  [ AC_MSG_CHECKING([for XFS_IOC_EXCHANGE_RANGE])
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#define _GNU_SOURCE
+#include <xfs/xfs.h>
+    ]], [[
+         struct xfs_exch_range obj;
+         ioctl(-1, XFS_IOC_EXCHANGE_RANGE, &obj);
+    ]])],[have_xfs_ioc_exchange_range=yes
+       AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])
+    AC_SUBST(have_xfs_ioc_exchange_range)
+  ])
diff --git a/m4/visibility_hidden.m4 b/m4/visibility_hidden.m4
deleted file mode 100644 (file)
index bfd74e4..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-AC_DEFUN([AC_FUNC_GCC_VISIBILITY],
-  [AC_CACHE_CHECK(whether __attribute__((visibility())) is supported,
-                 libc_cv_visibility_attribute,
-                 [cat > conftest.c <<EOF
-                  int foo __attribute__ ((visibility ("hidden"))) = 1;
-                  int bar __attribute__ ((visibility ("protected"))) = 1;
-EOF
-                 libc_cv_visibility_attribute=no
-                 if ${CC-cc} -Werror -S conftest.c -o conftest.s \
-                       >/dev/null 2>&1; then
-                   if grep '\.hidden.*foo' conftest.s >/dev/null; then
-                     if grep '\.protected.*bar' conftest.s >/dev/null; then
-                       libc_cv_visibility_attribute=yes
-                     fi
-                   fi
-                 fi
-                 rm -f conftest.[cs]
-                 ])
-   if test $libc_cv_visibility_attribute = yes; then
-     AC_DEFINE(HAVE_VISIBILITY_ATTRIBUTE, [], [GCC supports visibility attributes])
-   fi
-  ])
index 7eeb08ef8ad24883afe0b9ce1cc219f64802f957..ab98a06f496d344a06fb81fc8fb7273395f2a037 100644 (file)
@@ -18,7 +18,9 @@ TARGETS = dirstress fill fill2 getpagesize holes lstat64 \
        t_ext4_dax_journal_corruption t_ext4_dax_inline_corruption \
        t_ofd_locks t_mmap_collision mmap-write-concurrent \
        t_get_file_time t_create_short_dirs t_create_long_dirs t_enospc \
-       t_mmap_writev_overlap checkpoint_journal mmap-rw-fault allocstale
+       t_mmap_writev_overlap checkpoint_journal mmap-rw-fault allocstale \
+       t_mmap_cow_memory_failure fake-dump-rootino dio-buf-fault rewinddir-test \
+       readdir-while-renames
 
 LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
        preallo_rw_pattern_writer ftrunc trunc fs_perms testx looptest \
@@ -31,14 +33,16 @@ LINUX_TARGETS = xfsctl bstat t_mtab getdevicesize preallo_rw_pattern_reader \
        dio-invalidate-cache stat_test t_encrypted_d_revalidate \
        attr_replace_test swapon mkswap t_attr_corruption t_open_tmpfiles \
        fscrypt-crypt-util bulkstat_null_ocount splice-test chprojid_fail \
-       detached_mounts_propagation ext4_resize t_readdir_3 splice2pipe
+       detached_mounts_propagation ext4_resize t_readdir_3 splice2pipe \
+       uuid_ioctl t_snapshot_deleted_subvolume fiemap-fault
 
 EXTRA_EXECS = dmerror fill2attr fill2fs fill2fs_check scaleread.sh \
-             btrfs_crc32c_forged_name.py
+             btrfs_crc32c_forged_name.py popdir.pl popattr.py \
+             soak_duration.awk
 
 SUBDIRS = log-writes perf
 
-LLDLIBS = $(LIBHANDLE) $(LIBACL) -lpthread -lrt
+LLDLIBS = $(LIBHANDLE) $(LIBACL) -lpthread -lrt -luuid
 
 ifeq ($(HAVE_XLOG_ASSIGN_LSN), true)
 LINUX_TARGETS += loggen
@@ -81,6 +85,24 @@ ifeq ($(HAVE_LIBCAP), true)
 LLDLIBS += -lcap
 endif
 
+ifeq ($(HAVE_SEEK_DATA), yes)
+ ifeq ($(HAVE_NFTW), yes)
+  ifeq ($(HAVE_BMV_OF_SHARED), yes)
+   ifeq ($(HAVE_RLIMIT_NOFILE), yes)
+     TARGETS += xfsfind
+   endif
+  endif
+ endif
+endif
+
+ifeq ($(HAVE_FICLONE),yes)
+     TARGETS += t_reflink_read_race
+endif
+
+ifeq ($(HAVE_XFS_IOC_EXCHANGE_RANGE),yes)
+LCFLAGS += -DHAVE_XFS_IOC_EXCHANGE_RANGE
+endif
+
 CFILES = $(TARGETS:=.c)
 LDIRT = $(TARGETS) fssum
 
index 41037ee4b7ad792f3f3ab6b0919a756999fa1cea..89df76586f27faab7bd8ceac40f03ce066cae080 100644 (file)
@@ -17,8 +17,6 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
-#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
-
 int main(int argc, char *argv[])
 {
        struct sockaddr_un sun;
index 911f27230b95c4a70acf6d11ebe17691fa9a229b..d9f8982f007c7ec2896577b03696c339d609aede 100644 (file)
@@ -191,7 +191,7 @@ int main(int argc, char *argv[])
                }
 
                for (j = 0; j < rdata.read_sz; j++) {
-                       if (rdata.buf[j] != 'a') {
+                       if (rdata.buf[j] != 'a' && rdata.buf[j] != 0) {
                                fail("encounter an error: "
                                        "block %d offset %d, content %x\n",
                                        i, j, rbuf[j]);
index 302b8fe4a09605cc7c80d8538352a3bdbaa8fd12..513a338bee35629f3a06a93fd83a32332efd10d2 100644 (file)
 
 void usage(char *progname)
 {
-       fprintf(stderr, "usage: %s [-t truncsize ] <-a size=N,off=M [-a ...]>  filename\n"
+       fprintf(stderr, "usage: %s [-t truncsize ] <-a size=N,off=M [-a ...]>  [-S] [-N] filename\n"
                "\t-t truncsize: truncate the file to a special size before AIO wirte\n"
                "\t-a: specify once AIO write size and startoff, this option can be specified many times, but less than 128\n"
                "\t\tsize=N: AIO write size\n"
                "\t\toff=M:  AIO write startoff\n"
-               "e.g: %s -t 4608 -a size=4096,off=512 -a size=4096,off=4608 filename\n",
-               progname, progname);
+               "\t-S: uses O_SYNC flag for open. By default O_SYNC is not used\n"
+               "\t-N: no_verify: means no write verification. By default noverify is false\n"
+               "e.g: %s -t 4608 -a size=4096,off=512 -a size=4096,off=4608 filename\n"
+               "e.g: %s -t 1048576 -a size=1048576 -S -N filename\n",
+               progname, progname, progname);
        exit(1);
 }
 
@@ -238,6 +241,16 @@ static int io_write(int fd, int num_events)
                return 1;
        }
 
+       for (i = 0; i < num_events; i++) {
+               int err = (int)evs[i].res;
+
+               if (err < 0) {
+                       fprintf(stderr, "error %s with event %d\n",
+                               strerror(err), i);
+                       return 1;
+               }
+       }
+
        /* Try to destroy at here, not necessary, so don't check result */
        io_destroy(ctx);
 
@@ -257,13 +270,14 @@ static int io_verify(int fd)
                        perror("pread");
                        return 1;
                } else if (sret != p->param->buf_size) {
-                       fprintf(stderr, "short read %zd was less than %zu\n",
-                               sret, p->param->buf_size);
+                       fprintf(stderr, "short read %zd was less than %zu at %zu\n",
+                               sret, p->param->buf_size, p->param->offset);
                        return 1;
                }
                if (memcmp(p->param->buf,
                           p->param->cmp_buf, p->param->buf_size)) {
-                       printf("Find corruption\n");
+                       printf("Find corruption at %zu length %zu\n", p->param->offset,
+                              p->param->buf_size);
                        dump_buffer(p->param->buf, p->param->offset,
                                    p->param->buf_size);
                        corrupted++;
@@ -281,8 +295,10 @@ int main(int argc, char *argv[])
        char *filename = NULL;
        int num_events = 0;
        off_t tsize = 0;
+       int o_sync = 0;
+       int no_verify = 0;
 
-       while ((c = getopt(argc, argv, "a:t:")) != -1) {
+       while ((c = getopt(argc, argv, "a:t:SN")) != -1) {
                char *endp;
 
                switch (c) {
@@ -297,6 +313,12 @@ int main(int argc, char *argv[])
                case 't':
                        tsize = strtoul(optarg, &endp, 0);
                        break;
+               case 'S':
+                       o_sync = O_SYNC;
+                       break;
+               case 'N':
+                       no_verify = 1;
+                       break;
                default:
                        usage(argv[0]);
                }
@@ -313,7 +335,7 @@ int main(int argc, char *argv[])
        else
                usage(argv[0]);
 
-       fd = open(filename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR, 0600);
+       fd = open(filename, O_DIRECT | O_CREAT | O_TRUNC | O_RDWR | o_sync, 0600);
        if (fd == -1) {
                perror("open");
                return 1;
@@ -331,9 +353,11 @@ int main(int argc, char *argv[])
                return 1;
        }
 
-       if (io_verify(fd) != 0) {
-               fprintf(stderr, "Data verification fails\n");
-               return 1;
+       if (no_verify == 0) {
+               if (io_verify(fd) != 0) {
+                       fprintf(stderr, "Data verification fails\n");
+                       return 1;
+               }
        }
 
        close(fd);
index 51ede5bb6c8b83a3a96b186d9ad5c5d1f9fb7e20..685e3b9dce48ceaa5aea74f29d25c4a776d8e03f 100644 (file)
@@ -402,6 +402,7 @@ int main(int argc, char **argv)
        }
 
        strncpy(filename, argv[argc-1], PATH_MAX);
+       filename[PATH_MAX - 1] = '\0';
 
        if (alignment == 0)
                alignment = get_logical_block_size(filename);
index cca8dcf8ff6014fd2c2fc300d7496807117f2fdd..1218e7264c8f91132364d21762867a19abc2089b 100644 (file)
@@ -20,19 +20,41 @@ exit(1); } while (0)
 fprintf(stderr, __VA_ARGS__); exit (1); \
 } while (0)
 
+void usage(char *progname)
+{
+       fail("usage: %s [-m max_attr_size] <file>\n", progname);
+}
+
 int main(int argc, char *argv[])
 {
        int ret;
        int fd;
+       int c;
        char *path;
        char *name = "user.world";
        char *value;
        struct stat sbuf;
        size_t size = sizeof(value);
+       size_t maxsize = XATTR_SIZE_MAX;
+
+       while ((c = getopt(argc, argv, "m:")) != -1) {
+               char *endp;
+
+               switch (c) {
+               case 'm':
+                       maxsize = strtoul(optarg, &endp, 0);
+                       if (*endp || (maxsize > XATTR_SIZE_MAX))
+                               fail("Invalid 'max_attr_size' value\n");
+                       break;
+               default:
+                       usage(argv[0]);
+               }
+       }
 
-       if (argc != 2)
-               fail("Usage: %s <file>\n", argv[0]);
-       path = argv[1];
+       if (optind == argc - 1)
+               path = argv[optind];
+       else
+               usage(argv[0]);
 
        fd = open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
        if (fd < 0) die();
@@ -46,7 +68,7 @@ int main(int argc, char *argv[])
        size = sbuf.st_blksize * 3 / 4;
        if (!size)
                fail("Invalid st_blksize(%ld)\n", sbuf.st_blksize);
-       size = MIN(size, XATTR_SIZE_MAX);
+       size = MIN(size, maxsize);
        value = malloc(size);
        if (!value)
                fail("Failed to allocate memory\n");
index 6c08fcb77637eb178d4450f9b7ab08f34d6ad96b..d29bbb709dcf3840e3cd90feafc4ecf1e3cffe9c 100755 (executable)
@@ -59,9 +59,10 @@ class CRC32(object):
     # deduce the 4 bytes we need to insert
     for c in struct.pack('<L',fwd_crc)[::-1]:
       bkd_crc = ((bkd_crc << 8) & 0xffffffff) ^ self.reverse[bkd_crc >> 24]
-      bkd_crc ^= ord(c)
+      bkd_crc ^= c
 
-    res = s[:pos] + struct.pack('<L', bkd_crc) + s[pos:]
+    res = bytes(s[:pos], 'ascii') + struct.pack('<L', bkd_crc) + \
+          bytes(s[pos:], 'ascii')
     return res
 
   def parse_args(self):
@@ -72,6 +73,12 @@ class CRC32(object):
                         help="number of forged names to create")
     return parser.parse_args()
 
+def has_invalid_chars(result: bytes):
+    for i in result:
+        if i == 0 or i == int.from_bytes(b'/', byteorder="little"):
+            return True
+    return False
+
 if __name__=='__main__':
 
   crc = CRC32()
@@ -80,12 +87,15 @@ if __name__=='__main__':
   args = crc.parse_args()
   dirpath=args.dir
   while count < args.count :
-    origname = os.urandom (89).encode ("hex")[:-1].strip ("\x00")
+    origname = os.urandom (89).hex()[:-1].strip ("\x00")
     forgename = crc.forge(wanted_crc, origname, 4)
-    if ("/" not in forgename) and ("\x00" not in forgename):
+    if not has_invalid_chars(forgename):
       srcpath=dirpath + '/' + str(count)
-      dstpath=dirpath + '/' + forgename
-      file (srcpath, 'a').close()
+      # We have to convert all strings to bytes to concatenate the forged
+      # name (bytes).
+      # Thankfully os.rename() can accept bytes directly.
+      dstpath=bytes(dirpath, "ascii") + bytes('/', "ascii") + forgename
+      open(srcpath, mode="a").close()
       os.rename(srcpath, dstpath)
       os.system('btrfs fi sync %s' % (dirpath))
       count+=1;
index 17db2c026ebfa283a7e761b7664f32ddf136372e..dd11f7be883187722962c63afedc801331b6358a 100644 (file)
@@ -20,7 +20,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
 #include <sys/types.h>
@@ -127,7 +126,7 @@ int main(int argc, char *argv[])
        if (ret < 0)
                exit_log("%m - Failed to create new mount namespace");
 
-       ret = mount(NULL, base_dir, NULL, MS_REC | MS_SHARED, NULL);
+       ret = sys_mount(NULL, base_dir, NULL, MS_REC | MS_SHARED, NULL);
        if (ret < 0)
                exit_log("%m - Failed to make base_dir shared mountpoint");
 
@@ -174,7 +173,7 @@ int main(int argc, char *argv[])
                }
                close(fd_tree);
 
-               ret = umount2(target, MNT_DETACH);
+               ret = sys_umount2(target, MNT_DETACH);
                if (ret < 0) {
                        fprintf(stderr, "%m - Failed to unmount %s", target);
                        exit_code = EXIT_FAILURE;
diff --git a/src/dio-buf-fault.c b/src/dio-buf-fault.c
new file mode 100644 (file)
index 0000000..911c3e1
--- /dev/null
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 Meta Platforms, Inc.  All Rights Reserved.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* to get definition of O_DIRECT flag. */
+#endif
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+/*
+ * mmap a source file, then do a direct write of that mmapped region to a
+ * destination file.
+ */
+
+int prep_mmap_buffer(char *src_filename, void **addr)
+{
+       struct stat st;
+       int fd;
+       int ret;
+
+       fd = open(src_filename, O_RDWR, 0666);
+       if (fd == -1)
+               err(1, "failed to open %s", src_filename);
+
+       ret = fstat(fd, &st);
+       if (ret)
+               err(1, "failed to stat %d", fd);
+
+       *addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (*addr == MAP_FAILED)
+               err(1, "failed to mmap %d", fd);
+
+       return st.st_size;
+}
+
+int do_dio(char *dst_filename, void *buf, size_t sz)
+{
+       int fd;
+       ssize_t ret;
+
+       fd = open(dst_filename, O_CREAT | O_TRUNC | O_WRONLY | O_DIRECT, 0666);
+       if (fd == -1)
+               err(1, "failed to open %s", dst_filename);
+       while (sz) {
+               ret = write(fd, buf, sz);
+               if (ret < 0) {
+                       if (errno == -EINTR)
+                               continue;
+                       else
+                               err(1, "failed to write %lu bytes to %d", sz, fd);
+               } else if (ret == 0) {
+                       break;
+               }
+               buf += ret;
+               sz -= ret;
+       }
+       return sz;
+}
+
+int main(int argc, char *argv[]) {
+       size_t sz;
+       void *buf = NULL;
+       char c;
+
+       if (argc != 3)
+               errx(1, "no in and out file name arguments given");
+       sz = prep_mmap_buffer(argv[1], &buf);
+
+       /* touch the first page of the mapping to bring it into cache */
+       c = ((char *)buf)[0];
+       printf("%u\n", c);
+
+       do_dio(argv[2], buf, sz);
+}
index 78b6643288f95cf781d20fd3ff11fff3dba7b6c6..317792257b1c41293cbe9d63d21027c93a378185 100644 (file)
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
+#include <linux/types.h>
 
-typedef unsigned long long __u64;
+#ifdef HAVE_LINUX_EXT4_H
+#include <linux/ext4.h>
+#endif
 
 #ifndef EXT4_IOC_RESIZE_FS
 #define EXT4_IOC_RESIZE_FS             _IOW('f', 16, __u64)
diff --git a/src/fake-dump-rootino.c b/src/fake-dump-rootino.c
new file mode 100644 (file)
index 0000000..8a30dff
--- /dev/null
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022 Fujitsu Limited.  All Rights Reserved. */
+#define _LARGEFILE64_SOURCE
+#include <endian.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <uuid/uuid.h>
+
+// Definitions from xfsdump
+#define PGSZLOG2       12
+#define PGSZ           (1 << PGSZLOG2)
+#define GLOBAL_HDR_SZ          PGSZ
+#define GLOBAL_HDR_MAGIC_SZ    8
+#define GLOBAL_HDR_STRING_SZ   0x100
+#define GLOBAL_HDR_TIME_SZ     4
+#define GLOBAL_HDR_UUID_SZ     0x10
+typedef int32_t time32_t;
+struct global_hdr {
+       char gh_magic[GLOBAL_HDR_MAGIC_SZ];             /*   8    8 */
+               /* unique signature of xfsdump */
+       uint32_t gh_version;                            /*   4    c */
+               /* header version */
+       uint32_t gh_checksum;                           /*   4   10 */
+               /* 32-bit unsigned additive inverse of entire header */
+       time32_t gh_timestamp;                          /*   4   14 */
+               /* time32_t of dump */
+       char gh_pad1[4];                                /*   4   18 */
+               /* alignment */
+       uint64_t gh_ipaddr;                             /*   8   20 */
+               /* from gethostid(2), room for expansion */
+       uuid_t gh_dumpid;                               /*  10   30 */
+               /* ID of dump session    */
+       char gh_pad2[0xd0];                             /*  d0  100 */
+               /* alignment */
+       char gh_hostname[GLOBAL_HDR_STRING_SZ]; /* 100  200 */
+               /* from gethostname(2) */
+       char gh_dumplabel[GLOBAL_HDR_STRING_SZ];        /* 100  300 */
+               /* label of dump session */
+       char gh_pad3[0x100];                            /* 100  400 */
+               /* padding */
+       char gh_upper[GLOBAL_HDR_SZ - 0x400];           /* c00 1000 */
+               /* header info private to upper software layers */
+};
+typedef struct global_hdr global_hdr_t;
+
+#define sizeofmember( t, m )   sizeof( ( ( t * )0 )->m )
+
+#define DRIVE_HDR_SZ           sizeofmember(global_hdr_t, gh_upper)
+struct drive_hdr {
+       uint32_t dh_drivecnt;                           /*   4    4 */
+               /* number of drives used to dump the fs */
+       uint32_t dh_driveix;                            /*   4    8 */
+               /* 0-based index of the drive used to dump this stream */
+       int32_t dh_strategyid;                          /*   4    c */
+               /* ID of the drive strategy used to produce this dump */
+       char dh_pad1[0x1f4];                            /* 1f4  200 */
+               /* padding */
+       char dh_specific[0x200];                        /* 200  400 */
+               /* drive strategy-specific info */
+       char dh_upper[DRIVE_HDR_SZ - 0x400];            /* 800  c00 */
+               /* header info private to upper software layers */
+};
+typedef struct drive_hdr drive_hdr_t;
+
+#define MEDIA_HDR_SZ           sizeofmember(drive_hdr_t, dh_upper)
+struct media_hdr {
+       char mh_medialabel[GLOBAL_HDR_STRING_SZ];       /* 100  100 */
+               /* label of media object containing file */
+       char mh_prevmedialabel[GLOBAL_HDR_STRING_SZ];   /* 100  200 */
+               /* label of upstream media object */
+       char mh_pad1[GLOBAL_HDR_STRING_SZ];             /* 100  300 */
+               /* in case more labels needed */
+       uuid_t mh_mediaid;                              /*  10  310 */
+               /* ID of media object   */
+       uuid_t mh_prevmediaid;                          /*  10  320 */
+               /* ID of upstream media object */
+       char mh_pad2[GLOBAL_HDR_UUID_SZ];               /*  10  330 */
+               /* in case more IDs needed */
+       uint32_t mh_mediaix;                            /*   4  334 */
+               /* 0-based index of this media object within the dump stream */
+       uint32_t mh_mediafileix;                        /*   4  338 */
+               /* 0-based index of this file within this media object */
+       uint32_t mh_dumpfileix;                 /*   4  33c */
+               /* 0-based index of this file within this dump stream */
+       uint32_t mh_dumpmediafileix;                    /*   4  340 */
+               /* 0-based index of file within dump stream and media object */
+       uint32_t mh_dumpmediaix;                        /*   4  344 */
+               /* 0-based index of this dump within the media object */
+       int32_t mh_strategyid;                          /*   4  348 */
+               /* ID of the media strategy used to produce this dump */
+       char mh_pad3[0x38];                             /*  38  380 */
+               /* padding */
+       char mh_specific[0x80];                 /*  80  400 */
+               /* media strategy-specific info */
+       char mh_upper[MEDIA_HDR_SZ - 0x400];            /* 400  800 */
+               /* header info private to upper software layers */
+};
+typedef struct media_hdr media_hdr_t;
+
+#define CONTENT_HDR_SZ         sizeofmember(media_hdr_t, mh_upper)
+#define CONTENT_HDR_FSTYPE_SZ  16
+#define CONTENT_STATSZ         160 /* must match dlog.h DLOG_MULTI_STATSZ */
+struct content_hdr {
+       char ch_mntpnt[GLOBAL_HDR_STRING_SZ];           /* 100  100 */
+               /* full pathname of fs mount point */
+       char ch_fsdevice[GLOBAL_HDR_STRING_SZ]; /* 100  200 */
+               /* full pathname of char device containing fs */
+       char  ch_pad1[GLOBAL_HDR_STRING_SZ];            /* 100  300 */
+               /* in case another label is needed */
+       char ch_fstype[CONTENT_HDR_FSTYPE_SZ];  /*  10  310 */
+               /* from fsid.h */
+       uuid_t ch_fsid;                                 /*  10  320 */
+               /* file system uuid */
+       char  ch_pad2[GLOBAL_HDR_UUID_SZ];              /*  10  330 */
+               /* in case another id is needed */
+       char ch_pad3[8];                                /*   8  338 */
+               /* padding */
+       int32_t ch_strategyid;                          /*   4  33c */
+               /* ID of the content strategy used to produce this dump */
+       char ch_pad4[4];                                /*   4  340 */
+               /* alignment */
+       char ch_specific[0xc0];                 /*  c0  400 */
+               /* content strategy-specific info */
+};
+typedef struct content_hdr content_hdr_t;
+
+typedef uint64_t xfs_ino_t;
+struct startpt {
+       xfs_ino_t sp_ino;               /* first inode to dump */
+       off64_t sp_offset;      /* bytes to skip in file data fork */
+       int32_t sp_flags;
+       int32_t sp_pad1;
+};
+typedef struct startpt startpt_t;
+
+#define CONTENT_INODE_HDR_SZ  sizeofmember(content_hdr_t, ch_specific)
+struct content_inode_hdr {
+       int32_t cih_mediafiletype;                      /*   4   4 */
+               /* dump media file type: see #defines below */
+       int32_t cih_dumpattr;                           /*   4   8 */
+               /* dump attributes: see #defines below */
+       int32_t cih_level;                              /*   4   c */
+               /* dump level */
+       char pad1[4];                                   /*   4  10 */
+               /* alignment */
+       time32_t cih_last_time;                         /*   4  14 */
+               /* if an incremental, time of previous dump at a lesser level */
+       time32_t cih_resume_time;                       /*   4  18 */
+               /* if a resumed dump, time of interrupted dump */
+       xfs_ino_t cih_rootino;                          /*   8  20 */
+               /* root inode number */
+       uuid_t cih_last_id;                             /*  10  30 */
+               /* if an incremental, uuid of prev dump */
+       uuid_t cih_resume_id;                           /*  10  40 */
+               /* if a resumed dump, uuid of interrupted dump */
+       startpt_t cih_startpt;                          /*  18  58 */
+               /* starting point of media file contents */
+       startpt_t cih_endpt;                            /*  18  70 */
+               /* starting point of next stream */
+       uint64_t cih_inomap_hnkcnt;                     /*   8  78 */
+
+       uint64_t cih_inomap_segcnt;                     /*   8  80 */
+
+       uint64_t cih_inomap_dircnt;                     /*   8  88 */
+
+       uint64_t cih_inomap_nondircnt;                  /*   8  90 */
+
+       xfs_ino_t cih_inomap_firstino;                  /*   8  98 */
+
+       xfs_ino_t cih_inomap_lastino;                   /*   8  a0 */
+
+       uint64_t cih_inomap_datasz;                     /*   8  a8 */
+               /* bytes of non-metadata dumped */
+       char cih_pad2[CONTENT_INODE_HDR_SZ - 0xa8];     /*  18  c0 */
+               /* padding */
+};
+typedef struct content_inode_hdr content_inode_hdr_t;
+// End of definitions from xfsdump
+
+int main(int argc, char *argv[]) {
+
+       if (argc < 3) {
+               fprintf(stderr, "Usage: %s <path/to/dumpfile> <fake rootino>\n", argv[0]);
+               exit(1);
+       }
+
+       const char *filepath = argv[1];
+       const uint64_t fake_root_ino = (uint64_t)strtol(argv[2], NULL, 10);
+
+       int fd = open(filepath, O_RDWR);
+       if (fd < 0) {
+               perror("open");
+               exit(1);
+       }
+       global_hdr_t *header = mmap(NULL, GLOBAL_HDR_SZ, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+       if (header == MAP_FAILED) {
+               perror("mmap");
+               exit(1);
+       }
+
+       drive_hdr_t *dh = (drive_hdr_t *)header->gh_upper;
+       media_hdr_t *mh = (media_hdr_t *)dh->dh_upper;
+       content_hdr_t *ch = (content_hdr_t *)mh->mh_upper;
+       content_inode_hdr_t *cih = (content_inode_hdr_t *)ch->ch_specific;
+       uint64_t *rootino_ptr = &cih->cih_rootino;
+
+       int32_t checksum = (int32_t)be32toh(header->gh_checksum);
+       uint64_t orig_rootino = be64toh(*rootino_ptr);
+
+       // Fake root inode number
+       *rootino_ptr = htobe64(fake_root_ino);
+
+       // Update checksum along with overwriting rootino.
+       uint64_t gap = orig_rootino - fake_root_ino;
+       checksum += (gap >> 32) + (gap & 0x00000000ffffffff);
+       header->gh_checksum = htobe32(checksum);
+
+       munmap(header, GLOBAL_HDR_SZ);
+       close(fd);
+}
index 941f96fb309710b17cba398252d35a952620e035..7e474ce558e623113c887f9a85e1d1bf9498b6aa 100644 (file)
@@ -232,15 +232,20 @@ check_uring_support(void)
        int err;
 
        err = io_uring_queue_init(1, &ring, 0);
-       if (err == 0)
+       switch (err) {
+       case 0:
                return 0;
-
-       if (err == -ENOSYS) /* CONFIG_IO_URING=n */
+       case -ENOSYS:
+               /* CONFIG_IO_URING=n */
                return 1;
-
-       fprintf(stderr, "unexpected error from io_uring_queue_init(): %s\n",
-               strerror(-err));
-       return 2;
+       case -EPERM:
+               /* Might be due to sysctl io_uring_disabled isn't 0 */
+               return 2;
+       default:
+               fprintf(stderr, "unexpected error from io_uring_queue_init(): %s\n",
+                       strerror(-err));
+               return 100;
+       }
 #else
        /* liburing is unavailable, assume IO_URING is unsupported */
        return 1;
diff --git a/src/fiemap-fault.c b/src/fiemap-fault.c
new file mode 100644 (file)
index 0000000..7326006
--- /dev/null
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Meta Platforms, Inc.  All Rights Reserved.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/fiemap.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+int prep_mmap_buffer(int fd, void **addr)
+{
+       struct stat st;
+       int ret;
+
+       ret = fstat(fd, &st);
+       if (ret)
+               err(1, "failed to stat %d", fd);
+
+       *addr = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+       if (*addr == MAP_FAILED)
+               err(1, "failed to mmap %d", fd);
+
+       return st.st_size;
+}
+
+int main(int argc, char *argv[])
+{
+       struct fiemap *fiemap;
+       size_t sz, last = 0;
+       void *buf = NULL;
+       int ret, fd;
+
+       if (argc != 2)
+               errx(1, "no in and out file name arguments given");
+
+       fd = open(argv[1], O_RDWR, 0666);
+       if (fd == -1)
+               err(1, "failed to open %s", argv[1]);
+
+       sz = prep_mmap_buffer(fd, &buf);
+
+       fiemap = (struct fiemap *)buf;
+       fiemap->fm_flags = 0;
+       fiemap->fm_extent_count = (sz - sizeof(struct fiemap)) /
+                                 sizeof(struct fiemap_extent);
+
+       while (last < sz) {
+               int i;
+
+               fiemap->fm_start = last;
+               fiemap->fm_length = sz - last;
+
+               ret = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
+               if (ret < 0)
+                       err(1, "fiemap failed %d", errno);
+               for (i = 0; i < fiemap->fm_mapped_extents; i++)
+                      last = fiemap->fm_extents[i].fe_logical +
+                              fiemap->fm_extents[i].fe_length;
+       }
+
+       munmap(buf, sz);
+       close(fd);
+       return 0;
+}
index 3db24daa792b0e7a6338adbc6626b3a4351c2552..fa085a25f1c4fe9d8dae18d762edba19295bac19 100644 (file)
@@ -236,7 +236,7 @@ check_flags(struct fiemap *fiemap, int blocksize)
 
 static int
 check_data(struct fiemap *fiemap, __u64 logical_offset, int blocksize,
-          int last, int prealloc)
+          int prealloc)
 {
        struct fiemap_extent *extent;
        __u64 orig_offset = logical_offset;
@@ -280,11 +280,6 @@ check_data(struct fiemap *fiemap, __u64 logical_offset, int blocksize,
        if (!found) {
                printf("ERROR: couldn't find extent at %llu\n",
                       (unsigned long long)(orig_offset / blocksize));
-       } else if (last &&
-                  !(fiemap->fm_extents[c].fe_flags & FIEMAP_EXTENT_LAST)) {
-               printf("ERROR: last extent not marked as last: %llu\n",
-                      (unsigned long long)(orig_offset / blocksize));
-               found = 0;
        }
 
        return (!found) ? -1 : 0;
@@ -375,6 +370,13 @@ check_hole(struct fiemap *fiemap, int fd, __u64 logical_offset, int blocksize)
                if (logical_offset + blocksize < start)
                        break;
 
+               /*
+                * Filesystems are allowed to fill in holes with preallocated
+                * unwritten extents
+                */
+               if (extent->fe_flags & FIEMAP_EXTENT_UNWRITTEN)
+                       continue;
+
                if (logical_offset >= start &&
                    logical_offset < end) {
 
@@ -411,7 +413,7 @@ compare_fiemap_and_map(int fd, char *map, int blocks, int blocksize, int syncfil
 {
        struct fiemap *fiemap;
        char *fiebuf;
-       int blocks_to_map, ret, cur_extent = 0, last_data = 0;
+       int blocks_to_map, ret, cur_extent = 0;
        __u64 map_start, map_length;
        int i, c;
 
@@ -430,11 +432,6 @@ compare_fiemap_and_map(int fd, char *map, int blocks, int blocksize, int syncfil
        map_start = 0;
        map_length = blocks_to_map * blocksize;
 
-       for (i = 0; i < blocks; i++) {
-               if (map[i] != 'H')
-                       last_data = i;
-       }
-
        fiemap->fm_flags = syncfile ? FIEMAP_FLAG_SYNC : 0;
        fiemap->fm_extent_count = blocks_to_map;
        fiemap->fm_mapped_extents = 0;
@@ -464,7 +461,7 @@ compare_fiemap_and_map(int fd, char *map, int blocks, int blocksize, int syncfil
                        switch (map[i]) {
                        case 'D':
                                if (check_data(fiemap, logical_offset,
-                                              blocksize, last_data == i, 0))
+                                              blocksize, 0))
                                        goto error;
                                break;
                        case 'H':
@@ -474,7 +471,7 @@ compare_fiemap_and_map(int fd, char *map, int blocks, int blocksize, int syncfil
                                break;
                        case 'P':
                                if (check_data(fiemap, logical_offset,
-                                              blocksize, last_data == i, 1))
+                                              blocksize, 1))
                                        goto error;
                                break;
                        default:
diff --git a/src/fiexchange.h b/src/fiexchange.h
new file mode 100644 (file)
index 0000000..6a3ae89
--- /dev/null
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */
+/*
+ * FIEXCHANGE ioctl definitions, to facilitate exchanging parts of files.
+ *
+ * Copyright (C) 2022 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef _LINUX_FIEXCHANGE_H
+#define _LINUX_FIEXCHANGE_H
+
+#include <linux/types.h>
+
+/*
+ * Exchange part of file1 with part of the file that this ioctl that is being
+ * called against (which we'll call file2).  Filesystems must be able to
+ * restart and complete the operation even after the system goes down.
+ */
+struct xfs_exch_range {
+       __s64           file1_fd;
+       __s64           file1_offset;   /* file1 offset, bytes */
+       __s64           file2_offset;   /* file2 offset, bytes */
+       __s64           length;         /* bytes to exchange */
+
+       __u64           flags;          /* see XFS_EXCH_RANGE_* below */
+
+       /* file2 metadata for optional freshness checks */
+       __s64           file2_ino;      /* inode number */
+       __s64           file2_mtime;    /* modification time */
+       __s64           file2_ctime;    /* change time */
+       __s32           file2_mtime_nsec; /* mod time, nsec */
+       __s32           file2_ctime_nsec; /* change time, nsec */
+
+       __u64           pad[6];         /* must be zeroes */
+};
+
+/*
+ * Atomic exchange operations are not required.  This relaxes the requirement
+ * that the filesystem must be able to complete the operation after a crash.
+ */
+#define XFS_EXCH_RANGE_NONATOMIC       (1 << 0)
+
+/*
+ * Check that file2's inode number, mtime, and ctime against the values
+ * provided, and return -EBUSY if there isn't an exact match.
+ */
+#define XFS_EXCH_RANGE_FILE2_FRESH     (1 << 1)
+
+/*
+ * Check that the file1's length is equal to file1_offset + length, and that
+ * file2's length is equal to file2_offset + length.  Returns -EDOM if there
+ * isn't an exact match.
+ */
+#define XFS_EXCH_RANGE_FULL_FILES      (1 << 2)
+
+/*
+ * Exchange file data all the way to the ends of both files, and then exchange
+ * the file sizes.  This flag can be used to replace a file's contents with a
+ * different amount of data.  length will be ignored.
+ */
+#define XFS_EXCH_RANGE_TO_EOF          (1 << 3)
+
+/* Flush all changes in file data and file metadata to disk before returning. */
+#define XFS_EXCH_RANGE_FSYNC           (1 << 4)
+
+/* Dry run; do all the parameter verification but do not change anything. */
+#define XFS_EXCH_RANGE_DRY_RUN         (1 << 5)
+
+/*
+ * Only exchange ranges where file1's range maps to a written extent.  This can
+ * be used to emulate scatter-gather atomic writes with a temp file.
+ */
+#define XFS_EXCH_RANGE_FILE1_WRITTEN   (1 << 6)
+
+/*
+ * Commit the contents of file1 into file2 if file2 has the same inode number,
+ * mtime, and ctime as the arguments provided to the call.  The old contents of
+ * file2 will be moved to file1.
+ *
+ * With this flag, all committed information can be retrieved even if the
+ * system crashes or is rebooted.  This includes writing through or flushing a
+ * disk cache if present.  The call blocks until the device reports that the
+ * commit is complete.
+ *
+ * This flag should not be combined with NONATOMIC.  It can be combined with
+ * FILE1_WRITTEN.
+ */
+#define XFS_EXCH_RANGE_COMMIT          (XFS_EXCH_RANGE_FILE2_FRESH | \
+                                        XFS_EXCH_RANGE_FSYNC)
+
+#define XFS_EXCH_RANGE_ALL_FLAGS       (XFS_EXCH_RANGE_NONATOMIC | \
+                                        XFS_EXCH_RANGE_FILE2_FRESH | \
+                                        XFS_EXCH_RANGE_FULL_FILES | \
+                                        XFS_EXCH_RANGE_TO_EOF | \
+                                        XFS_EXCH_RANGE_FSYNC | \
+                                        XFS_EXCH_RANGE_DRY_RUN | \
+                                        XFS_EXCH_RANGE_FILE1_WRITTEN)
+
+#define XFS_IOC_EXCHANGE_RANGE _IOWR('X', 129, struct xfs_exch_range)
+
+#endif /* _LINUX_FIEXCHANGE_H */
index ffb9534d81740ef9d65ade424a9ce58848c5d483..a1b5005d001fc5bab11037156a379b4add5a4b62 100644 (file)
@@ -37,7 +37,8 @@
 /*
  * Define to enable the tests of the crypto code in this file.  If enabled, you
  * must link this program with OpenSSL (-lcrypto) v1.1.0 or later, and your
- * kernel needs CONFIG_CRYPTO_USER_API_SKCIPHER=y and CONFIG_CRYPTO_ADIANTUM=y.
+ * kernel needs CONFIG_CRYPTO_USER_API_SKCIPHER=y, CONFIG_CRYPTO_ADIANTUM=y, and
+ * CONFIG_CRYPTO_HCTR2=y.
  */
 #undef ENABLE_ALG_TESTS
 
@@ -54,14 +55,15 @@ static void usage(FILE *fp)
 "resulting ciphertext (or plaintext) to stdout.\n"
 "\n"
 "CIPHER can be AES-256-XTS, AES-256-CTS-CBC, AES-128-CBC-ESSIV, AES-128-CTS-CBC,\n"
-"or Adiantum.  MASTER_KEY must be a hex string long enough for the cipher.\n"
+"Adiantum, or AES-256-HCTR2.  MASTER_KEY must be a hex string long enough for\n"
+"the cipher.\n"
 "\n"
 "WARNING: this program is only meant for testing, not for \"real\" use!\n"
 "\n"
 "Options:\n"
-"  --block-number=BNUM         Starting block number for IV generation.\n"
+"  --data-unit-index=DUIDX     Starting data unit index for IV generation.\n"
 "                                Default: 0\n"
-"  --block-size=BLOCK_SIZE     Encrypt each BLOCK_SIZE bytes independently.\n"
+"  --data-unit-size=DUSIZE     Encrypt each DUSIZE bytes independently.\n"
 "                                Default: 4096 bytes\n"
 "  --decrypt                   Decrypt instead of encrypt\n"
 "  --direct-key                Use the format where the IVs include the file\n"
@@ -84,8 +86,8 @@ static void usage(FILE *fp)
 "                                HKDF-SHA512, or none.  Default: none\n"
 "  --mode-num=NUM              The encryption mode number.  This may be required\n"
 "                                for key derivation, depending on other options.\n"
-"  --padding=PADDING           If last block is partial, zero-pad it to next\n"
-"                                PADDING-byte boundary.  Default: BLOCK_SIZE\n"
+"  --padding=PADDING           If last data unit is partial, zero-pad it to next\n"
+"                                PADDING-byte boundary.  Default: DUSIZE\n"
        , fp);
 }
 
@@ -271,7 +273,59 @@ static void rand_bytes(u8 *buf, size_t count)
        while (count--)
                *buf++ = rand();
 }
-#endif
+
+#include <linux/if_alg.h>
+#include <sys/socket.h>
+#define SOL_ALG 279
+static void af_alg_crypt(int algfd, int op, const u8 *key, size_t keylen,
+                        const u8 *iv, size_t ivlen,
+                        const u8 *src, u8 *dst, size_t datalen)
+{
+       size_t controllen = CMSG_SPACE(sizeof(int)) +
+                           CMSG_SPACE(sizeof(struct af_alg_iv) + ivlen);
+       u8 *control = xmalloc(controllen);
+       struct iovec iov = { .iov_base = (u8 *)src, .iov_len = datalen };
+       struct msghdr msg = {
+               .msg_iov = &iov,
+               .msg_iovlen = 1,
+               .msg_control = control,
+               .msg_controllen = controllen,
+       };
+       struct cmsghdr *cmsg;
+       struct af_alg_iv *algiv;
+       int reqfd;
+
+       memset(control, 0, controllen);
+
+       cmsg = CMSG_FIRSTHDR(&msg);
+       cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+       cmsg->cmsg_level = SOL_ALG;
+       cmsg->cmsg_type = ALG_SET_OP;
+       *(int *)CMSG_DATA(cmsg) = op;
+
+       cmsg = CMSG_NXTHDR(&msg, cmsg);
+       cmsg->cmsg_len = CMSG_LEN(sizeof(struct af_alg_iv) + ivlen);
+       cmsg->cmsg_level = SOL_ALG;
+       cmsg->cmsg_type = ALG_SET_IV;
+       algiv = (struct af_alg_iv *)CMSG_DATA(cmsg);
+       algiv->ivlen = ivlen;
+       memcpy(algiv->iv, iv, ivlen);
+
+       if (setsockopt(algfd, SOL_ALG, ALG_SET_KEY, key, keylen) != 0)
+               die_errno("can't set key on AF_ALG socket");
+
+       reqfd = accept(algfd, NULL, NULL);
+       if (reqfd < 0)
+               die_errno("can't accept() AF_ALG socket");
+       if (sendmsg(reqfd, &msg, 0) != datalen)
+               die_errno("can't sendmsg() AF_ALG request socket");
+       if (xread(reqfd, dst, datalen) != datalen)
+               die("short read from AF_ALG request socket");
+       close(reqfd);
+
+       free(control);
+}
+#endif /* ENABLE_ALG_TESTS */
 
 /*----------------------------------------------------------------------------*
  *                          Finite field arithmetic                           *
@@ -296,7 +350,7 @@ typedef struct {
 } ble128;
 
 /* Multiply a GF(2^128) element by the polynomial 'x' */
-static inline void gf2_128_mul_x(ble128 *t)
+static inline void gf2_128_mul_x_xts(ble128 *t)
 {
        u64 lo = le64_to_cpu(t->lo);
        u64 hi = le64_to_cpu(t->hi);
@@ -305,6 +359,38 @@ static inline void gf2_128_mul_x(ble128 *t)
        t->lo = cpu_to_le64((lo << 1) ^ ((hi & (1ULL << 63)) ? 0x87 : 0));
 }
 
+static inline void gf2_128_mul_x_polyval(ble128 *t)
+{
+       u64 lo = le64_to_cpu(t->lo);
+       u64 hi = le64_to_cpu(t->hi);
+       u64 lo_reducer = (hi & (1ULL << 63)) ? 1 : 0;
+       u64 hi_reducer = (hi & (1ULL << 63)) ? 0xc2ULL << 56 : 0;
+
+       t->hi = cpu_to_le64(((hi << 1) | (lo >> 63)) ^ hi_reducer);
+       t->lo = cpu_to_le64((lo << 1) ^ lo_reducer);
+}
+
+static void gf2_128_mul_polyval(ble128 *r, const ble128 *b)
+{
+       int i;
+       ble128 p;
+       u64 lo = le64_to_cpu(b->lo);
+       u64 hi = le64_to_cpu(b->hi);
+
+       memset(&p, 0, sizeof(p));
+       for (i = 0; i < 64; i++) {
+               if (lo & (1ULL << i))
+                       xor((u8 *)&p, (u8 *)&p, (u8 *)r, sizeof(p));
+               gf2_128_mul_x_polyval(r);
+       }
+       for (i = 0; i < 64; i++) {
+               if (hi & (1ULL << i))
+                       xor((u8 *)&p, (u8 *)&p, (u8 *)r, sizeof(p));
+               gf2_128_mul_x_polyval(r);
+       }
+       *r = p;
+}
+
 /*----------------------------------------------------------------------------*
  *                             Group arithmetic                               *
  *----------------------------------------------------------------------------*/
@@ -576,19 +662,37 @@ static void aes_decrypt(const struct aes_key *k, const u8 src[AES_BLOCK_SIZE],
 }
 
 #ifdef ENABLE_ALG_TESTS
-#include <openssl/aes.h>
+#include <openssl/evp.h>
 static void test_aes_keysize(int keysize)
 {
        unsigned long num_tests = NUM_ALG_TEST_ITERATIONS;
+       const EVP_CIPHER *evp_cipher;
+       EVP_CIPHER_CTX *ctx;
 
+       switch (keysize) {
+       case 16:
+               evp_cipher = EVP_aes_128_ecb();
+               break;
+       case 24:
+               evp_cipher = EVP_aes_192_ecb();
+               break;
+       case 32:
+               evp_cipher = EVP_aes_256_ecb();
+               break;
+       default:
+               ASSERT(0);
+       }
+
+       ctx = EVP_CIPHER_CTX_new();
+       ASSERT(ctx != NULL);
        while (num_tests--) {
                struct aes_key k;
-               AES_KEY ref_k;
                u8 key[AES_256_KEY_SIZE];
                u8 ptext[AES_BLOCK_SIZE];
                u8 ctext[AES_BLOCK_SIZE];
                u8 ref_ctext[AES_BLOCK_SIZE];
                u8 decrypted[AES_BLOCK_SIZE];
+               int outl, res;
 
                rand_bytes(key, keysize);
                rand_bytes(ptext, AES_BLOCK_SIZE);
@@ -596,14 +700,18 @@ static void test_aes_keysize(int keysize)
                aes_setkey(&k, key, keysize);
                aes_encrypt(&k, ptext, ctext);
 
-               ASSERT(AES_set_encrypt_key(key, keysize*8, &ref_k) == 0);
-               AES_encrypt(ptext, ref_ctext, &ref_k);
-
+               res = EVP_EncryptInit_ex(ctx, evp_cipher, NULL, key, NULL);
+               ASSERT(res > 0);
+               res = EVP_EncryptUpdate(ctx, ref_ctext, &outl, ptext,
+                                       AES_BLOCK_SIZE);
+               ASSERT(res > 0);
+               ASSERT(outl == AES_BLOCK_SIZE);
                ASSERT(memcmp(ctext, ref_ctext, AES_BLOCK_SIZE) == 0);
 
                aes_decrypt(&k, ctext, decrypted);
                ASSERT(memcmp(ptext, decrypted, AES_BLOCK_SIZE) == 0);
        }
+       EVP_CIPHER_CTX_free(ctx);
 }
 
 static void test_aes(void)
@@ -889,7 +997,11 @@ static void test_hkdf_sha512(void)
                size_t ikmlen = 1 + (rand() % sizeof(ikm));
                size_t saltlen = rand() % (1 + sizeof(salt));
                size_t infolen = rand() % (1 + sizeof(info));
-               size_t outlen = rand() % (1 + sizeof(actual_output));
+               /*
+                * Don't test zero-length outputs, since OpenSSL 3.0 and later
+                * returns an error for those.
+                */
+               size_t outlen = 1 + (rand() % sizeof(actual_output));
 
                rand_bytes(ikm, ikmlen);
                rand_bytes(salt, saltlen);
@@ -904,6 +1016,47 @@ static void test_hkdf_sha512(void)
 }
 #endif /* ENABLE_ALG_TESTS */
 
+/*----------------------------------------------------------------------------*
+ *                                 POLYVAL                                     *
+ *----------------------------------------------------------------------------*/
+
+/*
+ * Reference: "AES-GCM-SIV: Nonce Misuse-Resistant Authenticated Encryption"
+ *     https://datatracker.ietf.org/doc/html/rfc8452
+ */
+
+#define POLYVAL_KEY_SIZE       16
+#define POLYVAL_BLOCK_SIZE     16
+
+static void polyval_update(const u8 key[POLYVAL_KEY_SIZE],
+                          const u8 *msg, size_t msglen,
+                          u8 accumulator[POLYVAL_BLOCK_SIZE])
+{
+       ble128 h;
+       ble128 aligned_accumulator;
+       // x^{-128} = x^127 + x^124 + x^121 + x^114 + 1
+       static const ble128 inv128 = {
+               cpu_to_le64(1),
+               cpu_to_le64(0x9204ULL << 48)
+       };
+
+       /* Partial block support is not necessary for HCTR2 */
+       ASSERT(msglen % POLYVAL_BLOCK_SIZE == 0);
+
+       memcpy(&h, key, POLYVAL_BLOCK_SIZE);
+       memcpy(&aligned_accumulator, accumulator, POLYVAL_BLOCK_SIZE);
+       gf2_128_mul_polyval(&h, &inv128);
+
+       while (msglen > 0) {
+               xor((u8 *)&aligned_accumulator, (u8 *)&aligned_accumulator, msg,
+                   POLYVAL_BLOCK_SIZE);
+               gf2_128_mul_polyval(&aligned_accumulator, &h);
+               msg += POLYVAL_BLOCK_SIZE;
+               msglen -= POLYVAL_BLOCK_SIZE;
+       }
+       memcpy(accumulator, &aligned_accumulator, POLYVAL_BLOCK_SIZE);
+}
+
 /*----------------------------------------------------------------------------*
  *                            AES encryption modes                            *
  *----------------------------------------------------------------------------*/
@@ -927,7 +1080,7 @@ static void aes_256_xts_crypt(const u8 key[2 * AES_256_KEY_SIZE],
                else
                        aes_encrypt(&cipher_key, &dst[i], &dst[i]);
                xor(&dst[i], &dst[i], (const u8 *)&t, AES_BLOCK_SIZE);
-               gf2_128_mul_x(&t);
+               gf2_128_mul_x_xts(&t);
        }
 }
 
@@ -956,12 +1109,18 @@ static void test_aes_256_xts(void)
        while (num_tests--) {
                u8 key[2 * AES_256_KEY_SIZE];
                u8 iv[AES_BLOCK_SIZE];
-               u8 ptext[512];
+               u8 ptext[32 * AES_BLOCK_SIZE];
                u8 ctext[sizeof(ptext)];
                u8 ref_ctext[sizeof(ptext)];
                u8 decrypted[sizeof(ptext)];
-               const size_t datalen = ROUND_DOWN(rand() % (1 + sizeof(ptext)),
-                                                 AES_BLOCK_SIZE);
+               /*
+                * Don't test message lengths that aren't a multiple of the AES
+                * block size, since support for that is not implemented here.
+                * Also don't test zero-length messages, since OpenSSL 3.0 and
+                * later returns an error for those.
+                */
+               const size_t datalen = AES_BLOCK_SIZE *
+                       (1 + rand() % (sizeof(ptext) / AES_BLOCK_SIZE));
                int outl, res;
 
                rand_bytes(key, sizeof(key));
@@ -1176,6 +1335,167 @@ static void aes_128_cts_cbc_decrypt(const u8 key[AES_128_KEY_SIZE],
        aes_cts_cbc_decrypt(key, AES_128_KEY_SIZE, iv, src, dst, nbytes);
 }
 
+/*
+ * Reference: "Length-preserving encryption with HCTR2"
+ *     https://ia.cr/2021/1441
+ */
+
+static void aes_256_xctr_crypt(const u8 key[AES_256_KEY_SIZE],
+                             const u8 iv[AES_BLOCK_SIZE], const u8 *src,
+                             u8 *dst, size_t nbytes)
+{
+       struct aes_key k;
+       union {
+               u8 bytes[AES_BLOCK_SIZE];
+               __le64 ctr;
+       } blk;
+       size_t i;
+
+       aes_setkey(&k, key, AES_256_KEY_SIZE);
+
+       for (i = 0; i < nbytes; i += AES_BLOCK_SIZE) {
+               memcpy(blk.bytes, iv, AES_BLOCK_SIZE);
+               blk.ctr ^= cpu_to_le64((i / AES_BLOCK_SIZE) + 1);
+               aes_encrypt(&k, blk.bytes, blk.bytes);
+               xor(&dst[i], blk.bytes, &src[i], MIN(AES_BLOCK_SIZE, nbytes - i));
+       }
+}
+
+/*
+ * Reference: "Length-preserving encryption with HCTR2"
+ *     https://ia.cr/2021/1441
+ */
+
+#define HCTR2_IV_SIZE 32
+static void hctr2_hash_iv(const u8 hbar[POLYVAL_KEY_SIZE],
+                         const u8 iv[HCTR2_IV_SIZE], size_t msglen,
+                         u8 digest[POLYVAL_BLOCK_SIZE])
+{
+       le128 tweaklen_blk = {
+               .lo = cpu_to_le64(HCTR2_IV_SIZE * 8 * 2 + 2 +
+                                 (msglen % AES_BLOCK_SIZE != 0))
+       };
+
+       memset(digest, 0, POLYVAL_BLOCK_SIZE);
+       polyval_update(hbar, (u8 *)&tweaklen_blk, POLYVAL_BLOCK_SIZE, digest);
+       polyval_update(hbar, iv, HCTR2_IV_SIZE, digest);
+}
+
+static void hctr2_hash_message(const u8 hbar[POLYVAL_KEY_SIZE],
+                              const u8 *msg, size_t msglen,
+                              u8 digest[POLYVAL_BLOCK_SIZE])
+{
+       size_t remainder = msglen % AES_BLOCK_SIZE;
+       u8 padded_block[POLYVAL_BLOCK_SIZE] = {0};
+
+       polyval_update(hbar, msg, msglen - remainder, digest);
+       if (remainder) {
+               memcpy(padded_block, &msg[msglen - remainder], remainder);
+               padded_block[remainder] = 1;
+               polyval_update(hbar, padded_block, POLYVAL_BLOCK_SIZE, digest);
+       }
+}
+
+static void aes_256_hctr2_crypt(const u8 key[AES_256_KEY_SIZE],
+                               const u8 iv[HCTR2_IV_SIZE], const u8 *src,
+                               u8 *dst, size_t nbytes, bool decrypting)
+{
+       struct aes_key k;
+       u8 hbar[AES_BLOCK_SIZE] = {0};
+       u8 L[AES_BLOCK_SIZE] = {1};
+       size_t bulk_bytes = nbytes - AES_BLOCK_SIZE;
+       u8 digest[POLYVAL_BLOCK_SIZE];
+       const u8 *M = src;
+       const u8 *N = src + AES_BLOCK_SIZE;
+       u8 MM[AES_BLOCK_SIZE];
+       u8 UU[AES_BLOCK_SIZE];
+       u8 S[AES_BLOCK_SIZE];
+       u8 *U = dst;
+       u8 *V = dst + AES_BLOCK_SIZE;
+
+       ASSERT(nbytes >= AES_BLOCK_SIZE);
+       aes_setkey(&k, key, AES_256_KEY_SIZE);
+
+       aes_encrypt(&k, hbar, hbar);
+       aes_encrypt(&k, L, L);
+
+       hctr2_hash_iv(hbar, iv, bulk_bytes, digest);
+       hctr2_hash_message(hbar, N, bulk_bytes, digest);
+
+       xor(MM, M, digest, AES_BLOCK_SIZE);
+
+       if (decrypting)
+               aes_decrypt(&k, MM, UU);
+       else
+               aes_encrypt(&k, MM, UU);
+
+       xor(S, MM, UU, AES_BLOCK_SIZE);
+       xor(S, L, S, AES_BLOCK_SIZE);
+
+       aes_256_xctr_crypt(key, S, N, V, bulk_bytes);
+
+       hctr2_hash_iv(hbar, iv, bulk_bytes, digest);
+       hctr2_hash_message(hbar, V, bulk_bytes, digest);
+
+       xor(U, UU, digest, AES_BLOCK_SIZE);
+}
+
+static void aes_256_hctr2_encrypt(const u8 key[AES_256_KEY_SIZE],
+                                 const u8 iv[HCTR2_IV_SIZE], const u8 *src,
+                                 u8 *dst, size_t nbytes)
+{
+       aes_256_hctr2_crypt(key, iv, src, dst, nbytes, false);
+}
+
+static void aes_256_hctr2_decrypt(const u8 key[AES_256_KEY_SIZE],
+                                 const u8 iv[HCTR2_IV_SIZE], const u8 *src,
+                                 u8 *dst, size_t nbytes)
+{
+       aes_256_hctr2_crypt(key, iv, src, dst, nbytes, true);
+}
+
+#ifdef ENABLE_ALG_TESTS
+#include <linux/if_alg.h>
+#include <sys/socket.h>
+static void test_aes_256_hctr2(void)
+{
+       int algfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
+       struct sockaddr_alg addr = {
+               .salg_type = "skcipher",
+               .salg_name = "hctr2(aes)",
+       };
+       unsigned long num_tests = NUM_ALG_TEST_ITERATIONS;
+
+       if (algfd < 0)
+               die_errno("can't create AF_ALG socket");
+       if (bind(algfd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
+               die_errno("can't bind AF_ALG socket to HCTR2 algorithm");
+
+       while (num_tests--) {
+               u8 key[AES_256_KEY_SIZE];
+               u8 iv[HCTR2_IV_SIZE];
+               u8 ptext[4096];
+               u8 ctext[sizeof(ptext)];
+               u8 ref_ctext[sizeof(ptext)];
+               u8 decrypted[sizeof(ptext)];
+               const size_t datalen = 16 + (rand() % (sizeof(ptext) - 15));
+
+               rand_bytes(key, sizeof(key));
+               rand_bytes(iv, sizeof(iv));
+               rand_bytes(ptext, datalen);
+
+               aes_256_hctr2_encrypt(key, iv, ptext, ctext, datalen);
+               af_alg_crypt(algfd, ALG_OP_ENCRYPT, key, sizeof(key),
+                            iv, sizeof(iv), ptext, ref_ctext, datalen);
+               ASSERT(memcmp(ctext, ref_ctext, datalen) == 0);
+
+               aes_256_hctr2_decrypt(key, iv, ctext, decrypted, datalen);
+               ASSERT(memcmp(ptext, decrypted, datalen) == 0);
+       }
+       close(algfd);
+}
+#endif /* ENABLE_ALG_TESTS */
+
 /*----------------------------------------------------------------------------*
  *                           XChaCha12 stream cipher                          *
  *----------------------------------------------------------------------------*/
@@ -1503,58 +1823,6 @@ static void adiantum_decrypt(const u8 key[ADIANTUM_KEY_SIZE],
 }
 
 #ifdef ENABLE_ALG_TESTS
-#include <linux/if_alg.h>
-#include <sys/socket.h>
-#define SOL_ALG 279
-static void af_alg_crypt(int algfd, int op, const u8 *key, size_t keylen,
-                        const u8 *iv, size_t ivlen,
-                        const u8 *src, u8 *dst, size_t datalen)
-{
-       size_t controllen = CMSG_SPACE(sizeof(int)) +
-                           CMSG_SPACE(sizeof(struct af_alg_iv) + ivlen);
-       u8 *control = xmalloc(controllen);
-       struct iovec iov = { .iov_base = (u8 *)src, .iov_len = datalen };
-       struct msghdr msg = {
-               .msg_iov = &iov,
-               .msg_iovlen = 1,
-               .msg_control = control,
-               .msg_controllen = controllen,
-       };
-       struct cmsghdr *cmsg;
-       struct af_alg_iv *algiv;
-       int reqfd;
-
-       memset(control, 0, controllen);
-
-       cmsg = CMSG_FIRSTHDR(&msg);
-       cmsg->cmsg_len = CMSG_LEN(sizeof(int));
-       cmsg->cmsg_level = SOL_ALG;
-       cmsg->cmsg_type = ALG_SET_OP;
-       *(int *)CMSG_DATA(cmsg) = op;
-
-       cmsg = CMSG_NXTHDR(&msg, cmsg);
-       cmsg->cmsg_len = CMSG_LEN(sizeof(struct af_alg_iv) + ivlen);
-       cmsg->cmsg_level = SOL_ALG;
-       cmsg->cmsg_type = ALG_SET_IV;
-       algiv = (struct af_alg_iv *)CMSG_DATA(cmsg);
-       algiv->ivlen = ivlen;
-       memcpy(algiv->iv, iv, ivlen);
-
-       if (setsockopt(algfd, SOL_ALG, ALG_SET_KEY, key, keylen) != 0)
-               die_errno("can't set key on AF_ALG socket");
-
-       reqfd = accept(algfd, NULL, NULL);
-       if (reqfd < 0)
-               die_errno("can't accept() AF_ALG socket");
-       if (sendmsg(reqfd, &msg, 0) != datalen)
-               die_errno("can't sendmsg() AF_ALG request socket");
-       if (xread(reqfd, dst, datalen) != datalen)
-               die("short read from AF_ALG request socket");
-       close(reqfd);
-
-       free(control);
-}
-
 static void test_adiantum(void)
 {
        int algfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
@@ -1678,6 +1946,12 @@ static const struct fscrypt_cipher {
                .decrypt = aes_128_cts_cbc_decrypt,
                .keysize = AES_128_KEY_SIZE,
                .min_input_size = AES_BLOCK_SIZE,
+       }, {
+               .name = "AES-256-HCTR2",
+               .encrypt = aes_256_hctr2_encrypt,
+               .decrypt = aes_256_hctr2_decrypt,
+               .keysize = AES_256_KEY_SIZE,
+               .min_input_size = AES_BLOCK_SIZE,
        }, {
                .name = "Adiantum",
                .encrypt = adiantum_encrypt,
@@ -1701,8 +1975,8 @@ static const struct fscrypt_cipher *find_fscrypt_cipher(const char *name)
 union fscrypt_iv {
        /* usual IV format */
        struct {
-               /* logical block number within the file */
-               __le64 block_number;
+               /* data unit index within the file */
+               __le64 data_unit_index;
 
                /* per-file nonce; only set in DIRECT_KEY mode */
                u8 nonce[FILE_NONCE_SIZE];
@@ -1710,11 +1984,11 @@ union fscrypt_iv {
        /* IV format for IV_INO_LBLK_* modes */
        struct {
                /*
-                * IV_INO_LBLK_64: logical block number within the file
-                * IV_INO_LBLK_32: hashed inode number + logical block number
-                *                 within the file, mod 2^32
+                * IV_INO_LBLK_64: data unit index within the file
+                * IV_INO_LBLK_32: hashed inode number + data unit index within
+                *                 the file, mod 2^32
                 */
-               __le32 block_number32;
+               __le32 data_unit_index32;
 
                /* IV_INO_LBLK_64: inode number */
                __le32 inode_number;
@@ -1725,18 +1999,19 @@ union fscrypt_iv {
 
 static void crypt_loop(const struct fscrypt_cipher *cipher, const u8 *key,
                       union fscrypt_iv *iv, bool decrypting,
-                      size_t block_size, size_t padding, bool is_bnum_32bit)
+                      size_t data_unit_size, size_t padding,
+                      bool is_data_unit_index_32bit)
 {
-       u8 *buf = xmalloc(block_size);
+       u8 *buf = xmalloc(data_unit_size);
        size_t res;
 
-       while ((res = xread(STDIN_FILENO, buf, block_size)) > 0) {
-               size_t crypt_len = block_size;
+       while ((res = xread(STDIN_FILENO, buf, data_unit_size)) > 0) {
+               size_t crypt_len = data_unit_size;
 
                if (padding > 0) {
                        crypt_len = MAX(res, cipher->min_input_size);
                        crypt_len = ROUND_UP(crypt_len, padding);
-                       crypt_len = MIN(crypt_len, block_size);
+                       crypt_len = MIN(crypt_len, data_unit_size);
                }
                ASSERT(crypt_len >= res);
                memset(&buf[res], 0, crypt_len - res);
@@ -1748,12 +2023,12 @@ static void crypt_loop(const struct fscrypt_cipher *cipher, const u8 *key,
 
                full_write(STDOUT_FILENO, buf, crypt_len);
 
-               if (is_bnum_32bit)
-                       iv->block_number32 = cpu_to_le32(
-                                       le32_to_cpu(iv->block_number32) + 1);
+               if (is_data_unit_index_32bit)
+                       iv->data_unit_index32 = cpu_to_le32(
+                               le32_to_cpu(iv->data_unit_index32) + 1);
                else
-                       iv->block_number = cpu_to_le64(
-                                       le64_to_cpu(iv->block_number) + 1);
+                       iv->data_unit_index = cpu_to_le64(
+                               le64_to_cpu(iv->data_unit_index) + 1);
        }
        free(buf);
 }
@@ -1796,7 +2071,7 @@ struct key_and_iv_params {
        bool direct_key;
        bool iv_ino_lblk_64;
        bool iv_ino_lblk_32;
-       u64 block_number;
+       u64 data_unit_index;
        u64 inode_number;
        u8 fs_uuid[UUID_SIZE];
        bool fs_uuid_specified;
@@ -1904,26 +2179,26 @@ static void generate_iv(const struct key_and_iv_params *params,
        if (params->direct_key) {
                if (!params->file_nonce_specified)
                        die("--direct-key requires --file-nonce");
-               iv->block_number = cpu_to_le64(params->block_number);
+               iv->data_unit_index = cpu_to_le64(params->data_unit_index);
                memcpy(iv->nonce, params->file_nonce, FILE_NONCE_SIZE);
        } else if (params->iv_ino_lblk_64) {
-               if (params->block_number > UINT32_MAX)
-                       die("iv-ino-lblk-64 can't use --block-number > UINT32_MAX");
+               if (params->data_unit_index > UINT32_MAX)
+                       die("iv-ino-lblk-64 can't use --data-unit-index > UINT32_MAX");
                if (params->inode_number == 0)
                        die("iv-ino-lblk-64 requires --inode-number");
                if (params->inode_number > UINT32_MAX)
                        die("iv-ino-lblk-64 can't use --inode-number > UINT32_MAX");
-               iv->block_number32 = cpu_to_le32(params->block_number);
+               iv->data_unit_index32 = cpu_to_le32(params->data_unit_index);
                iv->inode_number = cpu_to_le32(params->inode_number);
        } else if (params->iv_ino_lblk_32) {
-               if (params->block_number > UINT32_MAX)
-                       die("iv-ino-lblk-32 can't use --block-number > UINT32_MAX");
+               if (params->data_unit_index > UINT32_MAX)
+                       die("iv-ino-lblk-32 can't use --data-unit-index > UINT32_MAX");
                if (params->inode_number == 0)
                        die("iv-ino-lblk-32 requires --inode-number");
-               iv->block_number32 = cpu_to_le32(hash_inode_number(params) +
-                                                params->block_number);
+               iv->data_unit_index32 = cpu_to_le32(hash_inode_number(params) +
+                                                   params->data_unit_index);
        } else {
-               iv->block_number = cpu_to_le64(params->block_number);
+               iv->data_unit_index = cpu_to_le64(params->data_unit_index);
        }
 }
 
@@ -1978,8 +2253,8 @@ static void parse_master_key(const char *arg, struct key_and_iv_params *params)
 }
 
 enum {
-       OPT_BLOCK_NUMBER,
-       OPT_BLOCK_SIZE,
+       OPT_DATA_UNIT_INDEX,
+       OPT_DATA_UNIT_SIZE,
        OPT_DECRYPT,
        OPT_DIRECT_KEY,
        OPT_DUMP_KEY_IDENTIFIER,
@@ -1995,8 +2270,8 @@ enum {
 };
 
 static const struct option longopts[] = {
-       { "block-number",    required_argument, NULL, OPT_BLOCK_NUMBER },
-       { "block-size",      required_argument, NULL, OPT_BLOCK_SIZE },
+       { "data-unit-index", required_argument, NULL, OPT_DATA_UNIT_INDEX },
+       { "data-unit-size",  required_argument, NULL, OPT_DATA_UNIT_SIZE },
        { "decrypt",         no_argument,       NULL, OPT_DECRYPT },
        { "direct-key",      no_argument,       NULL, OPT_DIRECT_KEY },
        { "dump-key-identifier", no_argument,   NULL, OPT_DUMP_KEY_IDENTIFIER },
@@ -2014,7 +2289,7 @@ static const struct option longopts[] = {
 
 int main(int argc, char *argv[])
 {
-       size_t block_size = 4096;
+       size_t data_unit_size = 4096;
        bool decrypting = false;
        bool dump_key_identifier = false;
        struct key_and_iv_params params;
@@ -2036,21 +2311,22 @@ int main(int argc, char *argv[])
        test_aes_256_xts();
        test_aes_256_cts_cbc();
        test_adiantum();
+       test_aes_256_hctr2();
 #endif
 
        while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
                switch (c) {
-               case OPT_BLOCK_NUMBER:
+               case OPT_DATA_UNIT_INDEX:
                        errno = 0;
-                       params.block_number = strtoull(optarg, &tmp, 10);
+                       params.data_unit_index = strtoull(optarg, &tmp, 10);
                        if (*tmp || errno)
-                               die("Invalid block number: %s", optarg);
+                               die("Invalid data unit index: %s", optarg);
                        break;
-               case OPT_BLOCK_SIZE:
+               case OPT_DATA_UNIT_SIZE:
                        errno = 0;
-                       block_size = strtoul(optarg, &tmp, 10);
-                       if (block_size <= 0 || *tmp || errno)
-                               die("Invalid block size: %s", optarg);
+                       data_unit_size = strtoul(optarg, &tmp, 10);
+                       if (data_unit_size <= 0 || *tmp || errno)
+                               die("Invalid data unit size: %s", optarg);
                        break;
                case OPT_DECRYPT:
                        decrypting = true;
@@ -2126,9 +2402,9 @@ int main(int argc, char *argv[])
        if (cipher == NULL)
                die("Unknown cipher: %s", argv[0]);
 
-       if (block_size < cipher->min_input_size)
-               die("Block size of %zu bytes is too small for cipher %s",
-                   block_size, cipher->name);
+       if (data_unit_size < cipher->min_input_size)
+               die("Data unit size of %zu bytes is too small for cipher %s",
+                   data_unit_size, cipher->name);
 
        parse_master_key(argv[1], &params);
 
@@ -2137,7 +2413,7 @@ int main(int argc, char *argv[])
 
        get_key_and_iv(&params, real_key, cipher->keysize, &iv);
 
-       crypt_loop(cipher, real_key, &iv, decrypting, block_size, padding,
+       crypt_loop(cipher, real_key, &iv, decrypting, data_unit_size, padding,
                   params.iv_ino_lblk_64 || params.iv_ino_lblk_32);
        return 0;
 }
index e4b9e081144a9401a84c81aea81ed1f3e61413c6..4f6dc643dd12fa49a385aa047de1d51ba9de483c 100644 (file)
@@ -88,11 +88,16 @@ static void check_buffer(uchar *buf, int loop, int child, int fnum, int ofs)
 static void create_file(const char *dir, int loop, int child, int fnum)
 {
        char *buf;
-       int size, fd;
+       int size, fd, ret;
        char fname[1024];
 
        buf = x_malloc(block_size);
-       sprintf(fname, "%s/file%d", dir, fnum);
+       ret = snprintf(fname, sizeof(fname), "%s/file%d", dir, fnum);
+       if (ret < 0 || ret >= sizeof(fname)) {
+               fprintf(stderr,"file path '%s' too long %d\n", dir, ret);
+               exit(1);
+       }
+
        fd = open(fname, O_RDWR|O_CREAT|O_TRUNC | (use_sync?O_SYNC:0), 0644);
        if (fd == -1) {
                perror(fname);
@@ -158,12 +163,16 @@ bozo!
 static void check_file(const char *dir, int loop, int child, int fnum)
 {
        uchar *buf;
-       int size, fd;
+       int size, fd, ret;
        char fname[1024];
 
        buf = x_malloc(block_size);
 
-       sprintf(fname, "%s/file%d", dir, fnum);
+       ret = snprintf(fname, sizeof(fname), "%s/file%d", dir, fnum);
+       if (ret < 0 || ret >= sizeof(fname)) {
+               fprintf(stderr,"file path is '%s' too long %d\n", dir, ret);
+               exit(1);
+       }
        fd = open(fname, O_RDONLY);
        if (fd == -1) {
                perror(fname);
index b44070993c0f45fa65c20944160ce5ffdcea3e45..4f92308d6c22068bc1cc909fedeff072c7d8af60 100644 (file)
 #include <sys/mman.h>
 #endif
 
+#ifndef HAVE_XFS_IOC_EXCHANGE_RANGE
+# include "fiexchange.h"
+#endif
+
 static inline unsigned long long
 rounddown_64(unsigned long long x, unsigned int y)
 {
index 82f9d9781e06396cc10462a95ab989d8c775d86c..161d9f7983debd7409c3ae1f6d1e456957453983 100644 (file)
@@ -20,6 +20,7 @@
 #include <string.h>
 #include <errno.h>
 #include <err.h>
+#include <getopt.h>
 
 char *filename;
 unsigned int page_size;
@@ -109,11 +110,29 @@ static ssize_t do_write(int fd, const void *buf, size_t count, off_t offset)
        return count2;
 }
 
+static void usage(const char *argv0)
+{
+       fprintf(stderr, "Usage: %s [-2] {filename}\n", argv0);
+       exit(2);
+}
+
 int main(int argc, char *argv[])
 {
-       if (argc != 2)
-               errx(1, "no test filename argument given");
-       filename = argv[1];
+       int opt, opt_2 = 0;
+
+       while ((opt = getopt(argc, argv, "2")) != -1) {
+               switch(opt) {
+               case '2':
+                       opt_2 = 1;
+                       break;
+               default:
+                       usage(argv[0]);
+               }
+       }
+
+       if (optind + 1 != argc)
+               usage(argv[0]);
+       filename = argv[optind];
 
        page_size = ret = sysconf(_SC_PAGE_SIZE);
        if (ret == -1)
@@ -179,6 +198,14 @@ int main(int argc, char *argv[])
                errx(1, "pread (D_DIRECT) from hole is broken");
        done();
 
+       if (opt_2) {
+               init('f', O_RDWR | O_DIRECT);
+               ret = do_write(fd, addr + page_size, page_size, page_size);
+               if (ret != page_size)
+                       err(1, "pwrite %s (O_DIRECT): %ld != %u", filename, ret, page_size);
+               done();
+       }
+
        if (unlink(filename))
                err(1, "unlink %s", filename);
 
diff --git a/src/popattr.py b/src/popattr.py
new file mode 100755 (executable)
index 0000000..397ced9
--- /dev/null
@@ -0,0 +1,62 @@
+#!/usr/bin/python3
+
+# Copyright (c) 2023 Oracle.  All rights reserved.
+# SPDX-License-Identifier: GPL-2.0
+#
+# Create a bunch of xattrs in a file.
+
+import argparse
+import sys
+import os
+
+parser = argparse.ArgumentParser(description = 'Mass create xattrs in a file')
+parser.add_argument(
+       '--file', required = True, type = str, help = 'manipulate this file')
+parser.add_argument(
+       '--start', type = int, default = 0,
+       help = 'create xattrs starting with this number')
+parser.add_argument(
+       '--incr', type = int, default = 1,
+       help = 'increment attr number by this much')
+parser.add_argument(
+       '--end', type = int, default = 1000,
+       help = 'stop at this attr number')
+parser.add_argument(
+       '--remove', dest = 'remove', action = 'store_true',
+       help = 'remove instead of creating')
+parser.add_argument(
+       '--format', type = str, default = '%08d',
+       help = 'printf formatting string for attr name')
+parser.add_argument(
+       '--verbose', dest = 'verbose', action = 'store_true',
+       help = 'verbose output')
+
+args = parser.parse_args()
+
+fmtstring = "user.%s" % args.format
+
+# If we are passed a regular file, open it as a proper file descriptor and
+# pass that around for speed.  Otherwise, we pass the path.
+fp = None
+try:
+       fp = open(args.file, 'r')
+       fd = fp.fileno()
+       os.listxattr(fd)
+       if args.verbose:
+               print("using fd calls")
+except:
+       if args.verbose:
+               print("using path calls")
+       fd = args.file
+
+for i in range(args.start, args.end + 1, args.incr):
+       fname = fmtstring % i
+
+       if args.remove:
+               if args.verbose:
+                       print("removexattr %s" % fname)
+               os.removexattr(fd, fname)
+       else:
+               if args.verbose:
+                       print("setxattr %s" % fname)
+               os.setxattr(fd, fname, b'abcdefgh')
diff --git a/src/popdir.pl b/src/popdir.pl
new file mode 100755 (executable)
index 0000000..e89095a
--- /dev/null
@@ -0,0 +1,77 @@
+#!/usr/bin/perl -w
+
+# Copyright (c) 2023 Oracle.  All rights reserved.
+# SPDX-License-Identifier: GPL-2.0
+#
+# Create a bunch of files and subdirs in a directory.
+
+use Getopt::Long;
+use File::Basename;
+
+$progname=$0;
+GetOptions("start=i" => \$start,
+          "end=i" => \$end,
+          "file-pct=i" => \$file_pct,
+          "incr=i" => \$incr,
+          "format=s" => \$format,
+          "dir=s" => \$dir,
+          "remove!" => \$remove,
+          "help!" => \$help,
+          "verbose!" => \$verbose);
+
+
+# check/remove output directory, get filesystem info
+if (defined $help) {
+  # newline at end of die message suppresses line number
+  print STDERR <<"EOF";
+Usage: $progname [options]
+Options:
+  --dir             chdir here before starting
+  --start=num       create names starting with this number (0)
+  --incr=num        increment file number by this much (1)
+  --end=num         stop at this file number (100)
+  --file-pct        create this percentage of regular files (90 percent)
+  --remove          remove instead of creating
+  --format=str      printf formatting string for file name ("%08d")
+  --verbose         verbose output
+  --help            this help screen
+EOF
+  exit(1) unless defined $help;
+  # otherwise...
+  exit(0);
+}
+
+if (defined $dir) {
+       chdir($dir) or die("chdir $dir");
+}
+$start = 0 if (!defined $start);
+$end = 100 if (!defined $end);
+$file_pct = 90 if (!defined $file_pct);
+$format = "%08d" if (!defined $format);
+$incr = 1 if (!defined $incr);
+
+if ($file_pct < 0) {
+       $file_pct = 0;
+} elsif ($file_pct > 100) {
+       $file_pct = 100;
+}
+
+for ($i = $start; $i <= $end; $i += $incr) {
+       $fname = sprintf($format, $i);
+
+       if ($remove) {
+               $verbose && print "rm $fname\n";
+               unlink($fname) or rmdir($fname) or die("unlink $fname");
+       } elsif (($i % 100) < $file_pct) {
+               # create a file
+               $verbose && print "touch $fname\n";
+               open(DONTCARE, ">$fname") or die("touch $fname");
+               close(DONTCARE);
+       } else {
+               # create a subdir
+               $verbose && print "mkdir $fname\n";
+               mkdir($fname, 0755) or die("mkdir $fname");
+       }
+}
+
+exit(0);
diff --git a/src/readdir-while-renames.c b/src/readdir-while-renames.c
new file mode 100644 (file)
index 0000000..b99c049
--- /dev/null
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 SUSE Linux Products GmbH.  All Rights Reserved.
+ */
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/* Number of files we add to the test directory. */
+#define NUM_FILES 5000
+
+int main(int argc, char *argv[])
+{
+       struct dirent *entry;
+       DIR *dir = NULL;
+       char *dir_path = NULL;
+       int dentry_count = 0;
+       int ret = 0;
+       int i;
+
+       if (argc != 2) {
+               fprintf(stderr, "Usage:  %s <directory>\n", argv[0]);
+               ret = 1;
+               goto out;
+       }
+
+       dir_path = malloc(strlen(argv[1]) + strlen("/testdir") + 1);
+       if (!dir_path) {
+               fprintf(stderr, "malloc failure\n");
+               ret = ENOMEM;
+               goto out;
+       }
+       i = strlen(argv[1]);
+       memcpy(dir_path, argv[1], i);
+       memcpy(dir_path + i, "/testdir", strlen("/testdir"));
+       dir_path[i + strlen("/testdir")] = '\0';
+
+       ret = mkdir(dir_path, 0700);
+       if (ret == -1) {
+               fprintf(stderr, "Failed to create test directory: %d\n", errno);
+               ret = errno;
+               goto out;
+       }
+
+       ret = chdir(dir_path);
+       if (ret == -1) {
+               fprintf(stderr, "Failed to chdir to the test directory: %d\n", errno);
+               ret = errno;
+               goto out;
+       }
+
+       /* Now create all files inside the directory. */
+       for (i = 1; i <= NUM_FILES; i++) {
+               /* 8 characters is enough for NUM_FILES name plus '\0'. */
+               char file_name[8];
+               FILE *f;
+
+               ret = snprintf(file_name, sizeof(file_name), "%d", i);
+               if (ret < 0 || ret >= sizeof(file_name)) {
+                       fprintf(stderr, "Buffer to small for filename %i\n", i);
+                       ret = EOVERFLOW;
+                       goto out;
+               }
+               f = fopen(file_name, "w");
+               if (f == NULL) {
+                       fprintf(stderr, "Failed to create file number %d: %d\n",
+                               i, errno);
+                       ret = errno;
+                       goto out;
+               }
+               fclose(f);
+       }
+
+       dir = opendir(dir_path);
+       if (dir == NULL) {
+               fprintf(stderr, "Failed to open directory: %d\n", errno);
+               ret = errno;
+               goto out;
+       }
+
+       /*
+        * readdir(3) returns NULL when it reaches the end of the directory or
+        * when an error happens, so reset errno to 0 to distinguish between
+        * both cases later.
+        */
+       errno = 0;
+       while ((entry = readdir(dir)) != NULL) {
+               dentry_count++;
+               /*
+                * The actual number of dentries returned varies per filesystem
+                * implementation. On a 6.7-rc4 kernel, on x86_64 with default
+                * mkfs options, xfs returns 5031 dentries while ext4, f2fs and
+                * btrfs return 5002 (the 5000 files plus "." and ".."). These
+                * variations are fine and valid according to POSIX, as some of
+                * the renames may be visible or not while calling readdir(3).
+                * We only want to check we don't enter into an infinite loop,
+                * so let the maximum number of dentries be 3 * NUM_FILES, which
+                * is very reasonable.
+                */
+               if (dentry_count > 3 * NUM_FILES) {
+                       fprintf(stderr,
+                               "Found too many directory entries (%d)\n",
+                               dentry_count);
+                       ret = 1;
+                       goto out;
+               }
+               /* Can't rename "." and "..", skip them. */
+               if (strcmp(entry->d_name, ".") == 0 ||
+                   strcmp(entry->d_name, "..") == 0)
+                       continue;
+               ret = rename(entry->d_name, "TEMPFILE");
+               if (ret == -1) {
+                       fprintf(stderr,
+                               "Failed to rename '%s' to TEMPFILE: %d\n",
+                               entry->d_name, errno);
+                       ret = errno;
+                       goto out;
+               }
+               ret = rename("TEMPFILE", entry->d_name);
+               if (ret == -1) {
+                       fprintf(stderr,
+                               "Failed to rename TEMPFILE to '%s': %d\n",
+                               entry->d_name, errno);
+                       ret = errno;
+                       goto out;
+               }
+       }
+
+       if (errno) {
+               fprintf(stderr, "Failed to read directory: %d\n", errno);
+               ret = errno;
+               goto out;
+       }
+
+       /* It should return at least NUM_FILES entries +2 (for "." and ".."). */
+       if (dentry_count < NUM_FILES + 2) {
+               fprintf(stderr,
+                       "Found less directory entries than expected (%d but expected %d)\n",
+                       dentry_count, NUM_FILES + 2);
+               ret = 2;
+       }
+out:
+       free(dir_path);
+       if (dir != NULL)
+               closedir(dir);
+
+       return ret;
+}
diff --git a/src/rewinddir-test.c b/src/rewinddir-test.c
new file mode 100644 (file)
index 0000000..9f7505a
--- /dev/null
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 SUSE Linux Products GmbH.  All Rights Reserved.
+ */
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/*
+ * Number of files we add to the test directory after calling opendir(3) and
+ * before calling rewinddir(3).
+ */
+#define NUM_FILES 10000
+
+int main(int argc, char *argv[])
+{
+       int file_counters[NUM_FILES] = { 0 };
+       int dot_count = 0;
+       int dot_dot_count = 0;
+       struct dirent *entry;
+       DIR *dir = NULL;
+       char *dir_path = NULL;
+       char *file_path = NULL;
+       int ret = 0;
+       int i;
+
+       if (argc != 2) {
+               fprintf(stderr, "Usage:  %s <directory>\n", argv[0]);
+               ret = 1;
+               goto out;
+       }
+
+       dir_path = malloc(strlen(argv[1]) + strlen("/testdir") + 1);
+       if (!dir_path) {
+               fprintf(stderr, "malloc failure\n");
+               ret = ENOMEM;
+               goto out;
+       }
+       i = strlen(argv[1]);
+       memcpy(dir_path, argv[1], i);
+       memcpy(dir_path + i, "/testdir", strlen("/testdir"));
+       dir_path[i + strlen("/testdir")] = '\0';
+
+       /* More than enough to contain any full file path. */
+       file_path = malloc(strlen(dir_path) + 12);
+       if (!file_path) {
+               fprintf(stderr, "malloc failure\n");
+               ret = ENOMEM;
+               goto out;
+       }
+
+       ret = mkdir(dir_path, 0700);
+       if (ret == -1) {
+               fprintf(stderr, "Failed to create test directory: %d\n", errno);
+               ret = errno;
+               goto out;
+       }
+
+       /* Open the directory first. */
+       dir = opendir(dir_path);
+       if (dir == NULL) {
+               fprintf(stderr, "Failed to open directory: %d\n", errno);
+               ret = errno;
+               goto out;
+       }
+
+       /*
+        * Now create all files inside the directory.
+        * File names go from 1 to NUM_FILES, 0 is unused as it's the return
+        * value for atoi(3) when an error happens.
+        */
+       for (i = 1; i <= NUM_FILES; i++) {
+               FILE *f;
+
+               sprintf(file_path, "%s/%d", dir_path, i);
+               f = fopen(file_path, "w");
+               if (f == NULL) {
+                       fprintf(stderr, "Failed to create file number %d: %d\n",
+                               i, errno);
+                       ret = errno;
+                       goto out;
+               }
+               fclose(f);
+       }
+
+       /*
+        * Rewind the directory and read it.
+        * POSIX requires that after a rewind, any new names added to the
+        * directory after the openddir(3) call and before the rewinddir(3)
+        * call, must be returned by readdir(3) calls
+        */
+       rewinddir(dir);
+
+       /*
+        * readdir(3) returns NULL when it reaches the end of the directory or
+        * when an error happens, so reset errno to 0 to distinguish between
+        * both cases later.
+        */
+       errno = 0;
+       while ((entry = readdir(dir)) != NULL) {
+               if (strcmp(entry->d_name, ".") == 0) {
+                       dot_count++;
+                       continue;
+               }
+               if (strcmp(entry->d_name, "..") == 0) {
+                       dot_dot_count++;
+                       continue;
+               }
+               i = atoi(entry->d_name);
+               if (i == 0) {
+                       fprintf(stderr,
+                               "Failed to parse name '%s' to integer: %d\n",
+                               entry->d_name, errno);
+                       ret = errno;
+                       goto out;
+               }
+               /* File names go from 1 to NUM_FILES, so subtract 1. */
+               file_counters[i - 1]++;
+       }
+
+       if (errno) {
+               fprintf(stderr, "Failed to read directory: %d\n", errno);
+               ret = errno;
+               goto out;
+       }
+
+       /*
+        * Now check that the readdir() calls return every single file name
+        * and without repeating any of them. If any name is missing or
+        * repeated, don't exit immediatelly, so that we print a message for
+        * all missing or repeated names.
+        */
+       for (i = 0; i < NUM_FILES; i++) {
+               if (file_counters[i] != 1) {
+                       fprintf(stderr, "File name %d appeared %d times\n",
+                               i + 1,  file_counters[i]);
+                       ret = EINVAL;
+               }
+       }
+       if (dot_count != 1) {
+               fprintf(stderr, "File name . appeared %d times\n", dot_count);
+               ret = EINVAL;
+       }
+       if (dot_dot_count != 1) {
+               fprintf(stderr, "File name .. appeared %d times\n", dot_dot_count);
+               ret = EINVAL;
+       }
+out:
+       free(dir_path);
+       free(file_path);
+       if (dir != NULL)
+               closedir(dir);
+
+       return ret;
+}
index 76587b7f13197d0424183a79434666e7bc0caa1f..48b3ccc0d5c6c6eeb4ce35cbffad8faeb3a4b782 100644 (file)
@@ -40,12 +40,38 @@ static void get_file_system(int fd)
        }
 }
 
+/* Compute the file allocation unit size for an XFS file. */
+static int detect_xfs_alloc_unit(int fd)
+{
+       struct fsxattr fsx;
+       struct xfs_fsop_geom fsgeom;
+       int ret;
+
+       ret = ioctl(fd, XFS_IOC_FSGEOMETRY, &fsgeom);
+       if (ret)
+               return -1;
+
+       ret = ioctl(fd, XFS_IOC_FSGETXATTR, &fsx);
+       if (ret)
+               return -1;
+
+       alloc_size = fsgeom.blocksize;
+       if (fsx.fsx_xflags & XFS_XFLAG_REALTIME)
+               alloc_size *= fsgeom.rtextsize;
+
+       return 0;
+}
+
 static int get_io_sizes(int fd)
 {
        off_t pos = 0, offset = 1;
        struct stat buf;
        int shift, ret;
 
+       ret = detect_xfs_alloc_unit(fd);
+       if (!ret)
+               goto done;
+
        ret = fstat(fd, &buf);
        if (ret) {
                fprintf(stderr, "  ERROR %d: Failed to find io blocksize\n",
@@ -80,6 +106,7 @@ static int get_io_sizes(int fd)
        if (!shift)
                offset += pos ? 0 : 1;
        alloc_size = offset;
+done:
        fprintf(stdout, "Allocation size: %ld\n", alloc_size);
        return 0;
 
@@ -265,6 +292,41 @@ out:
        return ret;
 }
 
+/*
+ * Test seeking for data on a 1 byte file, both when there's delalloc and also
+ * after delalloc is flushed.
+ */
+static int test22(int fd, int testnum)
+{
+       const char buf = 'X';
+       int ret;
+
+       ret = do_pwrite(fd, &buf, 1, 0);
+       if (ret)
+               return ret;
+
+       /*
+        * Our file as a size of 1 byte and that byte is in delalloc. Seeking
+        * for data, with a start offset of 0, should return file offset 0.
+        */
+       ret = do_lseek(testnum, 1, fd, 1, SEEK_DATA, 0, 0);
+       if (ret)
+               return ret;
+
+       /* Flush all delalloc. */
+       ret = fsync(fd);
+       if (ret) {
+               fprintf(stderr, "fsync failed: %s (%d)\n", strerror(errno), errno);
+               return ret;
+       }
+
+       /*
+        * We should get the same result we got when we had delalloc, 0 is the
+        * offset with data.
+        */
+       return do_lseek(testnum, 2, fd, 1, SEEK_DATA, 0, 0);
+}
+
 /*
  * Make sure hole size is properly reported when punched in the middle of a file
  */
@@ -1104,6 +1166,7 @@ struct testrec seek_tests[] = {
        { 19, test19, "Test file SEEK_DATA from middle of a large hole" },
        { 20, test20, "Test file SEEK_DATA from middle of a huge hole" },
        { 21, test21, "Test file SEEK_HOLE that was created by PUNCH_HOLE" },
+       { 22, test22, "Test a 1 byte file" },
 };
 
 static int run_test(struct testrec *tr)
diff --git a/src/soak_duration.awk b/src/soak_duration.awk
new file mode 100644 (file)
index 0000000..6c38d09
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/awk
+#
+# Convert time interval specifications with suffixes to an integer number of
+# seconds.
+{
+       nr = $1;
+       if ($2 == "" || $2 ~ /s/)       # seconds
+               ;
+       else if ($2 ~ /m/)              # minutes
+               nr *= 60;
+       else if ($2 ~ /h/)              # hours
+               nr *= 3600;
+       else if ($2 ~ /d/)              # days
+               nr *= 86400;
+       else if ($2 ~ /w/)              # weeks
+               nr *= 604800;
+       else {
+               printf("%s: unknown suffix\n", $2);
+               exit 1;
+       }
+
+       printf("%d\n", nr);
+}
index 2f1ba2baaff0713cca65537b6278110612c0ca4a..eb86367380643b118a5d2ab253bba0cc8ca38c16 100644 (file)
 #include <errno.h>
 #include <malloc.h>
 
-#define SECTOR_SIZE 512
-#define BUFFER_SIZE (150 * SECTOR_SIZE)
+unsigned int sector_size;
+unsigned int buffer_size;
 
 void read_from_pipe(int fd, const char *filename, size_t size)
 {
-       char buffer[SECTOR_SIZE];
+       char *buffer;
        size_t sz;
        ssize_t ret;
+       buffer = malloc(buffer_size);
 
        while (size) {
                sz = size;
-               if (sz > sizeof buffer)
-                       sz = sizeof buffer;
+               if (sz > buffer_size)
+                       sz = buffer_size;
                ret = read(fd, buffer, sz);
                if (ret < 0)
                        err(1, "read: %s", filename);
@@ -41,6 +42,7 @@ void read_from_pipe(int fd, const char *filename, size_t size)
                }
                size -= sz;
        }
+       free(buffer);
 }
 
 void do_splice1(int fd, const char *filename, size_t size)
@@ -108,7 +110,7 @@ void do_splice2(int fd, const char *filename, size_t size)
 
 void usage(const char *argv0)
 {
-       fprintf(stderr, "USAGE: %s [-rd] {filename}\n", basename(argv0));
+       fprintf(stderr, "USAGE: %s [-rd] [-s sectorsize] {filename}\n", basename(argv0));
        exit(2);
 }
 
@@ -120,11 +122,22 @@ int main(int argc, char *argv[])
        int opt, open_flags, fd;
        ssize_t ret;
 
+       /*
+        * init default sector_size and buffer_size, might be changed if the -s
+        * option is specified
+        */
+       sector_size = 512;
+       buffer_size = 150 * sector_size;
+
        do_splice = do_splice1;
        open_flags = O_CREAT | O_TRUNC | O_RDWR | O_DIRECT;
 
-       while ((opt = getopt(argc, argv, "rd")) != -1) {
+       while ((opt = getopt(argc, argv, "rds:")) != -1) {
                switch(opt) {
+               case 's':
+                       sector_size = strtol(optarg, NULL, 0);
+                       buffer_size = 150 * sector_size;
+                       break;
                case 'r':
                        do_splice = do_splice2;
                        break;
@@ -140,11 +153,13 @@ int main(int argc, char *argv[])
                usage(argv[0]);
        filename = argv[optind];
 
+       /* force below printf line buffered */
+       setlinebuf(stdout);
        printf("%s reader %s O_DIRECT\n",
                   do_splice == do_splice1 ? "sequential" : "concurrent",
                   (open_flags & O_DIRECT) ? "with" : "without");
 
-       buffer = memalign(SECTOR_SIZE, BUFFER_SIZE);
+       buffer = memalign(sector_size, buffer_size);
        if (buffer == NULL)
                err(1, "memalign");
 
@@ -152,11 +167,11 @@ int main(int argc, char *argv[])
        if (fd == -1)
                err(1, "open: %s", filename);
 
-       memset(buffer, 'x', BUFFER_SIZE);
-       ret = write(fd, buffer, BUFFER_SIZE);
+       memset(buffer, 'x', buffer_size);
+       ret = write(fd, buffer, buffer_size);
        if (ret < 0)
                err(1, "write: %s", filename);
-       if (ret != BUFFER_SIZE) {
+       if (ret != buffer_size) {
                fprintf(stderr, "%s: short write\n", filename);
                exit(1);
        }
@@ -165,7 +180,7 @@ int main(int argc, char *argv[])
        if (ret != 0)
                err(1, "lseek: %s", filename);
 
-       do_splice(fd, filename, BUFFER_SIZE);
+       do_splice(fd, filename, buffer_size);
 
        if (unlink(filename) == -1)
                err(1, "unlink: %s", filename);
index bd33ff6736da3392c53758b3849aba2be286ca61..00b3a23af993ae361983d33be14409243c51b474 100644 (file)
@@ -41,6 +41,7 @@
  */
 static void prepare_pipe(int p[2])
 {
+       unsigned int r = 0;
        if (pipe(p)) {
                perror("pipe failed");
                abort();
@@ -51,7 +52,7 @@ static void prepare_pipe(int p[2])
 
        /* fill the pipe completely; each pipe_buffer will now have
           the PIPE_BUF_FLAG_CAN_MERGE flag */
-       for (unsigned r = pipe_size; r > 0;) {
+       for (r = pipe_size; r > 0;) {
                unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
                write(p[1], buffer, n);
                r -= n;
@@ -59,7 +60,7 @@ static void prepare_pipe(int p[2])
 
        /* drain the pipe, freeing all pipe_buffer instances (but
           leaving the flags initialized) */
-       for (unsigned r = pipe_size; r > 0;) {
+       for (r = pipe_size; r > 0;) {
                unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
                read(p[0], buffer, n);
                r -= n;
index 27255bd024e1066f78d5e3b0194349f250ea875a..e9b5d7feb31f7131435b9000f7da10e6ae90137b 100644 (file)
@@ -30,14 +30,14 @@ int test_getcwd(char *init_cwd)
        return 0;
 }
 
-void do_rename(char *prefix)
+static void do_rename(char *prefix)
 {
        int i = 0;
        int fd;
        char c_name[BUF_SIZE];
        char n_name[BUF_SIZE];
 
-       strncpy(c_name, prefix, BUF_SIZE);
+       strncpy(c_name, prefix, BUF_SIZE - 1);
 
        fd = open(c_name, O_CREAT | O_RDWR, 0644);
        if (fd < 0) {
diff --git a/src/t_mmap_cow_memory_failure.c b/src/t_mmap_cow_memory_failure.c
new file mode 100644 (file)
index 0000000..bb3fd3f
--- /dev/null
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022 Fujitsu Limited.  All Rights Reserved. */
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <semaphore.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <sys/sem.h>
+#include <time.h>
+#include <unistd.h>
+
+sem_t *sem;
+
+void sigbus_handler(int signal)
+{
+       printf("Process is killed by signal: %d\n", signal);
+       sem_post(sem);
+}
+
+void mmap_read_file(char *filename, off_t offset, size_t size)
+{
+       int fd;
+       char *map, *dummy;
+       struct timespec ts;
+
+       fd = open(filename, O_RDWR);
+       map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset);
+       dummy = malloc(size);
+
+       /* make sure page fault happens */
+       memcpy(dummy, map, size);
+
+       /* ready */
+       sem_post(sem);
+
+       usleep(200000);
+
+       clock_gettime(CLOCK_REALTIME, &ts);
+       ts.tv_sec += 3;
+       /* wait for injection done */
+       sem_timedwait(sem, &ts);
+
+       free(dummy);
+       munmap(map, size);
+       close(fd);
+}
+
+void mmap_read_file_then_poison(char *filename, off_t offset, size_t size,
+               off_t poisonOffset, size_t poisonSize)
+{
+       int fd, error;
+       char *map, *dummy;
+
+       /* wait for parent preparation done */
+       sem_wait(sem);
+
+       fd = open(filename, O_RDWR);
+       map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset);
+       dummy = malloc(size);
+
+       /* make sure page fault happens */
+       memcpy(dummy, map, size);
+
+       printf("Inject poison...\n");
+       error = madvise(map + poisonOffset, poisonSize, MADV_HWPOISON);
+       if (error)
+               printf("madvise() has fault: %d, errno: %d\n", error, errno);
+
+       free(dummy);
+       munmap(map, size);
+       close(fd);
+}
+
+int main(int argc, char *argv[])
+{
+       char *pReadFile = NULL, *pPoisonFile = NULL;
+       size_t mmapSize, poisonSize;
+       off_t mmapOffset = 0, poisonOffset = 0;
+       long pagesize = sysconf(_SC_PAGESIZE);
+       int c;
+       pid_t pid;
+
+       if (pagesize < 1) {
+               fprintf(stderr, "sysconf(_SC_PAGESIZE): failed to get page size\n");
+               abort();
+       }
+
+       /* default mmap / poison size, in unit of System Page Size */
+       mmapSize = poisonSize = pagesize;
+
+       while ((c = getopt(argc, argv, "o::s::O::S::R:P:")) != -1) {
+               switch (c) {
+               /* mmap offset */
+               case 'o':
+                       mmapOffset = atoi(optarg) * pagesize;
+                       break;
+               /* mmap size */
+               case 's':
+                       mmapSize = atoi(optarg) * pagesize;
+                       break;
+               /* madvice offset */
+               case 'O':
+                       poisonOffset = atoi(optarg) * pagesize;
+                       break;
+               /* madvice size */
+               case 'S':
+                       poisonSize = atoi(optarg) * pagesize;
+                       break;
+               /* filename for mmap read */
+               case 'R':
+                       pReadFile = optarg;
+                       break;
+               /* filename for poison read */
+               case 'P':
+                       pPoisonFile = optarg;
+                       break;
+               default:
+                       printf("Unknown option: %c\n", c);
+                       exit(1);
+               }
+       }
+
+       if (!pReadFile || !pPoisonFile) {
+               printf("Usage: \n"
+                      "  %s [-o mmapOffset] [-s mmapSize] [-O mmapOffset] [-S mmapSize] -R readFile -P poisonFile\n"
+                      "  (offset and size are both in unit of System Page Size: %ld)\n",
+                               basename(argv[0]), pagesize);
+               exit(0);
+       }
+       if (poisonSize < mmapSize)
+               mmapSize = poisonSize;
+
+       /* fork and mmap files */
+       pid = fork();
+       if (pid == 0) {
+               /* handle SIGBUS */
+               signal(SIGBUS, sigbus_handler);
+               sem = sem_open("sync", O_CREAT, 0666, 0);
+
+               /* mread & do memory failure on poison file */
+               mmap_read_file_then_poison(pPoisonFile, mmapOffset, mmapSize,
+                               poisonOffset, poisonSize);
+
+               sem_close(sem);
+       } else {
+               sem = sem_open("sync", O_CREAT, 0666, 0);
+
+               /* mread read file, wait for child process to be killed */
+               mmap_read_file(pReadFile, mmapOffset, mmapSize);
+               sem_close(sem);
+       }
+       exit(0);
+}
index c0640a1951a4ceb0f9e36755ccc3c6c9df4c7443..4b3b26c86d02ec72fd84b9326a67364468c381cc 100644 (file)
@@ -16,7 +16,7 @@
 #include <mntent.h>
 #include <limits.h>
 
-#define LOCK_TIMEOUT   10
+#define LOCK_TIMEOUT   120
 #define _(x)           (x)
 
 static char *mounted = "t_mtab";
index e3b15ddc6ee871aeaf972a59c0d0b69b9f54c2f0..58cb095999c0b0c106ce699ee1aa0d67cb702c8d 100644 (file)
@@ -87,8 +87,6 @@ static void err_exit(char *op, int errn)
        fprintf(stderr, "%s: %s\n", op, strerror(errn));
        if (fd > 0)
                close(fd);
-       if (semid > 0 && semctl(semid, 2, IPC_RMID) == -1)
-               perror("exit rmid");
        exit(errn);
 }
 
@@ -180,14 +178,16 @@ int main(int argc, char **argv)
        int setlk_macro = F_OFD_SETLKW;
        int getlk_macro = F_OFD_GETLK;
        struct timespec ts;
-       key_t semkey;
+       key_t semkey = -1;
        unsigned short vals[2];
        union semun semu;
        struct semid_ds sem_ds;
        struct sembuf sop;
-       int opt, ret, retry;
+       int opt, ret;
 
-       while((opt = getopt(argc, argv, "sgrwo:l:PRWtFd")) != -1) {
+       //avoid libcap errno bug
+       errno = 0;
+       while((opt = getopt(argc, argv, "sgrwo:l:PRWtFdK:")) != -1) {
                switch(opt) {
                case 's':
                        lock_cmd = 1;
@@ -225,6 +225,9 @@ int main(int argc, char **argv)
                case 'd':
                        use_dup = 1;
                        break;
+               case 'K':
+                       semkey = strtol(optarg, NULL, 16);
+                       break;
                default:
                        usage(argv[0]);
                        return -1;
@@ -274,37 +277,15 @@ int main(int argc, char **argv)
                err_exit("test_ofd_getlk", errno);
        }
 
-       if((semkey = ftok(argv[optind], 255)) == -1)
+       if (semkey == -1)
+               semkey = ftok(argv[optind], 255);
+       if (semkey == -1)
                err_exit("ftok", errno);
 
        /* setlk, and always init the semaphore at setlk time */
        if (lock_cmd == 1) {
-               /*
-                * Init the semaphore, with a key related to the testfile.
-                * getlk routine will wait untill this sem has been created and
-                * iniialized.
-                *
-                * We must make sure the semaphore set is newly created, rather
-                * then the one left from last run. In which case getlk will
-                * exit immediately and left setlk routine waiting forever.
-                * Also because newly created semaphore has zero sem_otime,
-                * which is used here to sync with getlk routine.
-                */
-               retry = 0;
-               do {
-                       semid = semget(semkey, 2, IPC_CREAT|IPC_EXCL);
-                       if (semid < 0 && errno == EEXIST) {
-                               /* remove sem set after one round of test */
-                               if (semctl(semid, 2, IPC_RMID, semu) == -1)
-                                       err_exit("rmid 0", errno);
-                               retry++;
-                       } else if (semid < 0)
-                               err_exit("semget", errno);
-                       else
-                               retry = 10;
-               } while (retry < 5);
-               /* We can't create a new semaphore set in 5 tries */
-               if (retry == 5)
+               semid = semget(semkey, 2, 0);
+               if (semid < 0)
                        err_exit("semget", errno);
 
                /* Init both new sem to 1 */
@@ -313,32 +294,29 @@ int main(int argc, char **argv)
                semu.array = vals;
                if (semctl(semid, 2, SETALL, semu) == -1)
                        err_exit("init sem", errno);
-               /* Inc both new sem to 2 */
-               sop.sem_num = 0;
-               sop.sem_op = 1;
-               sop.sem_flg = 0;
-               ts.tv_sec = 5;
-               ts.tv_nsec = 0;
-               if (semtimedop(semid, &sop, 1, &ts) == -1)
-                       err_exit("inc sem0 2", errno);
-               sop.sem_num = 1;
-               sop.sem_op = 1;
-               sop.sem_flg = 0;
-               ts.tv_sec = 5;
-               ts.tv_nsec = 0;
-               if (semtimedop(semid, &sop, 1, &ts) == -1)
-                       err_exit("inc sem1 2", errno);
 
                /*
-                * Wait initialization complete. semctl(2) only update
-                * sem_ctime, semop(2) will update sem_otime.
+                * Wait initialization complete.
                 */
                ret = -1;
                do {
+                       if (ret != -1)
+                               usleep(100000);
                        memset(&sem_ds, 0, sizeof(sem_ds));
                        semu.buf = &sem_ds;
-                       ret = semctl(semid, 0, IPC_STAT, semu);
-               } while (!(ret == 0 && sem_ds.sem_otime != 0));
+                       ret = semctl(semid, 1, GETVAL, semu);
+                       if (ret == -1)
+                               err_exit("wait sem1 2", errno);
+               } while (ret != 2);
+
+               /* Inc sem0 to 2 */
+               sop.sem_num = 0;
+               sop.sem_op = 1;
+               sop.sem_flg = 0;
+               ts.tv_sec = 5;
+               ts.tv_nsec = 0;
+               if (semtimedop(semid, &sop, 1, &ts) == -1)
+                       err_exit("inc sem0 2", errno);
 
                /* place the lock */
                if (fcntl(fd, setlk_macro, &flk) < 0)
@@ -380,35 +358,38 @@ int main(int argc, char **argv)
                ts.tv_nsec = 0;
                if (semtimedop(semid, &sop, 1, &ts) == -1)
                        err_exit("wait sem1 0", errno);
-
-               /* remove sem set after one round of test */
-               if (semctl(semid, 2, IPC_RMID, semu) == -1)
-                       err_exit("rmid", errno);
                close(fd);
                exit(0);
        }
 
        /* getlck */
        if (lock_cmd == 0) {
-               /* wait sem created and initialized */
-               retry = 5;
-               do {
-                       semid = semget(semkey, 2, 0);
-                       if (semid != -1)
-                               break;
-                       if (errno == ENOENT && retry) {
-                               sleep(1);
-                               retry--;
-                               continue;
-                       } else {
-                               err_exit("getlk_semget", errno);
-                       }
-               } while (1);
+               semid = semget(semkey, 2, 0);
+               if (semid < 0)
+                       err_exit("getlk_semget", errno);
+
+               /*
+                * Wait initialization complete.
+                */
+               ret = -1;
                do {
+                       if (ret != -1)
+                               usleep(100000);
                        memset(&sem_ds, 0, sizeof(sem_ds));
                        semu.buf = &sem_ds;
-                       ret = semctl(semid, 0, IPC_STAT, semu);
-               } while (!(ret == 0 && sem_ds.sem_otime != 0));
+                       ret = semctl(semid, 1, GETVAL, semu);
+                       if (ret == -1)
+                               err_exit("wait sem1 1", errno);
+               } while (ret != 1);
+
+               /* inc sem1 to 2 (initialization completed) */
+               sop.sem_num = 1;
+               sop.sem_op = 1;
+               sop.sem_flg = 0;
+               ts.tv_sec = 5;
+               ts.tv_nsec = 0;
+               if (semtimedop(semid, &sop, 1, &ts) == -1)
+                       err_exit("inc sem1 2", errno);
 
                /* wait sem0 == 0 (setlk and close fd done) */
                sop.sem_num = 0;
index e5179ab27c51d8453037db7cbcec84aee53eb573..8f9bb326dccbf7d83dd06838c7d36051d086bc95 100644 (file)
@@ -27,6 +27,7 @@ struct linux_dirent64 {
 static DIR *dir;
 static int dfd;
 static int ignore_error;
+static int ignore_dtype = 0;
 
 struct dir_ops {
        loff_t (*getpos)(void);
@@ -61,6 +62,10 @@ static void libc_getentry(struct dirent *entry)
                exit(1);
        }
        memcpy(entry, ret, sizeof(struct dirent));
+
+       /* NFS may or may not set d_type, depending on READDIRPLUS */
+       if (!ignore_dtype && entry->d_type == DT_UNKNOWN)
+               ignore_dtype = 1;
 }
 
 static off64_t kernel_getpos(void)
@@ -95,6 +100,10 @@ static void kernel_getentry(struct dirent *entry)
        entry->d_reclen = lentry->d_reclen;
        entry->d_type = lentry->d_type;
        strcpy(entry->d_name, lentry->d_name);
+
+       /* NFS may or may not set d_type, depending on READDIRPLUS */
+       if (!ignore_dtype && entry->d_type == DT_UNKNOWN)
+               ignore_dtype = 1;
 }
 
 struct dir_ops libc_ops = {
@@ -168,8 +177,9 @@ static void test(int count, struct dir_ops *ops)
                pos = random() % count;
                ops->setpos(pbuf[pos]);
                ops->getentry(&entry);
+
                if (dbuf[pos].d_ino != entry.d_ino ||
-                   dbuf[pos].d_type != entry.d_type ||
+                   (!ignore_dtype && dbuf[pos].d_type != entry.d_type) ||
                    strcmp(dbuf[pos].d_name, entry.d_name)) {
                        fprintf(stderr,
                                "Mismatch in dir entry %u at pos %llu\n", pos,
diff --git a/src/t_reflink_read_race.c b/src/t_reflink_read_race.c
new file mode 100644 (file)
index 0000000..00c19d7
--- /dev/null
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Race reads with reflink to see if reads continue while we're cloning.
+ * Copyright 2023 Oracle.  All rights reserved.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <time.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+static pid_t mypid;
+
+static FILE *outcome_fp;
+
+/* Significant timestamps.  Initialized to zero */
+static double program_start, clone_start, clone_end;
+
+/* Coordinates access to timestamps */
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+struct readinfo {
+       int fd;
+       unsigned int blocksize;
+       char *descr;
+};
+
+struct cloneinfo {
+       int src_fd, dst_fd;
+};
+
+#define MSEC_PER_SEC   1000
+#define NSEC_PER_MSEC  1000000
+
+/*
+ * Assume that it shouldn't take longer than 100ms for the FICLONE ioctl to
+ * enter the kernel and take locks on an uncontended file.  This is also the
+ * required CLOCK_MONOTONIC granularity.
+ */
+#define EARLIEST_READ_MS       (MSEC_PER_SEC / 10)
+
+/*
+ * We want to be reasonably sure that the FICLONE takes long enough that any
+ * read initiated after clone operation locked the source file could have
+ * completed a disk IO before the clone finishes.  Therefore, we require that
+ * the clone operation must take at least 4x the maximum setup time.
+ */
+#define MINIMUM_CLONE_MS       (EARLIEST_READ_MS * 5)
+
+static double timespec_to_msec(const struct timespec *ts)
+{
+       return (ts->tv_sec * (double)MSEC_PER_SEC) +
+              (ts->tv_nsec / (double)NSEC_PER_MSEC);
+}
+
+static void sleep_ms(unsigned int len)
+{
+       struct timespec time = {
+               .tv_nsec = len * NSEC_PER_MSEC,
+       };
+
+       nanosleep(&time, NULL);
+}
+
+static void outcome(const char *str)
+{
+       fprintf(outcome_fp, "%s\n", str);
+       fflush(outcome_fp);
+}
+
+static void *reader_fn(void *arg)
+{
+       struct timespec now;
+       struct readinfo *ri = arg;
+       char *buf = malloc(ri->blocksize);
+       loff_t pos = 0;
+       ssize_t copied;
+       int ret;
+
+       if (!buf) {
+               perror("alloc buffer");
+               goto kill_error;
+       }
+
+       /* Wait for the FICLONE to start */
+       pthread_mutex_lock(&lock);
+       while (clone_start < program_start) {
+               pthread_mutex_unlock(&lock);
+#ifdef DEBUG
+               printf("%s read waiting for clone to start; cs=%.2f ps=%.2f\n",
+                               ri->descr, clone_start, program_start);
+               fflush(stdout);
+#endif
+               sleep_ms(1);
+               pthread_mutex_lock(&lock);
+       }
+       pthread_mutex_unlock(&lock);
+       sleep_ms(EARLIEST_READ_MS);
+
+       /* Read from the file... */
+       while ((copied = read(ri->fd, buf, ri->blocksize)) > 0) {
+               double read_completion;
+
+               ret = clock_gettime(CLOCK_MONOTONIC, &now);
+               if (ret) {
+                       perror("clock_gettime");
+                       goto kill_error;
+               }
+               read_completion = timespec_to_msec(&now);
+
+               /*
+                * If clone_end is still zero, the FICLONE is still running.
+                * If the read completion occurred a safe duration after the
+                * start of the ioctl, then report that as an early read
+                * completion.
+                */
+               pthread_mutex_lock(&lock);
+               if (clone_end < program_start &&
+                   read_completion > clone_start + EARLIEST_READ_MS) {
+                       pthread_mutex_unlock(&lock);
+
+                       /* clone still running... */
+                       printf("finished %s read early at %.2fms\n",
+                                       ri->descr,
+                                       read_completion - program_start);
+                       fflush(stdout);
+                       outcome("finished read early");
+                       goto kill_done;
+               }
+               pthread_mutex_unlock(&lock);
+
+               sleep_ms(1);
+               pos += copied;
+       }
+       if (copied < 0) {
+               perror("read");
+               goto kill_error;
+       }
+
+       return NULL;
+kill_error:
+       outcome("reader error");
+kill_done:
+       kill(mypid, SIGTERM);
+       return NULL;
+}
+
+static void *clone_fn(void *arg)
+{
+       struct timespec now;
+       struct cloneinfo *ci = arg;
+       int ret;
+
+       /* Record the approximate start time of this thread */
+       ret = clock_gettime(CLOCK_MONOTONIC, &now);
+       if (ret) {
+               perror("clock_gettime clone start");
+               goto kill_error;
+       }
+       pthread_mutex_lock(&lock);
+       clone_start = timespec_to_msec(&now);
+       pthread_mutex_unlock(&lock);
+
+       printf("started clone at %.2fms\n", clone_start - program_start);
+       fflush(stdout);
+
+       /* Kernel call, only killable with a fatal signal */
+       ret = ioctl(ci->dst_fd, FICLONE, ci->src_fd);
+       if (ret) {
+               perror("FICLONE");
+               goto kill_error;
+       }
+
+       /* If the ioctl completes, note the completion time */
+       ret = clock_gettime(CLOCK_MONOTONIC, &now);
+       if (ret) {
+               perror("clock_gettime clone end");
+               goto kill_error;
+       }
+
+       pthread_mutex_lock(&lock);
+       clone_end = timespec_to_msec(&now);
+       pthread_mutex_unlock(&lock);
+
+       printf("finished clone at %.2fms\n",
+                       clone_end - program_start);
+       fflush(stdout);
+
+       /* Complain if we didn't take long enough to clone. */
+       if (clone_end < clone_start + MINIMUM_CLONE_MS) {
+               printf("clone did not take enough time (%.2fms)\n",
+                               clone_end - clone_start);
+               fflush(stdout);
+               outcome("clone too fast");
+               goto kill_done;
+       }
+
+       outcome("clone finished before any reads");
+       goto kill_done;
+
+kill_error:
+       outcome("clone error");
+kill_done:
+       kill(mypid, SIGTERM);
+       return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+       struct cloneinfo ci;
+       struct readinfo bufio = { .descr = "buffered" };
+       struct readinfo directio = { .descr = "directio" };
+       struct timespec now;
+       pthread_t clone_thread, bufio_thread, directio_thread;
+       double clockres;
+       int ret;
+
+       if (argc != 4) {
+               printf("Usage: %s src_file dst_file outcome_file\n", argv[0]);
+               return 1;
+       }
+
+       mypid = getpid();
+
+       /* Check for sufficient clock precision. */
+       ret = clock_getres(CLOCK_MONOTONIC, &now);
+       if (ret) {
+               perror("clock_getres MONOTONIC");
+               return 2;
+       }
+       printf("CLOCK_MONOTONIC resolution: %llus %luns\n",
+                       (unsigned long long)now.tv_sec,
+                       (unsigned long)now.tv_nsec);
+       fflush(stdout);
+
+       clockres = timespec_to_msec(&now);
+       if (clockres > EARLIEST_READ_MS) {
+               fprintf(stderr, "insufficient CLOCK_MONOTONIC resolution\n");
+               return 2;
+       }
+
+       /*
+        * Measure program start time since the MONOTONIC time base is not
+        * all that well defined.
+        */
+       ret = clock_gettime(CLOCK_MONOTONIC, &now);
+       if (ret) {
+               perror("clock_gettime MONOTONIC");
+               return 2;
+       }
+       if (now.tv_sec == 0 && now.tv_nsec == 0) {
+               fprintf(stderr, "Uhoh, start time is zero?!\n");
+               return 2;
+       }
+       program_start = timespec_to_msec(&now);
+
+       outcome_fp = fopen(argv[3], "w");
+       if (!outcome_fp) {
+               perror(argv[3]);
+               return 2;
+       }
+
+       /* Open separate fds for each thread */
+       ci.src_fd = open(argv[1], O_RDONLY);
+       if (ci.src_fd < 0) {
+               perror(argv[1]);
+               return 2;
+       }
+
+       ci.dst_fd = open(argv[2], O_RDWR | O_CREAT, 0600);
+       if (ci.dst_fd < 0) {
+               perror(argv[2]);
+               return 2;
+       }
+
+       bufio.fd = open(argv[1], O_RDONLY);
+       if (bufio.fd < 0) {
+               perror(argv[1]);
+               return 2;
+       }
+       bufio.blocksize = 37;
+
+       directio.fd = open(argv[1], O_RDONLY | O_DIRECT);
+       if (directio.fd < 0) {
+               perror(argv[1]);
+               return 2;
+       }
+       directio.blocksize = 512;
+
+       /* Start threads */
+       ret = pthread_create(&clone_thread, NULL, clone_fn, &ci);
+       if (ret) {
+               fprintf(stderr, "create clone thread: %s\n", strerror(ret));
+               return 2;
+       }
+
+       ret = pthread_create(&bufio_thread, NULL, reader_fn, &bufio);
+       if (ret) {
+               fprintf(stderr, "create bufio thread: %s\n", strerror(ret));
+               return 2;
+       }
+
+       ret = pthread_create(&directio_thread, NULL, reader_fn, &directio);
+       if (ret) {
+               fprintf(stderr, "create directio thread: %s\n", strerror(ret));
+               return 2;
+       }
+
+       /* Wait for threads */
+       ret = pthread_join(clone_thread, NULL);
+       if (ret) {
+               fprintf(stderr, "join clone thread: %s\n", strerror(ret));
+               return 2;
+       }
+
+       ret = pthread_join(bufio_thread, NULL);
+       if (ret) {
+               fprintf(stderr, "join bufio thread: %s\n", strerror(ret));
+               return 2;
+       }
+
+       ret = pthread_join(directio_thread, NULL);
+       if (ret) {
+               fprintf(stderr, "join directio thread: %s\n", strerror(ret));
+               return 2;
+       }
+
+       printf("Program should have killed itself?\n");
+       outcome("program failed to end early");
+       return 0;
+}
diff --git a/src/t_snapshot_deleted_subvolume.c b/src/t_snapshot_deleted_subvolume.c
new file mode 100644 (file)
index 0000000..402c051
--- /dev/null
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) Meta Platforms, Inc. and affiliates.
+
+#include "global.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <linux/types.h>
+#ifdef HAVE_STRUCT_BTRFS_IOCTL_VOL_ARGS_V2
+#include <linux/btrfs.h>
+#else
+#ifndef BTRFS_IOCTL_MAGIC
+#define BTRFS_IOCTL_MAGIC 0x94
+#endif
+
+#ifndef BTRFS_IOC_SNAP_CREATE_V2
+#define BTRFS_IOC_SNAP_CREATE_V2 \
+       _IOW(BTRFS_IOCTL_MAGIC, 23, struct btrfs_ioctl_vol_args_v2)
+#endif
+
+#ifndef BTRFS_IOC_SUBVOL_CREATE_V2
+#define BTRFS_IOC_SUBVOL_CREATE_V2 \
+       _IOW(BTRFS_IOCTL_MAGIC, 24, struct btrfs_ioctl_vol_args_v2)
+#endif
+
+#ifndef BTRFS_SUBVOL_NAME_MAX
+#define BTRFS_SUBVOL_NAME_MAX 4039
+#endif
+
+struct btrfs_ioctl_vol_args_v2 {
+       __s64 fd;
+       __u64 transid;
+       __u64 flags;
+       union {
+               struct {
+                       __u64 size;
+                       struct btrfs_qgroup_inherit *qgroup_inherit;
+               };
+               __u64 unused[4];
+       };
+       union {
+               char name[BTRFS_SUBVOL_NAME_MAX + 1];
+               __u64 devid;
+               __u64 subvolid;
+       };
+};
+#endif
+
+#if !HAVE_DECL_BTRFS_IOC_SNAP_DESTROY_V2
+#define BTRFS_IOC_SNAP_DESTROY_V2 \
+       _IOW(BTRFS_IOCTL_MAGIC, 63, struct btrfs_ioctl_vol_args_v2)
+#endif
+
+int main(int argc, char **argv)
+{
+       if (argc != 2) {
+               fprintf(stderr, "usage: %s PATH\n", argv[0]);
+               return EXIT_FAILURE;
+       }
+
+       int dirfd = open(argv[1], O_RDONLY | O_DIRECTORY);
+       if (dirfd < 0) {
+               perror(argv[1]);
+               return EXIT_FAILURE;
+       }
+
+       struct btrfs_ioctl_vol_args_v2 subvol_args = {};
+       strcpy(subvol_args.name, "subvol");
+       if (ioctl(dirfd, BTRFS_IOC_SUBVOL_CREATE_V2, &subvol_args) < 0) {
+               perror("BTRFS_IOC_SUBVOL_CREATE_V2");
+               return EXIT_FAILURE;
+       }
+
+       int subvolfd = openat(dirfd, "subvol", O_RDONLY | O_DIRECTORY);
+       if (subvolfd < 0) {
+               perror("openat");
+               return EXIT_FAILURE;
+       }
+
+       if (ioctl(dirfd, BTRFS_IOC_SNAP_DESTROY_V2, &subvol_args) < 0) {
+               perror("BTRFS_IOC_SNAP_DESTROY_V2");
+               return EXIT_FAILURE;
+       }
+
+       struct btrfs_ioctl_vol_args_v2 snap_args = { .fd = subvolfd };
+       strcpy(snap_args.name, "snap");
+       if (ioctl(dirfd, BTRFS_IOC_SNAP_CREATE_V2, &snap_args) < 0) {
+               if (errno == ENOENT)
+                       return EXIT_SUCCESS;
+               perror("BTRFS_IOC_SNAP_CREATE_V2");
+               return EXIT_FAILURE;
+       }
+       fprintf(stderr, "BTRFS_IOC_SNAP_CREATE_V2 should've failed\n");
+       return EXIT_FAILURE;
+}
diff --git a/src/uuid_ioctl.c b/src/uuid_ioctl.c
new file mode 100644 (file)
index 0000000..89a9b5d
--- /dev/null
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Google, Inc. All Rights Reserved.
+ *
+ * Test program which uses the raw ext4 set_fsuuid ioctl directly.
+ * SYNOPSIS:
+ *   $0 COMMAND MOUNT_POINT [UUID]
+ *
+ * COMMAND must be either "get" or "set".
+ * The UUID must be a 16 octet sequence represented as 32 hexadecimal digits in
+ * canonical textual representation, e.g. output from `uuidgen`.
+ *
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <uuid/uuid.h>
+#include <linux/fs.h>
+
+struct fsuuid {
+       __u32   fsu_len;
+       __u32   fsu_flags;
+       __u8    fsu_uuid[];
+};
+
+#ifndef EXT4_IOC_GETFSUUID
+#define EXT4_IOC_GETFSUUID      _IOR('f', 44, struct fsuuid)
+#endif
+
+#ifndef EXT4_IOC_SETFSUUID
+#define EXT4_IOC_SETFSUUID      _IOW('f', 44, struct fsuuid)
+#endif
+
+int main(int argc, char **argv)
+{
+       int error, fd;
+       struct fsuuid *fsuuid = NULL;
+
+       if (argc < 3) {
+               fprintf(stderr, "Invalid arguments\n");
+               return 1;
+       }
+
+       fd = open(argv[2], O_RDONLY);
+       if (!fd) {
+               perror(argv[2]);
+               return 1;
+       }
+
+       fsuuid = malloc(sizeof(*fsuuid) + sizeof(uuid_t));
+       if (!fsuuid) {
+               perror("malloc");
+               return 1;
+       }
+       fsuuid->fsu_len = sizeof(uuid_t);
+       fsuuid->fsu_flags = 0;
+
+       if (strcmp(argv[1], "get") == 0) {
+               uuid_t uuid;
+               char uuid_str[37];
+
+               if (ioctl(fd, EXT4_IOC_GETFSUUID, fsuuid)) {
+                       fprintf(stderr, "%s while trying to get fs uuid\n",
+                               strerror(errno));
+                       return 1;
+               }
+
+               memcpy(&uuid, fsuuid->fsu_uuid, sizeof(uuid));
+               uuid_unparse(uuid, uuid_str);
+               printf("%s\n", uuid_str);
+       } else if (strcmp(argv[1], "set") == 0) {
+               uuid_t uuid;
+
+               if (argc != 4) {
+                       fprintf(stderr, "UUID argument missing.\n");
+                       return 1;
+               }
+
+               error = uuid_parse(argv[3], uuid);
+               if (error < 0) {
+                       fprintf(stderr, "Invalid UUID. The UUID should be in "
+                               "canonical format. Example: "
+                               "8c628557-6987-42b2-ba16-b7cc79ddfb43\n");
+                       return 1;
+               }
+
+               memcpy(&fsuuid->fsu_uuid, uuid, sizeof(uuid));
+               if (ioctl(fd, EXT4_IOC_SETFSUUID, fsuuid)) {
+                       fprintf(stderr, "%s while trying to set fs uuid\n",
+                               strerror(errno));
+                       return 1;
+               }
+       } else {
+               fprintf(stderr, "Invalid command\n");
+               return 1;
+       }
+
+       return 0;
+}
index 1b0b364b2dec135b40a47c3e7f932c55f6e1f05d..868540f5787e9377f089fb1186c5c2a0bfeb7b92 100644 (file)
@@ -4,10 +4,10 @@ TOPDIR = ../..
 include $(TOPDIR)/include/builddefs
 
 TARGETS = vfstest mount-idmapped
-CFILES_VFSTEST = vfstest.c btrfs-idmapped-mounts.c idmapped-mounts.c utils.c
+CFILES_VFSTEST = vfstest.c btrfs-idmapped-mounts.c idmapped-mounts.c utils.c tmpfs-idmapped-mounts.c
 CFILES_MOUNT_IDMAPPED = mount-idmapped.c utils.c
 
-HFILES = missing.h utils.h btrfs-idmapped-mounts.h idmapped-mounts.h
+HFILES = missing.h utils.h btrfs-idmapped-mounts.h idmapped-mounts.h tmpfs-idmapped-mounts.h
 LLDLIBS += -pthread
 LDIRT = $(TARGETS)
 
@@ -19,6 +19,10 @@ ifeq ($(HAVE_URING), true)
 LLDLIBS += -luring
 endif
 
+ifeq ($(HAVE_XFS_IOC_EXCHANGE_RANGE),yes)
+LCFLAGS += -DHAVE_XFS_IOC_EXCHANGE_RANGE
+endif
+
 default: depend $(TARGETS)
 
 depend: .dep
index 63297d5fa43d47415535f98d4cfad0f56c5d7f17..f4dfc3f3a9378519d863830855142bc70f070f16 100644 (file)
@@ -28,7 +28,7 @@
 
 static char t_buf[PATH_MAX];
 
-static int acls(const struct vfstest_info *info)
+int tcore_acls(const struct vfstest_info *info)
 {
        int fret = -1;
        int dir1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -254,7 +254,7 @@ out:
 }
 
 /* Validate that basic file operations on idmapped mounts from a user namespace. */
-static int create_in_userns(const struct vfstest_info *info)
+int tcore_create_in_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -372,7 +372,7 @@ out:
 /* Validate that a caller whose fsids map into the idmapped mount within it's
  * user namespace cannot create any device nodes.
  */
-static int device_node_in_userns(const struct vfstest_info *info)
+int tcore_device_node_in_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int open_tree_fd = -EBADF;
@@ -431,7 +431,7 @@ out:
        return fret;
 }
 
-static int fsids_mapped(const struct vfstest_info *info)
+int tcore_fsids_mapped(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, hardlink_target_fd = -EBADF, open_tree_fd = -EBADF;
@@ -535,7 +535,7 @@ static int fsids_mapped(const struct vfstest_info *info)
                        die("failure: create");
 
                /* create character device */
-               if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | 0644, makedev(5, 1)))
+               if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | 0644, makedev(0, 0)))
                        die("failure: create");
 
                /* create symlink */
@@ -563,7 +563,7 @@ out:
 }
 
 /* Validate that basic file operations on idmapped mounts. */
-static int fsids_unmapped(const struct vfstest_info *info)
+int tcore_fsids_unmapped(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, hardlink_target_fd = -EBADF, open_tree_fd = -EBADF;
@@ -733,7 +733,7 @@ out:
 }
 
 /* Validate that changing file ownership works correctly on idmapped mounts. */
-static int expected_uid_gid_idmapped_mounts(const struct vfstest_info *info)
+int tcore_expected_uid_gid_idmapped_mounts(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd1 = -EBADF, open_tree_fd2 = -EBADF;
@@ -764,7 +764,7 @@ static int expected_uid_gid_idmapped_mounts(const struct vfstest_info *info)
        }
 
        /* create character device */
-       if (mknodat(info->t_dir1_fd, CHRDEV1, S_IFCHR | 0644, makedev(5, 1))) {
+       if (mknodat(info->t_dir1_fd, CHRDEV1, S_IFCHR | 0644, makedev(0, 0))) {
                log_stderr("failure: mknodat");
                goto out;
        }
@@ -1451,7 +1451,7 @@ out:
        return fret;
 }
 
-static int fscaps_idmapped_mounts(const struct vfstest_info *info)
+int tcore_fscaps_idmapped_mounts(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, file1_fd2 = -EBADF, open_tree_fd = -EBADF;
@@ -1599,7 +1599,7 @@ out:
        return fret;
 }
 
-static int fscaps_idmapped_mounts_in_userns(const struct vfstest_info *info)
+int tcore_fscaps_idmapped_mounts_in_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, file1_fd2 = -EBADF, open_tree_fd = -EBADF;
@@ -1812,7 +1812,7 @@ out:
        return fret;
 }
 
-static int fscaps_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info)
+int tcore_fscaps_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, file1_fd2 = -EBADF, open_tree_fd = -EBADF;
@@ -1961,7 +1961,7 @@ out:
        return fret;
 }
 
-static int hardlink_crossing_idmapped_mounts(const struct vfstest_info *info)
+int tcore_hardlink_crossing_idmapped_mounts(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd1 = -EBADF, open_tree_fd2 = -EBADF;
@@ -2061,7 +2061,7 @@ out:
        return fret;
 }
 
-static int hardlink_from_idmapped_mount(const struct vfstest_info *info)
+int tcore_hardlink_from_idmapped_mount(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -2130,7 +2130,7 @@ out:
        return fret;
 }
 
-static int hardlink_from_idmapped_mount_in_userns(const struct vfstest_info *info)
+int tcore_hardlink_from_idmapped_mount_in_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -2207,7 +2207,7 @@ out:
 
 
 #ifdef HAVE_LIBURING_H
-static int io_uring_idmapped(const struct vfstest_info *info)
+int tcore_io_uring_idmapped(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -2338,7 +2338,7 @@ out_unmap:
  * In no circumstances, even with recorded credentials can it be allowed to
  * open the file.
  */
-static int io_uring_idmapped_unmapped(const struct vfstest_info *info)
+int tcore_io_uring_idmapped_unmapped(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -2453,7 +2453,7 @@ out_unmap:
        return fret;
 }
 
-static int io_uring_idmapped_userns(const struct vfstest_info *info)
+int tcore_io_uring_idmapped_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -2624,7 +2624,7 @@ out_unmap:
        return fret;
 }
 
-static int io_uring_idmapped_unmapped_userns(const struct vfstest_info *info)
+int tcore_io_uring_idmapped_unmapped_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -2746,7 +2746,7 @@ out_unmap:
 #endif /* HAVE_LIBURING_H */
 
 /* Validate that protected symlinks work correctly on idmapped mounts. */
-static int protected_symlinks_idmapped_mounts(const struct vfstest_info *info)
+int tcore_protected_symlinks_idmapped_mounts(const struct vfstest_info *info)
 {
        int fret = -1;
        int dir_fd = -EBADF, fd = -EBADF, open_tree_fd = -EBADF;
@@ -2987,7 +2987,7 @@ out:
 /* Validate that protected symlinks work correctly on idmapped mounts inside a
  * user namespace.
  */
-static int protected_symlinks_idmapped_mounts_in_userns(const struct vfstest_info *info)
+int tcore_protected_symlinks_idmapped_mounts_in_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int dir_fd = -EBADF, fd = -EBADF, open_tree_fd = -EBADF;
@@ -3234,7 +3234,7 @@ out:
        return fret;
 }
 
-static int rename_crossing_idmapped_mounts(const struct vfstest_info *info)
+int tcore_rename_crossing_idmapped_mounts(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd1 = -EBADF, open_tree_fd2 = -EBADF;
@@ -3332,7 +3332,7 @@ out:
        return fret;
 }
 
-static int rename_from_idmapped_mount(const struct vfstest_info *info)
+int tcore_rename_from_idmapped_mount(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -3399,7 +3399,7 @@ out:
        return fret;
 }
 
-static int rename_from_idmapped_mount_in_userns(const struct vfstest_info *info)
+int tcore_rename_from_idmapped_mount_in_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -3474,7 +3474,7 @@ out:
        return fret;
 }
 
-static int setattr_truncate_idmapped(const struct vfstest_info *info)
+int tcore_setattr_truncate_idmapped(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -3588,7 +3588,7 @@ out:
        return fret;
 }
 
-static int setattr_truncate_idmapped_in_userns(const struct vfstest_info *info)
+int tcore_setattr_truncate_idmapped_in_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -3780,7 +3780,7 @@ out:
        return fret;
 }
 
-static int setgid_create_idmapped(const struct vfstest_info *info)
+int tcore_setgid_create_idmapped(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -3956,7 +3956,7 @@ out:
        return fret;
 }
 
-static int setgid_create_idmapped_in_userns(const struct vfstest_info *info)
+int tcore_setgid_create_idmapped_in_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -4359,7 +4359,7 @@ out:
 }
 
 /* Validate that setid transitions are handled correctly on idmapped mounts. */
-static int setid_binaries_idmapped_mounts(const struct vfstest_info *info)
+int tcore_setid_binaries_idmapped_mounts(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, exec_fd = -EBADF, open_tree_fd = -EBADF;
@@ -4498,7 +4498,7 @@ out:
  * running in a user namespace where the uid and gid of the setid binary have no
  * mapping.
  */
-static int setid_binaries_idmapped_mounts_in_userns(const struct vfstest_info *info)
+int tcore_setid_binaries_idmapped_mounts_in_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, exec_fd = -EBADF, open_tree_fd = -EBADF;
@@ -4776,7 +4776,7 @@ out:
  * running in a user namespace where the uid and gid of the setid binary have no
  * mapping.
  */
-static int setid_binaries_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info)
+int tcore_setid_binaries_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, exec_fd = -EBADF, open_tree_fd = -EBADF;
@@ -5069,7 +5069,7 @@ out:
        return fret;
 }
 
-static int sticky_bit_unlink_idmapped_mounts(const struct vfstest_info *info)
+int tcore_sticky_bit_unlink_idmapped_mounts(const struct vfstest_info *info)
 {
        int fret = -1;
        int dir_fd = -EBADF, open_tree_fd = -EBADF;
@@ -5362,7 +5362,7 @@ out:
 /* Validate that the sticky bit behaves correctly on idmapped mounts for unlink
  * operations in a user namespace.
  */
-static int sticky_bit_unlink_idmapped_mounts_in_userns(const struct vfstest_info *info)
+int tcore_sticky_bit_unlink_idmapped_mounts_in_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int dir_fd = -EBADF, open_tree_fd = -EBADF;
@@ -5703,7 +5703,7 @@ out:
        return fret;
 }
 
-static int sticky_bit_rename_idmapped_mounts(const struct vfstest_info *info)
+int tcore_sticky_bit_rename_idmapped_mounts(const struct vfstest_info *info)
 {
        int fret = -1;
        int dir_fd = -EBADF, open_tree_fd = -EBADF;
@@ -5960,7 +5960,7 @@ out:
 /* Validate that the sticky bit behaves correctly on idmapped mounts for unlink
  * operations in a user namespace.
  */
-static int sticky_bit_rename_idmapped_mounts_in_userns(const struct vfstest_info *info)
+int tcore_sticky_bit_rename_idmapped_mounts_in_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int dir_fd = -EBADF, open_tree_fd = -EBADF;
@@ -6264,7 +6264,7 @@ out:
        return fret;
 }
 
-static int symlink_idmapped_mounts(const struct vfstest_info *info)
+int tcore_symlink_idmapped_mounts(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -6349,7 +6349,7 @@ out:
        return fret;
 }
 
-static int symlink_idmapped_mounts_in_userns(const struct vfstest_info *info)
+int tcore_symlink_idmapped_mounts_in_userns(const struct vfstest_info *info)
 {
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
@@ -6556,7 +6556,7 @@ static int nested_userns(const struct vfstest_info *info)
        }
 
        /* Don't write a mapping in the 4th userns. */
-       list_empty(&hierarchy[4].id_map);
+       list_empty(&hierarchy[3].id_map);
 
        /* Create the actual userns hierarchy. */
        ret = create_userns_hierarchy(hierarchy);
@@ -6667,7 +6667,7 @@ static int nested_userns(const struct vfstest_info *info)
        }
 
        if (sys_mount_setattr(fd_open_tree_level4, "", AT_EMPTY_PATH,
-                             &attr_level4, sizeof(attr_level4))) {
+                             &attr_level4, sizeof(attr_level4)) != -1 || errno != EINVAL) {
                log_stderr("failure: sys_mount_setattr");
                goto out;
        }
@@ -6706,11 +6706,6 @@ static int nested_userns(const struct vfstest_info *info)
                        log_stderr("failure: check ownership %s", file);
                        goto out;
                }
-
-               if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid)) {
-                       log_stderr("failure: check ownership %s", file);
-                       goto out;
-               }
        }
 
        /* Verify that ownership looks correct for callers in the first userns. */
@@ -7367,7 +7362,7 @@ static int setattr_fix_968219708108(const struct vfstest_info *info)
                 */
                if (!fchownat(open_tree_fd, FILE1, 0, 0, AT_SYMLINK_NOFOLLOW))
                        die("failure: change ownership");
-               if (errno != EINVAL)
+               if (errno != EINVAL && errno != EOVERFLOW)
                        die("failure: errno");
 
                /*
@@ -7457,7 +7452,7 @@ static int setattr_fix_968219708108(const struct vfstest_info *info)
                 */
                if (!fchownat(open_tree_fd, FILE1, 0, 0, AT_SYMLINK_NOFOLLOW))
                        die("failure: change ownership");
-               if (errno != EINVAL)
+               if (errno != EINVAL && errno != EOVERFLOW)
                        die("failure: errno");
 
                /*
@@ -7664,84 +7659,1291 @@ out:
        return fret;
 }
 
-static const struct test_struct t_idmapped_mounts[] = {
-       { acls,                                                         true,   "posix acls on regular mounts",                                                                 },
-       { create_in_userns,                                             true,   "create operations in user namespace",                                                          },
-       { device_node_in_userns,                                        true,   "device node in user namespace",                                                                },
-       { expected_uid_gid_idmapped_mounts,                             true,   "expected ownership on idmapped mounts",                                                        },
-       { fscaps_idmapped_mounts,                                       true,   "fscaps on idmapped mounts",                                                                    },
-       { fscaps_idmapped_mounts_in_userns,                             true,   "fscaps on idmapped mounts in user namespace",                                                  },
-       { fscaps_idmapped_mounts_in_userns_separate_userns,             true,   "fscaps on idmapped mounts in user namespace with different id mappings",                       },
-       { fsids_mapped,                                                 true,   "mapped fsids",                                                                                 },
-       { fsids_unmapped,                                               true,   "unmapped fsids",                                                                               },
-       { hardlink_crossing_idmapped_mounts,                            true,   "cross idmapped mount hardlink",                                                                },
-       { hardlink_from_idmapped_mount,                                 true,   "hardlinks from idmapped mounts",                                                               },
-       { hardlink_from_idmapped_mount_in_userns,                       true,   "hardlinks from idmapped mounts in user namespace",                                             },
-#ifdef HAVE_LIBURING_H
-       { io_uring_idmapped,                                            true,   "io_uring from idmapped mounts",                                                                },
-       { io_uring_idmapped_userns,                                     true,   "io_uring from idmapped mounts in user namespace",                                              },
-       { io_uring_idmapped_unmapped,                                   true,   "io_uring from idmapped mounts with unmapped ids",                                              },
-       { io_uring_idmapped_unmapped_userns,                            true,   "io_uring from idmapped mounts with unmapped ids in user namespace",                            },
-#endif
-       { protected_symlinks_idmapped_mounts,                           true,   "following protected symlinks on idmapped mounts",                                              },
-       { protected_symlinks_idmapped_mounts_in_userns,                 true,   "following protected symlinks on idmapped mounts in user namespace",                            },
-       { rename_crossing_idmapped_mounts,                              true,   "cross idmapped mount rename",                                                                  },
-       { rename_from_idmapped_mount,                                   true,   "rename from idmapped mounts",                                                                  },
-       { rename_from_idmapped_mount_in_userns,                         true,   "rename from idmapped mounts in user namespace",                                                },
-       { setattr_truncate_idmapped,                                    true,   "setattr truncate on idmapped mounts",                                                          },
-       { setattr_truncate_idmapped_in_userns,                          true,   "setattr truncate on idmapped mounts in user namespace",                                        },
-       { setgid_create_idmapped,                                       true,   "create operations in directories with setgid bit set on idmapped mounts",                      },
-       { setgid_create_idmapped_in_userns,                             true,   "create operations in directories with setgid bit set on idmapped mounts in user namespace",    },
-       { setid_binaries_idmapped_mounts,                               true,   "setid binaries on idmapped mounts",                                                            },
-       { setid_binaries_idmapped_mounts_in_userns,                     true,   "setid binaries on idmapped mounts in user namespace",                                          },
-       { setid_binaries_idmapped_mounts_in_userns_separate_userns,     true,   "setid binaries on idmapped mounts in user namespace with different id mappings",               },
-       { sticky_bit_unlink_idmapped_mounts,                            true,   "sticky bit unlink operations on idmapped mounts",                                              },
-       { sticky_bit_unlink_idmapped_mounts_in_userns,                  true,   "sticky bit unlink operations on idmapped mounts in user namespace",                            },
-       { sticky_bit_rename_idmapped_mounts,                            true,   "sticky bit rename operations on idmapped mounts",                                              },
-       { sticky_bit_rename_idmapped_mounts_in_userns,                  true,   "sticky bit rename operations on idmapped mounts in user namespace",                            },
-       { symlink_idmapped_mounts,                                      true,   "symlink from idmapped mounts",                                                                 },
-       { symlink_idmapped_mounts_in_userns,                            true,   "symlink from idmapped mounts in user namespace",                                               },
-};
+/* The current_umask() is stripped from the mode directly in the vfs if the
+ * filesystem either doesn't support acls or the filesystem has been
+ * mounted without posic acl support.
+ *
+ * If the filesystem does support acls then current_umask() stripping is
+ * deferred to posix_acl_create(). So when the filesystem calls
+ * posix_acl_create() and there are no acls set or not supported then
+ * current_umask() will be stripped.
+ *
+ * Use umask(S_IXGRP) to check whether inode strip S_ISGID works correctly
+ * in idmapped situation.
+ *
+ * Test for commit ac6800e279a2 ("fs: Add missing umask strip in vfs_tmpfile")
+ * and 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers").
+ */
+static int setgid_create_umask_idmapped(const struct vfstest_info *info)
+{
+       int fret = -1;
+       int file1_fd = -EBADF, open_tree_fd = -EBADF;
+       struct mount_attr attr = {
+               .attr_set = MOUNT_ATTR_IDMAP,
+       };
+       pid_t pid;
+       int tmpfile_fd = -EBADF;
+       bool supported = false;
+       char path[PATH_MAX];
+       mode_t mode;
 
-const struct test_suite s_idmapped_mounts = {
-       .tests          = t_idmapped_mounts,
-       .nr_tests       = ARRAY_SIZE(t_idmapped_mounts),
-};
+       if (!caps_supported())
+               return 0;
 
-static const struct test_struct t_fscaps_in_ancestor_userns[] = {
-       { fscaps_idmapped_mounts_in_userns_valid_in_ancestor_userns,    true,   "fscaps on idmapped mounts in user namespace writing fscap valid in ancestor userns",           },
-};
+       if (fchmod(info->t_dir1_fd, S_IRUSR |
+                             S_IWUSR |
+                             S_IRGRP |
+                             S_IWGRP |
+                             S_IROTH |
+                             S_IWOTH |
+                             S_IXUSR |
+                             S_IXGRP |
+                             S_IXOTH |
+                             S_ISGID), 0) {
+               log_stderr("failure: fchmod");
+               goto out;
+       }
 
-const struct test_suite s_fscaps_in_ancestor_userns = {
-       .tests          = t_fscaps_in_ancestor_userns,
-       .nr_tests       = ARRAY_SIZE(t_fscaps_in_ancestor_userns),
-};
+       /* Verify that the sid bits got raised. */
+       if (!is_setgid(info->t_dir1_fd, "", AT_EMPTY_PATH)) {
+               log_stderr("failure: is_setgid");
+               goto out;
+       }
 
-static const struct test_struct t_nested_userns[] = {
-       { nested_userns,                                                T_REQUIRE_IDMAPPED_MOUNTS,      "test that nested user namespaces behave correctly when attached to idmapped mounts",           },
-};
+       /* Changing mount properties on a detached mount. */
+       attr.userns_fd  = get_userns_fd(0, 10000, 10000);
+       if (attr.userns_fd < 0) {
+               log_stderr("failure: get_userns_fd");
+               goto out;
+       }
 
-const struct test_suite s_nested_userns = {
-       .tests = t_nested_userns,
-       .nr_tests = ARRAY_SIZE(t_nested_userns),
-};
+       open_tree_fd = sys_open_tree(info->t_dir1_fd, "",
+                                    AT_EMPTY_PATH |
+                                    AT_NO_AUTOMOUNT |
+                                    AT_SYMLINK_NOFOLLOW |
+                                    OPEN_TREE_CLOEXEC |
+                                    OPEN_TREE_CLONE);
+       if (open_tree_fd < 0) {
+               log_stderr("failure: sys_open_tree");
+               goto out;
+       }
 
-/* Test for commit 968219708108 ("fs: handle circular mappings correctly"). */
-static const struct test_struct t_setattr_fix_968219708108[] = {
-       { setattr_fix_968219708108,                                     T_REQUIRE_IDMAPPED_MOUNTS,      "test that setattr works correctly",                                                            },
-};
+       if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) {
+               log_stderr("failure: sys_mount_setattr");
+               goto out;
+       }
 
-const struct test_suite s_setattr_fix_968219708108 = {
-       .tests = t_setattr_fix_968219708108,
-       .nr_tests = ARRAY_SIZE(t_setattr_fix_968219708108),
-};
+       supported = openat_tmpfile_supported(open_tree_fd);
 
-/* Test for commit 705191b03d50 ("fs: fix acl translation"). */
-static const struct test_struct t_setxattr_fix_705191b03d50[] = {
-       { setxattr_fix_705191b03d50,                                    T_REQUIRE_USERNS,       "test that setxattr works correctly for userns mountable filesystems",                          },
-};
+       pid = fork();
+       if (pid < 0) {
+               log_stderr("failure: fork");
+               goto out;
+       }
+       if (pid == 0) {
+               /* Only umask with S_IXGRP because inode strip S_ISGID will check mode
+                * whether has group execute or search permission.
+                */
+               umask(S_IXGRP);
+               mode = umask(S_IXGRP);
+               if (!(mode & S_IXGRP))
+                       die("failure: umask");
 
-const struct test_suite s_setxattr_fix_705191b03d50 = {
-       .tests = t_setxattr_fix_705191b03d50,
-       .nr_tests = ARRAY_SIZE(t_setxattr_fix_705191b03d50),
+               if (!switch_ids(10000, 11000))
+                       die("failure: switch fsids");
+
+               /* create regular file via open() */
+               file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+               if (file1_fd < 0)
+                       die("failure: create");
+
+               /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+                * bit needs to be stripped.
+                */
+               if (is_setgid(open_tree_fd, FILE1, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(open_tree_fd, FILE1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create directory */
+               if (mkdirat(open_tree_fd, DIR1, 0000))
+                       die("failure: create");
+
+               if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+                       /* We're not in_group_p(). */
+                       if (is_setgid(open_tree_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               } else {
+                       /* Directories always inherit the setgid bit. */
+                       if (!is_setgid(open_tree_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               }
+
+               if (is_ixgrp(open_tree_fd, DIR1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a special file via mknodat() vfs_create */
+               if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(open_tree_fd, FILE2, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(open_tree_fd, FILE2, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a whiteout device via mknodat() vfs_mknod */
+               if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(open_tree_fd, CHRDEV1, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(open_tree_fd, CHRDEV1, 0))
+                       die("failure: is_ixgrp");
+
+               /*
+                * In setgid directories newly created files always inherit the
+                * gid from the parent directory. Verify that the file is owned
+                * by gid 10000, not by gid 11000.
+                */
+               if (!expected_uid_gid(open_tree_fd, FILE1, 0, 10000, 10000))
+                       die("failure: check ownership");
+
+               /*
+                * In setgid directories newly created directories always
+                * inherit the gid from the parent directory. Verify that the
+                * directory is owned by gid 10000, not by gid 11000.
+                */
+               if (!expected_uid_gid(open_tree_fd, DIR1, 0, 10000, 10000))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(open_tree_fd, FILE2, 0, 10000, 10000))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 10000, 10000))
+                       die("failure: check ownership");
+
+               if (unlinkat(open_tree_fd, FILE1, 0))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, FILE2, 0))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, CHRDEV1, 0))
+                       die("failure: delete");
+
+               /* create tmpfile via filesystem tmpfile api */
+               if (supported) {
+                       tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+                       if (tmpfile_fd < 0)
+                               die("failure: create");
+                       /* link the temporary file into the filesystem, making it permanent */
+                       snprintf(path, PATH_MAX,  "/proc/self/fd/%d", tmpfile_fd);
+                       if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW))
+                               die("failure: linkat");
+                       if (close(tmpfile_fd))
+                               die("failure: close");
+                       if (is_setgid(open_tree_fd, FILE3, 0))
+                               die("failure: is_setgid");
+                       if (is_ixgrp(open_tree_fd, FILE3, 0))
+                               die("failure: is_ixgrp");
+                       if (!expected_uid_gid(open_tree_fd, FILE3, 0, 10000, 10000))
+                               die("failure: check ownership");
+                       if (unlinkat(open_tree_fd, FILE3, 0))
+                               die("failure: delete");
+               }
+
+               exit(EXIT_SUCCESS);
+       }
+       if (wait_for_pid(pid))
+               goto out;
+
+       fret = 0;
+       log_debug("Ran test");
+out:
+       safe_close(attr.userns_fd);
+       safe_close(file1_fd);
+       safe_close(open_tree_fd);
+
+       return fret;
+}
+
+/* The current_umask() is stripped from the mode directly in the vfs if the
+ * filesystem either doesn't support acls or the filesystem has been
+ * mounted without posic acl support.
+ *
+ * If the filesystem does support acls then current_umask() stripping is
+ * deferred to posix_acl_create(). So when the filesystem calls
+ * posix_acl_create() and there are no acls set or not supported then
+ * current_umask() will be stripped.
+ *
+ * Use umask(S_IXGRP) to check whether inode strip S_ISGID works correctly
+ * in idmapped_in_userns situation.
+ *
+ * Test for commit ac6800e279a2 ("fs: Add missing umask strip in vfs_tmpfile")
+ * and 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers").
+ */
+static int setgid_create_umask_idmapped_in_userns(const struct vfstest_info *info)
+{
+       int fret = -1;
+       int file1_fd = -EBADF, open_tree_fd = -EBADF;
+       struct mount_attr attr = {
+               .attr_set = MOUNT_ATTR_IDMAP,
+       };
+       pid_t pid;
+       int tmpfile_fd = -EBADF;
+       bool supported = false;
+       char path[PATH_MAX];
+       mode_t mode;
+
+       if (!caps_supported())
+               return 0;
+
+       if (fchmod(info->t_dir1_fd, S_IRUSR |
+                             S_IWUSR |
+                             S_IRGRP |
+                             S_IWGRP |
+                             S_IROTH |
+                             S_IWOTH |
+                             S_IXUSR |
+                             S_IXGRP |
+                             S_IXOTH |
+                             S_ISGID), 0) {
+               log_stderr("failure: fchmod");
+               goto out;
+       }
+
+       /* Verify that the sid bits got raised. */
+       if (!is_setgid(info->t_dir1_fd, "", AT_EMPTY_PATH)) {
+               log_stderr("failure: is_setgid");
+               goto out;
+       }
+
+       /* Changing mount properties on a detached mount. */
+       attr.userns_fd  = get_userns_fd(0, 10000, 10000);
+       if (attr.userns_fd < 0) {
+               log_stderr("failure: get_userns_fd");
+               goto out;
+       }
+
+       open_tree_fd = sys_open_tree(info->t_dir1_fd, "",
+                                    AT_EMPTY_PATH |
+                                    AT_NO_AUTOMOUNT |
+                                    AT_SYMLINK_NOFOLLOW |
+                                    OPEN_TREE_CLOEXEC |
+                                    OPEN_TREE_CLONE);
+       if (open_tree_fd < 0) {
+               log_stderr("failure: sys_open_tree");
+               goto out;
+       }
+
+       if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) {
+               log_stderr("failure: sys_mount_setattr");
+               goto out;
+       }
+
+       supported = openat_tmpfile_supported(open_tree_fd);
+
+       /*
+        * Below we verify that setgid inheritance for a newly created file or
+        * directory works correctly. As part of this we need to verify that
+        * newly created files or directories inherit their gid from their
+        * parent directory. So we change the parent directorie's gid to 1000
+        * and create a file with fs{g,u}id 0 and verify that the newly created
+        * file and directory inherit gid 1000, not 0.
+        */
+       if (fchownat(info->t_dir1_fd, "", -1, 1000, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) {
+               log_stderr("failure: fchownat");
+               goto out;
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               log_stderr("failure: fork");
+               goto out;
+       }
+       if (pid == 0) {
+               if (!caps_supported()) {
+                       log_debug("skip: capability library not installed");
+                       exit(EXIT_SUCCESS);
+               }
+
+               /* Only umask with S_IXGRP because inode strip S_ISGID will check mode
+                * whether has group execute or search permission.
+                */
+               umask(S_IXGRP);
+               mode = umask(S_IXGRP);
+               if (!(mode & S_IXGRP))
+                       die("failure: umask");
+
+               if (!switch_userns(attr.userns_fd, 0, 0, false))
+                       die("failure: switch_userns");
+
+               if (!caps_down_fsetid())
+                       die("failure: caps_down_fsetid");
+
+               /* create regular file via open() */
+               file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+               if (file1_fd < 0)
+                       die("failure: create");
+
+               /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+                * bit needs to be stripped.
+                */
+               if (is_setgid(open_tree_fd, FILE1, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(open_tree_fd, FILE1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create directory */
+               if (mkdirat(open_tree_fd, DIR1, 0000))
+                       die("failure: create");
+
+               if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+                       /* We're not in_group_p(). */
+                       if (is_setgid(open_tree_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               } else {
+                       /* Directories always inherit the setgid bit. */
+                       if (!is_setgid(open_tree_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               }
+
+               if (is_ixgrp(open_tree_fd, DIR1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a special file via mknodat() vfs_create */
+               if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(open_tree_fd, FILE2, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(open_tree_fd, FILE2, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a whiteout device via mknodat() vfs_mknod */
+               if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(open_tree_fd, CHRDEV1, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(open_tree_fd, CHRDEV1, 0))
+                       die("failure: is_ixgrp");
+
+               /*
+                * In setgid directories newly created files always inherit the
+                * gid from the parent directory. Verify that the file is owned
+                * by gid 1000, not by gid 0.
+                */
+               if (!expected_uid_gid(open_tree_fd, FILE1, 0, 0, 1000))
+                       die("failure: check ownership");
+
+               /*
+                * In setgid directories newly created directories always
+                * inherit the gid from the parent directory. Verify that the
+                * directory is owned by gid 1000, not by gid 0.
+                */
+               if (!expected_uid_gid(open_tree_fd, DIR1, 0, 0, 1000))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(open_tree_fd, FILE2, 0, 0, 1000))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 0, 1000))
+                       die("failure: check ownership");
+
+               if (unlinkat(open_tree_fd, FILE1, 0))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, FILE2, 0))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, CHRDEV1, 0))
+                       die("failure: delete");
+
+               /* create tmpfile via filesystem tmpfile api */
+               if (supported) {
+                       tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+                       if (tmpfile_fd < 0)
+                               die("failure: create");
+                       /* link the temporary file into the filesystem, making it permanent */
+                       snprintf(path, PATH_MAX,  "/proc/self/fd/%d", tmpfile_fd);
+                       if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW))
+                               die("failure: linkat");
+                       if (close(tmpfile_fd))
+                               die("failure: close");
+                       if (is_setgid(open_tree_fd, FILE3, 0))
+                               die("failure: is_setgid");
+                       if (is_ixgrp(open_tree_fd, FILE3, 0))
+                               die("failure: is_ixgrp");
+                       if (!expected_uid_gid(open_tree_fd, FILE3, 0, 0, 1000))
+                               die("failure: check ownership");
+                       if (unlinkat(open_tree_fd, FILE3, 0))
+                               die("failure: delete");
+               }
+
+               exit(EXIT_SUCCESS);
+       }
+       if (wait_for_pid(pid))
+               goto out;
+
+       fret = 0;
+       log_debug("Ran test");
+out:
+       safe_close(attr.userns_fd);
+       safe_close(file1_fd);
+       safe_close(open_tree_fd);
+
+       return fret;
+}
+
+/*
+ * If the parent directory has a default acl then permissions are based off
+ * of that and current_umask() is ignored. Specifically, if the ACL has an
+ * ACL_MASK entry, the group permissions correspond to the permissions of
+ * the ACL_MASK entry. Otherwise, if the ACL has no ACL_MASK entry, the
+ * group permissions correspond to the permissions of the ACL_GROUP_OBJ
+ * entry.
+ *
+ * Use setfacl to check whether inode strip S_ISGID works correctly under
+ * the above two situations when enabling idmapped.
+ *
+ * Test for commit
+ * 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers").
+ */
+static int setgid_create_acl_idmapped(const struct vfstest_info *info)
+{
+       int fret = -1;
+       int file1_fd = -EBADF, open_tree_fd = -EBADF;
+       struct mount_attr attr = {
+               .attr_set = MOUNT_ATTR_IDMAP,
+       };
+       pid_t pid;
+       int tmpfile_fd = -EBADF;
+       bool supported = false;
+       char path[PATH_MAX];
+       mode_t mode;
+
+       if (!caps_supported())
+               return 0;
+
+       if (fchmod(info->t_dir1_fd, S_IRUSR |
+                             S_IWUSR |
+                             S_IRGRP |
+                             S_IWGRP |
+                             S_IROTH |
+                             S_IWOTH |
+                             S_IXUSR |
+                             S_IXGRP |
+                             S_IXOTH |
+                             S_ISGID), 0) {
+               log_stderr("failure: fchmod");
+               goto out;
+       }
+
+       /* Verify that the sid bits got raised. */
+       if (!is_setgid(info->t_dir1_fd, "", AT_EMPTY_PATH)) {
+               log_stderr("failure: is_setgid");
+               goto out;
+       }
+
+       /* Changing mount properties on a detached mount. */
+       attr.userns_fd  = get_userns_fd(0, 10000, 10000);
+       if (attr.userns_fd < 0) {
+               log_stderr("failure: get_userns_fd");
+               goto out;
+       }
+
+       open_tree_fd = sys_open_tree(info->t_dir1_fd, "",
+                                    AT_EMPTY_PATH |
+                                    AT_NO_AUTOMOUNT |
+                                    AT_SYMLINK_NOFOLLOW |
+                                    OPEN_TREE_CLOEXEC |
+                                    OPEN_TREE_CLONE);
+       if (open_tree_fd < 0) {
+               log_stderr("failure: sys_open_tree");
+               goto out;
+       }
+
+       if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) {
+               log_stderr("failure: sys_mount_setattr");
+               goto out;
+       }
+
+       supported = openat_tmpfile_supported(open_tree_fd);
+
+       pid = fork();
+       if (pid < 0) {
+               log_stderr("failure: fork");
+               goto out;
+       }
+       if (pid == 0) {
+               umask(S_IXGRP);
+               mode = umask(S_IXGRP);
+               if (!(mode & S_IXGRP))
+                       die("failure: umask");
+
+               /* The group permissions correspond to the permissions of the
+                * ACL_MASK entry. Use setfacl to set ACL mask(m) as rw, so now
+                * the group permissions is rw. Also, umask doesn't affect
+                * group permissions because umask will be ignored if having
+                * acl.
+                */
+               snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rw,o::rwx,m:rw %s/%s", info->t_mountpoint, T_DIR1);
+               if (system(t_buf))
+                       die("failure: system");
+
+               if (!switch_ids(10000, 11000))
+                       die("failure: switch fsids");
+
+               /* create regular file via open() */
+               file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+               if (file1_fd < 0)
+                       die("failure: create");
+
+               /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+                * bit needs to be stripped.
+                */
+               if (is_setgid(open_tree_fd, FILE1, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(open_tree_fd, FILE1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create directory */
+               if (mkdirat(open_tree_fd, DIR1, 0000))
+                       die("failure: create");
+
+               if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+                       /* We're not in_group_p(). */
+                       if (is_setgid(open_tree_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               } else {
+                       /* Directories always inherit the setgid bit. */
+                       if (!is_setgid(open_tree_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               }
+
+               if (is_ixgrp(open_tree_fd, DIR1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a special file via mknodat() vfs_create */
+               if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(open_tree_fd, FILE2, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(open_tree_fd, FILE2, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a whiteout device via mknodat() vfs_mknod */
+               if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(open_tree_fd, CHRDEV1, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(open_tree_fd, CHRDEV1, 0))
+                       die("failure: is_ixgrp");
+
+               /*
+                * In setgid directories newly created files always inherit the
+                * gid from the parent directory. Verify that the file is owned
+                * by gid 10000, not by gid 11000.
+                */
+               if (!expected_uid_gid(open_tree_fd, FILE1, 0, 10000, 10000))
+                       die("failure: check ownership");
+
+               /*
+                * In setgid directories newly created directories always
+                * inherit the gid from the parent directory. Verify that the
+                * directory is owned by gid 10000, not by gid 11000.
+                */
+               if (!expected_uid_gid(open_tree_fd, DIR1, 0, 10000, 10000))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(open_tree_fd, FILE2, 0, 10000, 10000))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 10000, 10000))
+                       die("failure: check ownership");
+
+               if (unlinkat(open_tree_fd, FILE1, 0))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, FILE2, 0))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, CHRDEV1, 0))
+                       die("failure: delete");
+
+               /* create tmpfile via filesystem tmpfile api */
+               if (supported) {
+                       tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+                       if (tmpfile_fd < 0)
+                               die("failure: create");
+                       /* link the temporary file into the filesystem, making it permanent */
+                       snprintf(path, PATH_MAX,  "/proc/self/fd/%d", tmpfile_fd);
+                       if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW))
+                               die("failure: linkat");
+                       if (close(tmpfile_fd))
+                               die("failure: close");
+                       if (is_setgid(open_tree_fd, FILE3, 0))
+                               die("failure: is_setgid");
+                       if (is_ixgrp(open_tree_fd, FILE3, 0))
+                               die("failure: is_ixgrp");
+                       if (!expected_uid_gid(open_tree_fd, FILE3, 0, 10000, 10000))
+                               die("failure: check ownership");
+                       if (unlinkat(open_tree_fd, FILE3, 0))
+                               die("failure: delete");
+               }
+
+               exit(EXIT_SUCCESS);
+       }
+       if (wait_for_pid(pid))
+               goto out;
+
+       pid = fork();
+       if (pid < 0) {
+               log_stderr("failure: fork");
+               goto out;
+       }
+       if (pid == 0) {
+               umask(S_IXGRP);
+               mode = umask(S_IXGRP);
+               if (!(mode & S_IXGRP))
+                       die("failure: umask");
+
+               /* The group permissions correspond to the permissions of the
+                * ACL_GROUP_OBJ entry. Don't use setfacl to set ACL_MASK, so
+                * the group permissions is equal to ACL_GROUP_OBJ(g)
+                * entry(rwx). Also, umask doesn't affect group permissions
+                * because umask will be ignored if having acl.
+                */
+               snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rwx,o::rwx, %s/%s", info->t_mountpoint, T_DIR1);
+               if (system(t_buf))
+                       die("failure: system");
+
+               if (!switch_ids(10000, 11000))
+                       die("failure: switch fsids");
+
+               /* create regular file via open() */
+               file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+               if (file1_fd < 0)
+                       die("failure: create");
+
+               /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+                * bit needs to be stripped.
+                */
+               if (is_setgid(open_tree_fd, FILE1, 0))
+                       die("failure: is_setgid");
+
+               if (!is_ixgrp(open_tree_fd, FILE1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create directory */
+               if (mkdirat(open_tree_fd, DIR1, 0000))
+                       die("failure: create");
+
+               if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+                       /* We're not in_group_p(). */
+                       if (is_setgid(open_tree_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               } else {
+                       /* Directories always inherit the setgid bit. */
+                       if (!is_setgid(open_tree_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               }
+
+               if (is_ixgrp(open_tree_fd, DIR1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a special file via mknodat() vfs_create */
+               if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(open_tree_fd, FILE2, 0))
+                       die("failure: is_setgid");
+
+               if (!is_ixgrp(open_tree_fd, FILE2, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a whiteout device via mknodat() vfs_mknod */
+               if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(open_tree_fd, CHRDEV1, 0))
+                       die("failure: is_setgid");
+
+               if (!is_ixgrp(open_tree_fd, CHRDEV1, 0))
+                       die("failure: is_ixgrp");
+
+               /*
+                * In setgid directories newly created files always inherit the
+                * gid from the parent directory. Verify that the file is owned
+                * by gid 10000, not by gid 11000.
+                */
+               if (!expected_uid_gid(open_tree_fd, FILE1, 0, 10000, 10000))
+                       die("failure: check ownership");
+
+               /*
+                * In setgid directories newly created directories always
+                * inherit the gid from the parent directory. Verify that the
+                * directory is owned by gid 10000, not by gid 11000.
+                */
+               if (!expected_uid_gid(open_tree_fd, DIR1, 0, 10000, 10000))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(open_tree_fd, FILE2, 0, 10000, 10000))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 10000, 10000))
+                       die("failure: check ownership");
+
+               if (unlinkat(open_tree_fd, FILE1, 0))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, FILE2, 0))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, CHRDEV1, 0))
+                       die("failure: delete");
+
+               /* create tmpfile via filesystem tmpfile api */
+               if (supported) {
+                       tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+                       if (tmpfile_fd < 0)
+                               die("failure: create");
+                       /* link the temporary file into the filesystem, making it permanent */
+                       snprintf(path, PATH_MAX,  "/proc/self/fd/%d", tmpfile_fd);
+                       if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW))
+                               die("failure: linkat");
+                       if (close(tmpfile_fd))
+                               die("failure: close");
+                       if (is_setgid(open_tree_fd, FILE3, 0))
+                               die("failure: is_setgid");
+                       if (!is_ixgrp(open_tree_fd, FILE3, 0))
+                               die("failure: is_ixgrp");
+                       if (!expected_uid_gid(open_tree_fd, FILE3, 0, 10000, 10000))
+                               die("failure: check ownership");
+                       if (unlinkat(open_tree_fd, FILE3, 0))
+                               die("failure: delete");
+               }
+
+               exit(EXIT_SUCCESS);
+       }
+       if (wait_for_pid(pid))
+               goto out;
+
+       fret = 0;
+       log_debug("Ran test");
+out:
+       safe_close(attr.userns_fd);
+       safe_close(file1_fd);
+       safe_close(open_tree_fd);
+
+       return fret;
+}
+
+/*
+ * If the parent directory has a default acl then permissions are based off
+ * of that and current_umask() is ignored. Specifically, if the ACL has an
+ * ACL_MASK entry, the group permissions correspond to the permissions of
+ * the ACL_MASK entry. Otherwise, if the ACL has no ACL_MASK entry, the
+ * group permissions correspond to the permissions of the ACL_GROUP_OBJ
+ * entry.
+ *
+ * Use setfacl to check whether inode strip S_ISGID works correctly under
+ * the above two situations when enabling userns and idmapped feature.
+ *
+ * Test for commit
+ * 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers").
+ */
+static int setgid_create_acl_idmapped_in_userns(const struct vfstest_info *info)
+{
+       int fret = -1;
+       int file1_fd = -EBADF, open_tree_fd = -EBADF;
+       struct mount_attr attr = {
+               .attr_set = MOUNT_ATTR_IDMAP,
+       };
+       pid_t pid;
+       int tmpfile_fd = -EBADF;
+       bool supported = false;
+       char path[PATH_MAX];
+       mode_t mode;
+
+       if (!caps_supported())
+               return 0;
+
+       if (fchmod(info->t_dir1_fd, S_IRUSR |
+                             S_IWUSR |
+                             S_IRGRP |
+                             S_IWGRP |
+                             S_IROTH |
+                             S_IWOTH |
+                             S_IXUSR |
+                             S_IXGRP |
+                             S_IXOTH |
+                             S_ISGID), 0) {
+               log_stderr("failure: fchmod");
+               goto out;
+       }
+
+       /* Verify that the sid bits got raised. */
+       if (!is_setgid(info->t_dir1_fd, "", AT_EMPTY_PATH)) {
+               log_stderr("failure: is_setgid");
+               goto out;
+       }
+
+       /* Changing mount properties on a detached mount. */
+       attr.userns_fd  = get_userns_fd(0, 10000, 10000);
+       if (attr.userns_fd < 0) {
+               log_stderr("failure: get_userns_fd");
+               goto out;
+       }
+
+       open_tree_fd = sys_open_tree(info->t_dir1_fd, "",
+                                    AT_EMPTY_PATH |
+                                    AT_NO_AUTOMOUNT |
+                                    AT_SYMLINK_NOFOLLOW |
+                                    OPEN_TREE_CLOEXEC |
+                                    OPEN_TREE_CLONE);
+       if (open_tree_fd < 0) {
+               log_stderr("failure: sys_open_tree");
+               goto out;
+       }
+
+       if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) {
+               log_stderr("failure: sys_mount_setattr");
+               goto out;
+       }
+
+       supported = openat_tmpfile_supported(open_tree_fd);
+
+       /*
+        * Below we verify that setgid inheritance for a newly created file or
+        * directory works correctly. As part of this we need to verify that
+        * newly created files or directories inherit their gid from their
+        * parent directory. So we change the parent directorie's gid to 1000
+        * and create a file with fs{g,u}id 0 and verify that the newly created
+        * file and directory inherit gid 1000, not 0.
+        */
+       if (fchownat(info->t_dir1_fd, "", -1, 1000, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) {
+               log_stderr("failure: fchownat");
+               goto out;
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               log_stderr("failure: fork");
+               goto out;
+       }
+       if (pid == 0) {
+               umask(S_IXGRP);
+               mode = umask(S_IXGRP);
+               if (!(mode & S_IXGRP))
+                       die("failure: umask");
+
+               /* The group permissions correspond to the permissions of the
+                * ACL_MASK entry. Use setfacl to set ACL mask(m) as rw, so now
+                * the group permissions is rw. Also, umask doesn't affect
+                * group permissions because umask will be ignored if having
+                * acl.
+                */
+               snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rw,o::rwx,m:rw %s/%s", info->t_mountpoint, T_DIR1);
+               if (system(t_buf))
+                       die("failure: system");
+
+               if (!caps_supported()) {
+                       log_debug("skip: capability library not installed");
+                       exit(EXIT_SUCCESS);
+               }
+
+               if (!switch_userns(attr.userns_fd, 0, 0, false))
+                       die("failure: switch_userns");
+
+               if (!caps_down_fsetid())
+                       die("failure: caps_down_fsetid");
+
+               /* create regular file via open() */
+               file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+               if (file1_fd < 0)
+                       die("failure: create");
+
+               /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+                * bit needs to be stripped.
+                */
+               if (is_setgid(open_tree_fd, FILE1, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(open_tree_fd, FILE1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create directory */
+               if (mkdirat(open_tree_fd, DIR1, 0000))
+                       die("failure: create");
+
+               if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+                       /* We're not in_group_p(). */
+                       if (is_setgid(open_tree_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               } else {
+                       /* Directories always inherit the setgid bit. */
+                       if (!is_setgid(open_tree_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               }
+
+               if (is_ixgrp(open_tree_fd, DIR1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a special file via mknodat() vfs_create */
+               if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(open_tree_fd, FILE2, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(open_tree_fd, FILE2, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a whiteout device via mknodat() vfs_mknod */
+               if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(open_tree_fd, CHRDEV1, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(open_tree_fd, CHRDEV1, 0))
+                       die("failure: is_ixgrp");
+
+               /*
+                * In setgid directories newly created files always inherit the
+                * gid from the parent directory. Verify that the file is owned
+                * by gid 1000, not by gid 0.
+                */
+               if (!expected_uid_gid(open_tree_fd, FILE1, 0, 0, 1000))
+                       die("failure: check ownership");
+
+               /*
+                * In setgid directories newly created directories always
+                * inherit the gid from the parent directory. Verify that the
+                * directory is owned by gid 1000, not by gid 0.
+                */
+               if (!expected_uid_gid(open_tree_fd, DIR1, 0, 0, 1000))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(open_tree_fd, FILE2, 0, 0, 1000))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 0, 1000))
+                       die("failure: check ownership");
+
+               if (unlinkat(open_tree_fd, FILE1, 0))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, FILE2, 0))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, CHRDEV1, 0))
+                       die("failure: delete");
+
+               /* create tmpfile via filesystem tmpfile api */
+               if (supported) {
+                       tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+                       if (tmpfile_fd < 0)
+                               die("failure: create");
+                       /* link the temporary file into the filesystem, making it permanent */
+                       snprintf(path, PATH_MAX,  "/proc/self/fd/%d", tmpfile_fd);
+                       if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW))
+                               die("failure: linkat");
+                       if (close(tmpfile_fd))
+                               die("failure: close");
+                       if (is_setgid(open_tree_fd, FILE3, 0))
+                               die("failure: is_setgid");
+                       if (is_ixgrp(open_tree_fd, FILE3, 0))
+                               die("failure: is_ixgrp");
+                       if (!expected_uid_gid(open_tree_fd, FILE3, 0, 0, 1000))
+                               die("failure: check ownership");
+                       if (unlinkat(open_tree_fd, FILE3, 0))
+                               die("failure: delete");
+               }
+
+               exit(EXIT_SUCCESS);
+       }
+       if (wait_for_pid(pid))
+               goto out;
+
+       pid = fork();
+       if (pid < 0) {
+               log_stderr("failure: fork");
+               goto out;
+       }
+       if (pid == 0) {
+               umask(S_IXGRP);
+               mode = umask(S_IXGRP);
+               if (!(mode & S_IXGRP))
+                       die("failure: umask");
+
+               /* The group permissions correspond to the permissions of the
+                * ACL_GROUP_OBJ entry. Don't use setfacl to set ACL_MASK, so
+                * the group permissions is equal to ACL_GROUP_OBJ(g)
+                * entry(rwx). Also, umask doesn't affect group permissions
+                * because umask will be ignored if having acl.
+                */
+               snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rwx,o::rwx, %s/%s", info->t_mountpoint, T_DIR1);
+               if (system(t_buf))
+                       die("failure: system");
+
+               if (!caps_supported()) {
+                       log_debug("skip: capability library not installed");
+                       exit(EXIT_SUCCESS);
+               }
+
+               if (!switch_userns(attr.userns_fd, 0, 0, false))
+                       die("failure: switch_userns");
+
+               if (!caps_down_fsetid())
+                       die("failure: caps_down_fsetid");
+
+               /* create regular file via open() */
+               file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+               if (file1_fd < 0)
+                       die("failure: create");
+
+               /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+                * bit needs to be stripped.
+                */
+               if (is_setgid(open_tree_fd, FILE1, 0))
+                       die("failure: is_setgid");
+
+               if (!is_ixgrp(open_tree_fd, FILE1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create directory */
+               if (mkdirat(open_tree_fd, DIR1, 0000))
+                       die("failure: create");
+
+               if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+                       /* We're not in_group_p(). */
+                       if (is_setgid(open_tree_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               } else {
+                       /* Directories always inherit the setgid bit. */
+                       if (!is_setgid(open_tree_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               }
+
+               if (is_ixgrp(open_tree_fd, DIR1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a special file via mknodat() vfs_create */
+               if (mknodat(open_tree_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(open_tree_fd, FILE2, 0))
+                       die("failure: is_setgid");
+
+               if (!is_ixgrp(open_tree_fd, FILE2, 0))
+                        die("failure: is_ixgrp");
+               /* create a whiteout device via mknodat() vfs_mknod */
+               if (mknodat(open_tree_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(open_tree_fd, CHRDEV1, 0))
+                       die("failure: is_setgid");
+
+               if (!is_ixgrp(open_tree_fd, CHRDEV1, 0))
+                        die("failure: is_ixgrp");
+
+               /*
+                * In setgid directories newly created files always inherit the
+                * gid from the parent directory. Verify that the file is owned
+                * by gid 1000, not by gid 0.
+                */
+               if (!expected_uid_gid(open_tree_fd, FILE1, 0, 0, 1000))
+                       die("failure: check ownership");
+
+               /*
+                * In setgid directories newly created directories always
+                * inherit the gid from the parent directory. Verify that the
+                * directory is owned by gid 1000, not by gid 0.
+                */
+               if (!expected_uid_gid(open_tree_fd, DIR1, 0, 0, 1000))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(open_tree_fd, FILE2, 0, 0, 1000))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(open_tree_fd, CHRDEV1, 0, 0, 1000))
+                       die("failure: check ownership");
+
+               if (unlinkat(open_tree_fd, FILE1, 0))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, DIR1, AT_REMOVEDIR))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, FILE2, 0))
+                       die("failure: delete");
+
+               if (unlinkat(open_tree_fd, CHRDEV1, 0))
+                       die("failure: delete");
+
+               /* create tmpfile via filesystem tmpfile api */
+               if (supported) {
+                       tmpfile_fd = openat(open_tree_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+                       if (tmpfile_fd < 0)
+                               die("failure: create");
+                       /* link the temporary file into the filesystem, making it permanent */
+                       snprintf(path, PATH_MAX,  "/proc/self/fd/%d", tmpfile_fd);
+                       if (linkat(AT_FDCWD, path, open_tree_fd, FILE3, AT_SYMLINK_FOLLOW))
+                               die("failure: linkat");
+                       if (close(tmpfile_fd))
+                               die("failure: close");
+                       if (is_setgid(open_tree_fd, FILE3, 0))
+                               die("failure: is_setgid");
+                       if (!is_ixgrp(open_tree_fd, FILE3, 0))
+                               die("failure: is_ixgrp");
+                       if (!expected_uid_gid(open_tree_fd, FILE3, 0, 0, 1000))
+                               die("failure: check ownership");
+                       if (unlinkat(open_tree_fd, FILE3, 0))
+                               die("failure: delete");
+               }
+
+               exit(EXIT_SUCCESS);
+       }
+       if (wait_for_pid(pid))
+               goto out;
+
+       fret = 0;
+       log_debug("Ran test");
+out:
+       safe_close(attr.userns_fd);
+       safe_close(file1_fd);
+       safe_close(open_tree_fd);
+
+       return fret;
+}
+
+static const struct test_struct t_idmapped_mounts[] = {
+       { tcore_acls,                                                         true,   "posix acls on regular mounts",                                                                 },
+       { tcore_create_in_userns,                                             true,   "create operations in user namespace",                                                          },
+       { tcore_device_node_in_userns,                                        true,   "device node in user namespace",                                                                },
+       { tcore_expected_uid_gid_idmapped_mounts,                               true,   "expected ownership on idmapped mounts",                                                        },
+       { tcore_fscaps_idmapped_mounts,                                 true,   "fscaps on idmapped mounts",                                                                    },
+       { tcore_fscaps_idmapped_mounts_in_userns,                               true,   "fscaps on idmapped mounts in user namespace",                                                  },
+       { tcore_fscaps_idmapped_mounts_in_userns_separate_userns,               true,   "fscaps on idmapped mounts in user namespace with different id mappings",                       },
+       { tcore_fsids_mapped,                                                 true,   "mapped fsids",                                                                                 },
+       { tcore_fsids_unmapped,                                               true,   "unmapped fsids",                                                                               },
+       { tcore_hardlink_crossing_idmapped_mounts,                              true,   "cross idmapped mount hardlink",                                                                },
+       { tcore_hardlink_from_idmapped_mount,                                   true,   "hardlinks from idmapped mounts",                                                               },
+       { tcore_hardlink_from_idmapped_mount_in_userns,                 true,   "hardlinks from idmapped mounts in user namespace",                                             },
+#ifdef HAVE_LIBURING_H
+       { tcore_io_uring_idmapped,                                              true,   "io_uring from idmapped mounts",                                                                },
+       { tcore_io_uring_idmapped_userns,                                       true,   "io_uring from idmapped mounts in user namespace",                                              },
+       { tcore_io_uring_idmapped_unmapped,                                     true,   "io_uring from idmapped mounts with unmapped ids",                                              },
+       { tcore_io_uring_idmapped_unmapped_userns,                              true,   "io_uring from idmapped mounts with unmapped ids in user namespace",                            },
+#endif
+       { tcore_protected_symlinks_idmapped_mounts,                             true,   "following protected symlinks on idmapped mounts",                                              },
+       { tcore_protected_symlinks_idmapped_mounts_in_userns,                   true,   "following protected symlinks on idmapped mounts in user namespace",                            },
+       { tcore_rename_crossing_idmapped_mounts,                                true,   "cross idmapped mount rename",                                                                  },
+       { tcore_rename_from_idmapped_mount,                                     true,   "rename from idmapped mounts",                                                                  },
+       { tcore_rename_from_idmapped_mount_in_userns,                           true,   "rename from idmapped mounts in user namespace",                                                },
+       { tcore_setattr_truncate_idmapped,                                      true,   "setattr truncate on idmapped mounts",                                                          },
+       { tcore_setattr_truncate_idmapped_in_userns,                            true,   "setattr truncate on idmapped mounts in user namespace",                                        },
+       { tcore_setgid_create_idmapped,                                 true,   "create operations in directories with setgid bit set on idmapped mounts",                      },
+       { tcore_setgid_create_idmapped_in_userns,                               true,   "create operations in directories with setgid bit set on idmapped mounts in user namespace",    },
+       { tcore_setid_binaries_idmapped_mounts,                         true,   "setid binaries on idmapped mounts",                                                            },
+       { tcore_setid_binaries_idmapped_mounts_in_userns,                       true,   "setid binaries on idmapped mounts in user namespace",                                          },
+       { tcore_setid_binaries_idmapped_mounts_in_userns_separate_userns,       true,   "setid binaries on idmapped mounts in user namespace with different id mappings",               },
+       { tcore_sticky_bit_unlink_idmapped_mounts,                              true,   "sticky bit unlink operations on idmapped mounts",                                              },
+       { tcore_sticky_bit_unlink_idmapped_mounts_in_userns,                    true,   "sticky bit unlink operations on idmapped mounts in user namespace",                            },
+       { tcore_sticky_bit_rename_idmapped_mounts,                              true,   "sticky bit rename operations on idmapped mounts",                                              },
+       { tcore_sticky_bit_rename_idmapped_mounts_in_userns,                    true,   "sticky bit rename operations on idmapped mounts in user namespace",                            },
+       { tcore_symlink_idmapped_mounts,                                        true,   "symlink from idmapped mounts",                                                                 },
+       { tcore_symlink_idmapped_mounts_in_userns,                              true,   "symlink from idmapped mounts in user namespace",                                               },
+};
+
+const struct test_suite s_idmapped_mounts = {
+       .tests          = t_idmapped_mounts,
+       .nr_tests       = ARRAY_SIZE(t_idmapped_mounts),
+};
+
+static const struct test_struct t_fscaps_in_ancestor_userns[] = {
+       { fscaps_idmapped_mounts_in_userns_valid_in_ancestor_userns,    true,   "fscaps on idmapped mounts in user namespace writing fscap valid in ancestor userns",           },
+};
+
+const struct test_suite s_fscaps_in_ancestor_userns = {
+       .tests          = t_fscaps_in_ancestor_userns,
+       .nr_tests       = ARRAY_SIZE(t_fscaps_in_ancestor_userns),
+};
+
+static const struct test_struct t_nested_userns[] = {
+       { nested_userns,                                                T_REQUIRE_IDMAPPED_MOUNTS,      "test that nested user namespaces behave correctly when attached to idmapped mounts",           },
+};
+
+const struct test_suite s_nested_userns = {
+       .tests = t_nested_userns,
+       .nr_tests = ARRAY_SIZE(t_nested_userns),
+};
+
+/* Test for commit 968219708108 ("fs: handle circular mappings correctly"). */
+static const struct test_struct t_setattr_fix_968219708108[] = {
+       { setattr_fix_968219708108,                                     T_REQUIRE_IDMAPPED_MOUNTS,      "test that setattr works correctly",                                                            },
+};
+
+const struct test_suite s_setattr_fix_968219708108 = {
+       .tests = t_setattr_fix_968219708108,
+       .nr_tests = ARRAY_SIZE(t_setattr_fix_968219708108),
+};
+
+/* Test for commit 705191b03d50 ("fs: fix acl translation"). */
+static const struct test_struct t_setxattr_fix_705191b03d50[] = {
+       { setxattr_fix_705191b03d50,                                    T_REQUIRE_USERNS,       "test that setxattr works correctly for userns mountable filesystems",                          },
+};
+
+const struct test_suite s_setxattr_fix_705191b03d50 = {
+       .tests = t_setxattr_fix_705191b03d50,
+       .nr_tests = ARRAY_SIZE(t_setxattr_fix_705191b03d50),
+};
+
+static const struct test_struct t_setgid_create_umask_idmapped_mounts[] = {
+       { setgid_create_umask_idmapped,                                 T_REQUIRE_IDMAPPED_MOUNTS,      "create operations by using umask in directories with setgid bit set on idmapped mount",                },
+       { setgid_create_umask_idmapped_in_userns,                       T_REQUIRE_IDMAPPED_MOUNTS,      "create operations by using umask in directories with setgid bit set on idmapped mount inside userns",  },
+};
+
+const struct test_suite s_setgid_create_umask_idmapped_mounts = {
+       .tests = t_setgid_create_umask_idmapped_mounts,
+       .nr_tests = ARRAY_SIZE(t_setgid_create_umask_idmapped_mounts),
+};
+
+static const struct test_struct t_setgid_create_acl_idmapped_mounts[] = {
+       { setgid_create_acl_idmapped,                                   T_REQUIRE_IDMAPPED_MOUNTS,      "create operations by using acl in directories with setgid bit set on idmapped mount",                },
+       { setgid_create_acl_idmapped_in_userns,                         T_REQUIRE_IDMAPPED_MOUNTS,      "create operations by using acl in directories with setgid bit set on idmapped mount inside userns",  },
+};
+
+const struct test_suite s_setgid_create_acl_idmapped_mounts = {
+       .tests = t_setgid_create_acl_idmapped_mounts,
+       .nr_tests = ARRAY_SIZE(t_setgid_create_acl_idmapped_mounts),
 };
index ff21ea2c3f242fa2bede9f307fe821eb483c1118..4a2c7b393613b311a7c39c6740269060b4ea83e9 100644 (file)
@@ -14,5 +14,45 @@ extern const struct test_suite s_fscaps_in_ancestor_userns;
 extern const struct test_suite s_nested_userns;
 extern const struct test_suite s_setattr_fix_968219708108;
 extern const struct test_suite s_setxattr_fix_705191b03d50;
+extern const struct test_suite s_setgid_create_umask_idmapped_mounts;
+extern const struct test_suite s_setgid_create_acl_idmapped_mounts;
+
+/* Core tests */
+int tcore_acls(const struct vfstest_info *info);
+int tcore_create_in_userns(const struct vfstest_info *info);
+int tcore_device_node_in_userns(const struct vfstest_info *info);
+int tcore_fsids_mapped(const struct vfstest_info *info);
+int tcore_fsids_unmapped(const struct vfstest_info *info);
+int tcore_expected_uid_gid_idmapped_mounts(const struct vfstest_info *info);
+int tcore_fscaps_idmapped_mounts(const struct vfstest_info *info);
+int tcore_fscaps_idmapped_mounts_in_userns(const struct vfstest_info *info);
+int tcore_fscaps_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info);
+int tcore_hardlink_crossing_idmapped_mounts(const struct vfstest_info *info);
+int tcore_hardlink_from_idmapped_mount(const struct vfstest_info *info);
+int tcore_hardlink_from_idmapped_mount_in_userns(const struct vfstest_info *info);
+#ifdef HAVE_LIBURING_H
+int tcore_io_uring_idmapped(const struct vfstest_info *info);
+int tcore_io_uring_idmapped_userns(const struct vfstest_info *info);
+int tcore_io_uring_idmapped_unmapped(const struct vfstest_info *info);
+int tcore_io_uring_idmapped_unmapped_userns(const struct vfstest_info *info);
+#endif
+int tcore_protected_symlinks_idmapped_mounts(const struct vfstest_info *info);
+int tcore_protected_symlinks_idmapped_mounts_in_userns(const struct vfstest_info *info);
+int tcore_rename_crossing_idmapped_mounts(const struct vfstest_info *info);
+int tcore_rename_from_idmapped_mount(const struct vfstest_info *info);
+int tcore_rename_from_idmapped_mount_in_userns(const struct vfstest_info *info);
+int tcore_setattr_truncate_idmapped(const struct vfstest_info *info);
+int tcore_setattr_truncate_idmapped_in_userns(const struct vfstest_info *info);
+int tcore_setgid_create_idmapped(const struct vfstest_info *info);
+int tcore_setgid_create_idmapped_in_userns(const struct vfstest_info *info);
+int tcore_setid_binaries_idmapped_mounts(const struct vfstest_info *info);
+int tcore_setid_binaries_idmapped_mounts_in_userns(const struct vfstest_info *info);
+int tcore_setid_binaries_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info);
+int tcore_sticky_bit_unlink_idmapped_mounts(const struct vfstest_info *info);
+int tcore_sticky_bit_unlink_idmapped_mounts_in_userns(const struct vfstest_info *info);
+int tcore_sticky_bit_rename_idmapped_mounts(const struct vfstest_info *info);
+int tcore_sticky_bit_rename_idmapped_mounts_in_userns(const struct vfstest_info *info);
+int tcore_symlink_idmapped_mounts(const struct vfstest_info *info);
+int tcore_symlink_idmapped_mounts_in_userns(const struct vfstest_info *info);
 
 #endif /* __IDMAPPED_MOUNTS_H */
diff --git a/src/vfs/tmpfs-idmapped-mounts.c b/src/vfs/tmpfs-idmapped-mounts.c
new file mode 100644 (file)
index 0000000..0899aed
--- /dev/null
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "../global.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <grp.h>
+#include <limits.h>
+#include <linux/limits.h>
+#include <linux/types.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <sched.h>
+#include <stdbool.h>
+#include <sys/fsuid.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include "missing.h"
+#include "utils.h"
+#include "vfstest.h"
+#include "idmapped-mounts.h"
+
+static int tmpfs_nested_mount_setup(const struct vfstest_info *info, int (*test)(const struct vfstest_info *info))
+{
+       char path[PATH_MAX];
+       int fret = -1;
+       struct vfstest_info nested_test_info = *info;
+
+       /* Create mapping for userns
+        * Make the mapping quite long, so all nested userns that are created by
+        * any test we call is contained here (otherwise userns creation fails).
+        */
+       struct mount_attr attr = {
+               .attr_set       = MOUNT_ATTR_IDMAP,
+               .userns_fd      = -EBADF,
+       };
+       attr.userns_fd = get_userns_fd(0, 10000, 200000);
+       if (attr.userns_fd < 0) {
+               log_stderr("failure: get_userns_fd");
+               goto out_close;
+       }
+
+       if (!switch_userns(attr.userns_fd, 0, 0, false)) {
+               log_stderr("failure: switch_userns");
+               goto out_close;
+       }
+
+       /* create separate mount namespace */
+       if (unshare(CLONE_NEWNS)) {
+               log_stderr("failure: create new mount namespace");
+               goto out_close;
+       }
+
+       /* We don't want this mount in the parent mount ns */
+       if (sys_mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0)) {
+               log_stderr("failure: mount");
+               goto out_close;
+       }
+
+       /* Create DIR0 to mount there */
+       if (mkdirat(info->t_mnt_fd, DIR0, 0777)) {
+               log_stderr("failure: mkdirat");
+               goto out_close;
+       }
+       if (fchmodat(info->t_mnt_fd, DIR0, 0777, 0)) {
+               log_stderr("failure: fchmodat");
+               goto out_rm;
+       }
+
+       snprintf(path, sizeof(path), "%s/%s", info->t_mountpoint, DIR0);
+       if (sys_mount("tmpfs", path, "tmpfs", 0, NULL)) {
+               log_stderr("failure: mount");
+               goto out_rm;
+       }
+
+       // Create a new info to use for the test we will call.
+       nested_test_info = *info;
+       nested_test_info.t_mountpoint = strdup(path);
+       if (!nested_test_info.t_mountpoint) {
+               log_stderr("failure: strdup");
+               goto out;
+       }
+       nested_test_info.t_mnt_fd = openat(-EBADF, nested_test_info.t_mountpoint, O_CLOEXEC | O_DIRECTORY);
+       if (nested_test_info.t_mnt_fd < 0) {
+               log_stderr("failure: openat");
+               goto out;
+       }
+
+       test_setup(&nested_test_info);
+
+       // Run the test.
+       if ((*test)(&nested_test_info)) {
+               log_stderr("failure: calling test");
+               goto out;
+       }
+
+       test_cleanup(&nested_test_info);
+
+       fret = 0;
+       log_debug("Ran test");
+out:
+       snprintf(path, sizeof(path), "%s/" DIR0, info->t_mountpoint);
+       sys_umount2(path, MNT_DETACH);
+out_rm:
+       if (rm_r(info->t_mnt_fd, DIR0))
+               log_stderr("failure: rm_r");
+out_close:
+       safe_close(attr.userns_fd);
+       return fret;
+}
+
+static int tmpfs_acls(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_acls);
+}
+static int tmpfs_create_in_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_create_in_userns);
+}
+static int tmpfs_device_node_in_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_device_node_in_userns);
+}
+static int tmpfs_fsids_mapped(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_fsids_mapped);
+}
+static int tmpfs_fsids_unmapped(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_fsids_unmapped);
+}
+static int tmpfs_expected_uid_gid_idmapped_mounts(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_expected_uid_gid_idmapped_mounts);
+}
+static int tmpfs_fscaps_idmapped_mounts(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_fscaps_idmapped_mounts);
+}
+static int tmpfs_fscaps_idmapped_mounts_in_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_fscaps_idmapped_mounts_in_userns);
+}
+static int tmpfs_fscaps_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_fscaps_idmapped_mounts_in_userns_separate_userns);
+}
+
+static int tmpfs_hardlink_crossing_idmapped_mounts(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_hardlink_crossing_idmapped_mounts);
+}
+static int tmpfs_hardlink_from_idmapped_mount(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_hardlink_from_idmapped_mount);
+}
+static int tmpfs_hardlink_from_idmapped_mount_in_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_hardlink_from_idmapped_mount_in_userns);
+}
+
+#ifdef HAVE_LIBURING_H
+static int tmpfs_io_uring_idmapped(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_io_uring_idmapped);
+}
+static int tmpfs_io_uring_idmapped_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_io_uring_idmapped_userns);
+}
+static int tmpfs_io_uring_idmapped_unmapped(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_io_uring_idmapped_unmapped);
+}
+static int tmpfs_io_uring_idmapped_unmapped_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_io_uring_idmapped_unmapped_userns);
+}
+#endif /* HAVE_LIBURING_H */
+
+static int tmpfs_protected_symlinks_idmapped_mounts(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_protected_symlinks_idmapped_mounts);
+}
+static int tmpfs_protected_symlinks_idmapped_mounts_in_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_protected_symlinks_idmapped_mounts_in_userns);
+}
+static int tmpfs_rename_crossing_idmapped_mounts(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_rename_crossing_idmapped_mounts);
+}
+static int tmpfs_rename_from_idmapped_mount(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_rename_from_idmapped_mount);
+}
+static int tmpfs_rename_from_idmapped_mount_in_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_rename_from_idmapped_mount_in_userns);
+}
+static int tmpfs_setattr_truncate_idmapped(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_setattr_truncate_idmapped);
+}
+static int tmpfs_setattr_truncate_idmapped_in_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_setattr_truncate_idmapped_in_userns);
+}
+static int tmpfs_setgid_create_idmapped(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_setgid_create_idmapped);
+}
+static int tmpfs_setgid_create_idmapped_in_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_setgid_create_idmapped_in_userns);
+}
+static int tmpfs_setid_binaries_idmapped_mounts(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_setid_binaries_idmapped_mounts);
+}
+static int tmpfs_setid_binaries_idmapped_mounts_in_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_setid_binaries_idmapped_mounts_in_userns);
+}
+static int tmpfs_setid_binaries_idmapped_mounts_in_userns_separate_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_setid_binaries_idmapped_mounts_in_userns_separate_userns);
+}
+static int tmpfs_sticky_bit_unlink_idmapped_mounts(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_sticky_bit_unlink_idmapped_mounts);
+}
+static int tmpfs_sticky_bit_unlink_idmapped_mounts_in_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_sticky_bit_unlink_idmapped_mounts_in_userns);
+}
+static int tmpfs_sticky_bit_rename_idmapped_mounts(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_sticky_bit_rename_idmapped_mounts);
+}
+static int tmpfs_sticky_bit_rename_idmapped_mounts_in_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_sticky_bit_rename_idmapped_mounts_in_userns);
+}
+static int tmpfs_symlink_idmapped_mounts(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_symlink_idmapped_mounts);
+}
+static int tmpfs_symlink_idmapped_mounts_in_userns(const struct vfstest_info *info)
+{
+       return tmpfs_nested_mount_setup(info, tcore_symlink_idmapped_mounts_in_userns);
+}
+
+static const struct test_struct t_tmpfs[] = {
+       { tmpfs_acls,                                           T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs create operations in user namespace",                                                          },
+       { tmpfs_create_in_userns,                                               T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs create operations in user namespace",                                                          },
+       { tmpfs_device_node_in_userns,                                          T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs device node in user namespace",                                                                },
+       { tmpfs_expected_uid_gid_idmapped_mounts,                               T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs expected ownership on idmapped mounts",                                                  },
+       { tmpfs_fscaps_idmapped_mounts,                                         T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs fscaps on idmapped mounts",                                                                      },
+       { tmpfs_fscaps_idmapped_mounts_in_userns,                               T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs fscaps on idmapped mounts in user namespace",                                                    },
+       { tmpfs_fscaps_idmapped_mounts_in_userns_separate_userns,               T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs fscaps on idmapped mounts in user namespace with different id mappings",                 },
+       { tmpfs_fsids_mapped,                                                   T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs mapped fsids",                                                                                 },
+       { tmpfs_fsids_unmapped,                                                 T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs unmapped fsids",                                                                               },
+       { tmpfs_hardlink_crossing_idmapped_mounts,                              T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs cross idmapped mount hardlink",                                                          },
+       { tmpfs_hardlink_from_idmapped_mount,                                   T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs hardlinks from idmapped mounts",                                                         },
+       { tmpfs_hardlink_from_idmapped_mount_in_userns,                         T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs hardlinks from idmapped mounts in user namespace",                                               },
+#ifdef HAVE_LIBURING_H
+       { tmpfs_io_uring_idmapped,                                              T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs io_uring from idmapped mounts",                                                                },
+       { tmpfs_io_uring_idmapped_userns,                                       T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs io_uring from idmapped mounts in user namespace",                                              },
+       { tmpfs_io_uring_idmapped_unmapped,                                     T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs io_uring from idmapped mounts with unmapped ids",                                              },
+       { tmpfs_io_uring_idmapped_unmapped_userns,                              T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs io_uring from idmapped mounts with unmapped ids in user namespace",                            },
+#endif
+       { tmpfs_protected_symlinks_idmapped_mounts,                             T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs following protected symlinks on idmapped mounts",                                                },
+       { tmpfs_protected_symlinks_idmapped_mounts_in_userns,                   T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs following protected symlinks on idmapped mounts in user namespace",                              },
+       { tmpfs_rename_crossing_idmapped_mounts,                                T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs cross idmapped mount rename",                                                                    },
+       { tmpfs_rename_from_idmapped_mount,                                     T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs rename from idmapped mounts",                                                                    },
+       { tmpfs_rename_from_idmapped_mount_in_userns,                           T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs rename from idmapped mounts in user namespace",                                          },
+       { tmpfs_setattr_truncate_idmapped,                                      T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs setattr truncate on idmapped mounts",                                                            },
+       { tmpfs_setattr_truncate_idmapped_in_userns,                            T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs setattr truncate on idmapped mounts in user namespace",                                  },
+       { tmpfs_setgid_create_idmapped,                                         T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs create operations in directories with setgid bit set on idmapped mounts",                        },
+       { tmpfs_setgid_create_idmapped_in_userns,                               T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs create operations in directories with setgid bit set on idmapped mounts in user namespace",      },
+       { tmpfs_setid_binaries_idmapped_mounts,                                 T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs setid binaries on idmapped mounts",                                                              },
+       { tmpfs_setid_binaries_idmapped_mounts_in_userns,                       T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs setid binaries on idmapped mounts in user namespace",                                            },
+       { tmpfs_setid_binaries_idmapped_mounts_in_userns_separate_userns,       T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs setid binaries on idmapped mounts in user namespace with different id mappings",         },
+       { tmpfs_sticky_bit_unlink_idmapped_mounts,                              T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs sticky bit unlink operations on idmapped mounts",                                                },
+       { tmpfs_sticky_bit_unlink_idmapped_mounts_in_userns,                    T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs sticky bit unlink operations on idmapped mounts in user namespace",                              },
+       { tmpfs_sticky_bit_rename_idmapped_mounts,                              T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs sticky bit rename operations on idmapped mounts",                                                },
+       { tmpfs_sticky_bit_rename_idmapped_mounts_in_userns,                    T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs sticky bit rename operations on idmapped mounts in user namespace",                              },
+       { tmpfs_symlink_idmapped_mounts,                                        T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs symlink from idmapped mounts",                                                                   },
+       { tmpfs_symlink_idmapped_mounts_in_userns,                              T_REQUIRE_USERNS | T_REQUIRE_IDMAPPED_MOUNTS,   "tmpfs symlink from idmapped mounts in user namespace",                                         },
+};
+
+
+const struct test_suite s_tmpfs_idmapped_mounts = {
+       .tests = t_tmpfs,
+       .nr_tests = ARRAY_SIZE(t_tmpfs),
+};
diff --git a/src/vfs/tmpfs-idmapped-mounts.h b/src/vfs/tmpfs-idmapped-mounts.h
new file mode 100644 (file)
index 0000000..ed24651
--- /dev/null
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __TMPFS_IDMAPPED_MOUNTS_H
+#define __TMPFS_IDMAPPED_MOUNTS_H
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "utils.h"
+
+extern const struct test_suite s_tmpfs_idmapped_mounts;
+
+#endif /* __TMPFS_IDMAPPED_MOUNTS_H */
index 1388edda5877a3c82a5639931f388905a1b75b29..0ab5de15e60ba56b1cba760f1eaf1fc56209d197 100644 (file)
@@ -10,7 +10,6 @@
 #include <stdlib.h>
 #include <sys/eventfd.h>
 #include <sys/fsuid.h>
-#include <sys/mount.h>
 #include <sys/prctl.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
@@ -60,7 +59,9 @@ pid_t do_clone(int (*fn)(void *), void *arg, int flags)
 
 static int get_userns_fd_cb(void *data)
 {
-       return 0;
+       for (;;)
+               pause();
+       _exit(0);
 }
 
 int wait_for_pid(pid_t pid)
@@ -129,10 +130,8 @@ static int write_id_mapping(idmap_type_t map_type, pid_t pid, const char *buf, s
 
        fret = 0;
 out:
-       if (fd >= 0)
-               close(fd);
-       if (setgroups_fd >= 0)
-               close(setgroups_fd);
+       safe_close(fd);
+       safe_close(setgroups_fd);
 
        return fret;
 }
@@ -286,6 +285,10 @@ bool switch_ids(uid_t uid, gid_t gid)
        if (setresuid(uid, uid, uid))
                return syserror("failure: setresuid");
 
+       /* Ensure we can access proc files from processes we can ptrace. */
+       if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0))
+               return syserror("failure: make dumpable");
+
        return true;
 }
 
@@ -303,11 +306,6 @@ static int userns_fd_cb(void *data)
        if (c == '1') {
                if (!switch_ids(0, 0))
                        return syserror("failure: switch ids to 0");
-
-               /* Ensure we can access proc files from processes we can ptrace. */
-               ret = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
-               if (ret < 0)
-                       return syserror("failure: make dumpable");
        }
 
        ret = write_nointr(h->fd_event, "1", 1);
@@ -809,6 +807,20 @@ bool is_sticky(int dfd, const char *path, int flags)
        return (st.st_mode & S_ISVTX) > 0;
 }
 
+/*is_ixgrp - check whether file or directory is S_IXGRP */
+bool is_ixgrp(int dfd, const char *path, int flags)
+{
+       int ret;
+       struct stat st;
+
+       ret = fstatat(dfd, path, &st, flags);
+       if (ret < 0)
+               return false;
+
+       errno = 0; /* Don't report misleading errno. */
+       return (st.st_mode & S_IXGRP);
+}
+
 bool switch_resids(uid_t uid, gid_t gid)
 {
        if (setresgid(gid, gid, gid))
@@ -907,10 +919,12 @@ bool openat_tmpfile_supported(int dirfd)
 
        fd = openat(dirfd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
        if (fd == -1) {
-               if (errno == ENOTSUP)
+               if (errno == ENOTSUP) {
+                       errno = 0; /* Don't report misleading errno. */
                        return false;
-               else
+               } else {
                        return log_errno(false, "failure: create");
+               }
        }
 
        if (close(fd))
index 7fb702fd80b99aefbc2ec03bbdda7b3fd24fbe8f..872fd96f88c9d9129dce7beae1dfb37c666232ff 100644 (file)
@@ -45,6 +45,8 @@
 #define DIR2 "dir2"
 #define DIR3 "dir3"
 #define DIR1_RENAME "dir1_rename"
+// This directory may be used by tests that call another test.
+#define DIR0 "dir0"
 #define HARDLINK1 "hardlink1"
 #define SYMLINK1 "symlink1"
 #define SYMLINK_USER1 "symlink_user1"
@@ -177,7 +179,7 @@ struct vfs_ns_cap_data {
 struct vfstest_info {
        uid_t t_overflowuid;
        gid_t t_overflowgid;
-       /* path of the test device */
+       /* Filesystem type of the mountpoint */
        const char *t_fstype;
        /* path of the test device */
        const char *t_device;
@@ -368,6 +370,7 @@ extern bool expected_file_size(int dfd, const char *path, int flags,
 extern bool is_setid(int dfd, const char *path, int flags);
 extern bool is_setgid(int dfd, const char *path, int flags);
 extern bool is_sticky(int dfd, const char *path, int flags);
+extern bool is_ixgrp(int dfd, const char *path, int flags);
 extern bool openat_tmpfile_supported(int dirfd);
 
 #endif /* __IDMAP_UTILS_H */
index 29ac0bec3886a73596f5d579de1da7ebb5fae248..f842117df11d88d897d1bd92051d755f4cf69990 100644 (file)
 #include <unistd.h>
 
 #include "btrfs-idmapped-mounts.h"
+#include "tmpfs-idmapped-mounts.h"
 #include "idmapped-mounts.h"
 #include "missing.h"
 #include "utils.h"
 
+static char t_buf[PATH_MAX];
+
 static void init_vfstest_info(struct vfstest_info *info)
 {
        info->t_overflowuid             = 65534;
@@ -78,7 +81,7 @@ static void stash_overflowgid(struct vfstest_info *info)
        info->t_overflowgid = atoi(buf);
 }
 
-static void test_setup(struct vfstest_info *info)
+void test_setup(struct vfstest_info *info)
 {
        if (mkdirat(info->t_mnt_fd, T_DIR1, 0777))
                die("failure: mkdirat");
@@ -91,7 +94,7 @@ static void test_setup(struct vfstest_info *info)
                die("failure: fchmod");
 }
 
-static void test_cleanup(struct vfstest_info *info)
+void test_cleanup(struct vfstest_info *info)
 {
        safe_close(info->t_dir1_fd);
        if (rm_r(info->t_mnt_fd, T_DIR1))
@@ -103,7 +106,7 @@ static int hardlink_crossing_mounts(const struct vfstest_info *info)
        int fret = -1;
        int file1_fd = -EBADF, open_tree_fd = -EBADF;
 
-        if (chown_r(info->t_mnt_fd, T_DIR1, 10000, 10000)) {
+       if (chown_r(info->t_mnt_fd, T_DIR1, 10000, 10000)) {
                log_stderr("failure: chown_r");
                goto out;
        }
@@ -1733,6 +1736,515 @@ out:
        return fret;
 }
 
+/* The current_umask() is stripped from the mode directly in the vfs if the
+ * filesystem either doesn't support acls or the filesystem has been
+ * mounted without posic acl support.
+ *
+ * If the filesystem does support acls then current_umask() stripping is
+ * deferred to posix_acl_create(). So when the filesystem calls
+ * posix_acl_create() and there are no acls set or not supported then
+ * current_umask() will be stripped.
+ *
+ * Use umask(S_IXGRP) to check whether inode strip S_ISGID works correctly.
+ *
+ * test for commit ac6800e279a2 ("fs: Add missing umask strip in vfs_tmpfile")
+ * and 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers").
+ */
+static int setgid_create_umask(const struct vfstest_info *info)
+{
+       int fret = -1;
+       int file1_fd = -EBADF;
+       int tmpfile_fd = -EBADF;
+       pid_t pid;
+       bool supported = false;
+       mode_t mode;
+
+       if (!caps_supported())
+               return 0;
+
+       if (fchmod(info->t_dir1_fd, S_IRUSR |
+                             S_IWUSR |
+                             S_IRGRP |
+                             S_IWGRP |
+                             S_IROTH |
+                             S_IWOTH |
+                             S_IXUSR |
+                             S_IXGRP |
+                             S_IXOTH |
+                             S_ISGID), 0) {
+               log_stderr("failure: fchmod");
+               goto out;
+       }
+
+       /* Verify that the setgid bit got raised. */
+       if (!is_setgid(info->t_dir1_fd, "", AT_EMPTY_PATH)) {
+               log_stderr("failure: is_setgid");
+               goto out;
+       }
+
+       supported = openat_tmpfile_supported(info->t_dir1_fd);
+
+       pid = fork();
+       if (pid < 0) {
+               log_stderr("failure: fork");
+               goto out;
+       }
+       if (pid == 0) {
+               umask(S_IXGRP);
+               mode = umask(S_IXGRP);
+               if (!(mode & S_IXGRP))
+                       die("failure: umask");
+
+               if (!switch_ids(0, 10000))
+                       die("failure: switch_ids");
+
+               if (!caps_down_fsetid())
+                       die("failure: caps_down_fsetid");
+
+               /* create regular file via open() */
+               file1_fd = openat(info->t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+               if (file1_fd < 0)
+                       die("failure: create");
+
+               /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+                * bit needs to be stripped.
+                */
+               if (is_setgid(info->t_dir1_fd, FILE1, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(info->t_dir1_fd, FILE1, 0))
+                       die("failure: is_ixgrp");
+
+               if (mkdirat(info->t_dir1_fd, DIR1, 0000))
+                       die("failure: create");
+
+               if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+                       /* We're not in_group_p(). */
+                       if (is_setgid(info->t_dir1_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               } else {
+                       /* Directories always inherit the setgid bit. */
+                       if (!is_setgid(info->t_dir1_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               }
+
+               if (is_ixgrp(info->t_dir1_fd, DIR1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a special file via mknodat() vfs_create */
+               if (mknodat(info->t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(info->t_dir1_fd, FILE2, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(info->t_dir1_fd, FILE2, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a character device via mknodat() vfs_mknod */
+               if (mknodat(info->t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1)))
+                       die("failure: mknodat");
+
+               if (is_setgid(info->t_dir1_fd, CHRDEV1, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(info->t_dir1_fd, CHRDEV1, 0))
+                       die("failure: is_ixgrp");
+
+               /*
+                * In setgid directories newly created files always inherit the
+                * gid from the parent directory. Verify that the file is owned
+                * by gid 0, not by gid 10000.
+                */
+               if (!expected_uid_gid(info->t_dir1_fd, FILE1, 0, 0, 0))
+                       die("failure: check ownership");
+
+               /*
+                * In setgid directories newly created directories always
+                * inherit the gid from the parent directory. Verify that the
+                * directory is owned by gid 0, not by gid 10000.
+                */
+               if (!expected_uid_gid(info->t_dir1_fd, DIR1, 0, 0, 0))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(info->t_dir1_fd, FILE2, 0, 0, 0))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(info->t_dir1_fd, CHRDEV1, 0, 0, 0))
+                       die("failure: check ownership");
+
+               if (unlinkat(info->t_dir1_fd, FILE1, 0))
+                       die("failure: delete");
+
+               if (unlinkat(info->t_dir1_fd, DIR1, AT_REMOVEDIR))
+                       die("failure: delete");
+
+               if (unlinkat(info->t_dir1_fd, FILE2, 0))
+                       die("failure: delete");
+
+               if (unlinkat(info->t_dir1_fd, CHRDEV1, 0))
+                       die("failure: delete");
+
+               /* create tmpfile via filesystem tmpfile api */
+               if (supported) {
+                       tmpfile_fd = openat(info->t_dir1_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+                       if (tmpfile_fd < 0)
+                               die("failure: create");
+                       /* link the temporary file into the filesystem, making it permanent */
+                       if (linkat(tmpfile_fd, "", info->t_dir1_fd, FILE3, AT_EMPTY_PATH))
+                               die("failure: linkat");
+                       if (close(tmpfile_fd))
+                               die("failure: close");
+                       if (is_setgid(info->t_dir1_fd, FILE3, 0))
+                               die("failure: is_setgid");
+                       if (is_ixgrp(info->t_dir1_fd, FILE3, 0))
+                               die("failure: is_ixgrp");
+                       if (unlinkat(info->t_dir1_fd, FILE3, 0))
+                               die("failure: delete");
+               }
+
+               exit(EXIT_SUCCESS);
+       }
+       if (wait_for_pid(pid))
+               goto out;
+
+       fret = 0;
+       log_debug("Ran test");
+out:
+       safe_close(file1_fd);
+       return fret;
+}
+
+/*
+ * If the parent directory has a default acl then permissions are based off
+ * of that and current_umask() is ignored. Specifically, if the ACL has an
+ * ACL_MASK entry, the group permissions correspond to the permissions of
+ * the ACL_MASK entry. Otherwise, if the ACL has no ACL_MASK entry, the
+ * group permissions correspond to the permissions of the ACL_GROUP_OBJ
+ * entry.
+ *
+ * Use setfacl to check whether inode strip S_ISGID works correctly under
+ * the above two situations.
+ *
+ * Test for commit
+ * 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers").
+ */
+static int setgid_create_acl(const struct vfstest_info *info)
+{
+       int fret = -1;
+       int file1_fd = -EBADF;
+       int tmpfile_fd = -EBADF;
+       pid_t pid;
+       bool supported = false;
+       mode_t mode;
+
+       if (!caps_supported())
+               return 0;
+
+       if (fchmod(info->t_dir1_fd, S_IRUSR |
+                             S_IWUSR |
+                             S_IRGRP |
+                             S_IWGRP |
+                             S_IROTH |
+                             S_IWOTH |
+                             S_IXUSR |
+                             S_IXGRP |
+                             S_IXOTH |
+                             S_ISGID), 0) {
+               log_stderr("failure: fchmod");
+               goto out;
+       }
+
+       /* Verify that the setgid bit got raised. */
+       if (!is_setgid(info->t_dir1_fd, "", AT_EMPTY_PATH)) {
+               log_stderr("failure: is_setgid");
+               goto out;
+       }
+
+       supported = openat_tmpfile_supported(info->t_dir1_fd);
+
+       pid = fork();
+       if (pid < 0) {
+               log_stderr("failure: fork");
+               goto out;
+       }
+       if (pid == 0) {
+               umask(S_IXGRP);
+               mode = umask(S_IXGRP);
+               if (!(mode & S_IXGRP))
+                       die("failure: umask");
+
+               /* The group permissions correspond to the permissions of the
+                * ACL_MASK entry. Use setfacl to set ACL mask(m) as rw, so now
+                * the group permissions is rw. Also, umask doesn't affect
+                * group permissions because umask will be ignored if having
+                * acl.
+                */
+               snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rw,o::rwx,m:rw %s/%s", info->t_mountpoint, T_DIR1);
+               if (system(t_buf))
+                       die("failure: system");
+
+               if (!switch_ids(0, 10000))
+                       die("failure: switch_ids");
+
+               if (!caps_down_fsetid())
+                       die("failure: caps_down_fsetid");
+
+               /* create regular file via open() */
+               file1_fd = openat(info->t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+               if (file1_fd < 0)
+                       die("failure: create");
+
+               /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+                * bit needs to be stripped.
+                */
+               if (is_setgid(info->t_dir1_fd, FILE1, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(info->t_dir1_fd, FILE1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create directory */
+               if (mkdirat(info->t_dir1_fd, DIR1, 0000))
+                       die("failure: create");
+
+               if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+                       /* We're not in_group_p(). */
+                       if (is_setgid(info->t_dir1_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               } else {
+                       /* Directories always inherit the setgid bit. */
+                       if (!is_setgid(info->t_dir1_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               }
+
+               if (is_ixgrp(info->t_dir1_fd, DIR1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a special file via mknodat() vfs_create */
+               if (mknodat(info->t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(info->t_dir1_fd, FILE2, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(info->t_dir1_fd, FILE2, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a character device via mknodat() vfs_mknod */
+               if (mknodat(info->t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1)))
+                       die("failure: mknodat");
+
+               if (is_setgid(info->t_dir1_fd, CHRDEV1, 0))
+                       die("failure: is_setgid");
+
+               if (is_ixgrp(info->t_dir1_fd, CHRDEV1, 0))
+                       die("failure: is_ixgrp");
+
+               /*
+                * In setgid directories newly created files always inherit the
+                * gid from the parent directory. Verify that the file is owned
+                * by gid 0, not by gid 10000.
+                */
+               if (!expected_uid_gid(info->t_dir1_fd, FILE1, 0, 0, 0))
+                       die("failure: check ownership");
+
+               /*
+                * In setgid directories newly created directories always
+                * inherit the gid from the parent directory. Verify that the
+                * directory is owned by gid 0, not by gid 10000.
+                */
+               if (!expected_uid_gid(info->t_dir1_fd, DIR1, 0, 0, 0))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(info->t_dir1_fd, FILE2, 0, 0, 0))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(info->t_dir1_fd, CHRDEV1, 0, 0, 0))
+                       die("failure: check ownership");
+
+               if (unlinkat(info->t_dir1_fd, FILE1, 0))
+                       die("failure: delete");
+
+               if (unlinkat(info->t_dir1_fd, DIR1, AT_REMOVEDIR))
+                       die("failure: delete");
+
+               if (unlinkat(info->t_dir1_fd, FILE2, 0))
+                       die("failure: delete");
+
+               if (unlinkat(info->t_dir1_fd, CHRDEV1, 0))
+                       die("failure: delete");
+
+               /* create tmpfile via filesystem tmpfile api */
+               if (supported) {
+                       tmpfile_fd = openat(info->t_dir1_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+                       if (tmpfile_fd < 0)
+                               die("failure: create");
+                       /* link the temporary file into the filesystem, making it permanent */
+                       if (linkat(tmpfile_fd, "", info->t_dir1_fd, FILE3, AT_EMPTY_PATH))
+                               die("failure: linkat");
+                       if (close(tmpfile_fd))
+                               die("failure: close");
+                       if (is_setgid(info->t_dir1_fd, FILE3, 0))
+                               die("failure: is_setgid");
+                       if (is_ixgrp(info->t_dir1_fd, FILE3, 0))
+                               die("failure: is_ixgrp");
+                       if (!expected_uid_gid(info->t_dir1_fd, FILE3, 0, 0, 0))
+                               die("failure: check ownership");
+                       if (unlinkat(info->t_dir1_fd, FILE3, 0))
+                               die("failure: delete");
+               }
+
+               exit(EXIT_SUCCESS);
+       }
+       if (wait_for_pid(pid))
+               goto out;
+
+       pid = fork();
+       if (pid < 0) {
+               log_stderr("failure: fork");
+               goto out;
+       }
+       if (pid == 0) {
+               umask(S_IXGRP);
+               mode = umask(S_IXGRP);
+               if (!(mode & S_IXGRP))
+                       die("failure: umask");
+
+               /* The group permissions correspond to the permissions of the
+                * ACL_GROUP_OBJ entry. Don't use setfacl to set ACL_MASK, so
+                * the group permissions is equal to ACL_GROUP_OBJ(g)
+                * entry(rwx). Also, umask doesn't affect group permissions
+                * because umask will be ignored if having acl.
+                */
+               snprintf(t_buf, sizeof(t_buf), "setfacl -d -m u::rwx,g::rwx,o::rwx, %s/%s", info->t_mountpoint, T_DIR1);
+               if (system(t_buf))
+                       die("failure: system");
+
+               if (!switch_ids(0, 10000))
+                       die("failure: switch_ids");
+
+               if (!caps_down_fsetid())
+                       die("failure: caps_down_fsetid");
+
+               /* create regular file via open() */
+               file1_fd = openat(info->t_dir1_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
+               if (file1_fd < 0)
+                       die("failure: create");
+
+               /* Neither in_group_p() nor capable_wrt_inode_uidgid() so setgid
+                * bit needs to be stripped.
+                */
+               if (is_setgid(info->t_dir1_fd, FILE1, 0))
+                       die("failure: is_setgid");
+
+               if (!is_ixgrp(info->t_dir1_fd, FILE1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create directory */
+               if (mkdirat(info->t_dir1_fd, DIR1, 0000))
+                       die("failure: create");
+
+               if (xfs_irix_sgid_inherit_enabled(info->t_fstype)) {
+                       /* We're not in_group_p(). */
+                       if (is_setgid(info->t_dir1_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               } else {
+                       /* Directories always inherit the setgid bit. */
+                       if (!is_setgid(info->t_dir1_fd, DIR1, 0))
+                               die("failure: is_setgid");
+               }
+
+               if (is_ixgrp(info->t_dir1_fd, DIR1, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a special file via mknodat() vfs_create */
+               if (mknodat(info->t_dir1_fd, FILE2, S_IFREG | S_ISGID | S_IXGRP, 0))
+                       die("failure: mknodat");
+
+               if (is_setgid(info->t_dir1_fd, FILE2, 0))
+                       die("failure: is_setgid");
+
+               if (!is_ixgrp(info->t_dir1_fd, FILE2, 0))
+                       die("failure: is_ixgrp");
+
+               /* create a character device via mknodat() vfs_mknod */
+               if (mknodat(info->t_dir1_fd, CHRDEV1, S_IFCHR | S_ISGID | S_IXGRP, makedev(5, 1)))
+                       die("failure: mknodat");
+
+               if (is_setgid(info->t_dir1_fd, CHRDEV1, 0))
+                       die("failure: is_setgid");
+
+               if (!is_ixgrp(info->t_dir1_fd, CHRDEV1, 0))
+                       die("failure: is_ixgrp");
+
+               /*
+                * In setgid directories newly created files always inherit the
+                * gid from the parent directory. Verify that the file is owned
+                * by gid 0, not by gid 10000.
+                */
+               if (!expected_uid_gid(info->t_dir1_fd, FILE1, 0, 0, 0))
+                       die("failure: check ownership");
+
+               /*
+                * In setgid directories newly created directories always
+                * inherit the gid from the parent directory. Verify that the
+                * directory is owned by gid 0, not by gid 10000.
+                */
+               if (!expected_uid_gid(info->t_dir1_fd, DIR1, 0, 0, 0))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(info->t_dir1_fd, FILE2, 0, 0, 0))
+                       die("failure: check ownership");
+
+               if (!expected_uid_gid(info->t_dir1_fd, CHRDEV1, 0, 0, 0))
+                       die("failure: check ownership");
+
+               if (unlinkat(info->t_dir1_fd, FILE1, 0))
+                       die("failure: delete");
+
+               if (unlinkat(info->t_dir1_fd, DIR1, AT_REMOVEDIR))
+                       die("failure: delete");
+
+               if (unlinkat(info->t_dir1_fd, FILE2, 0))
+                       die("failure: delete");
+
+               if (unlinkat(info->t_dir1_fd, CHRDEV1, 0))
+                       die("failure: delete");
+
+               /* create tmpfile via filesystem tmpfile api */
+               if (supported) {
+                       tmpfile_fd = openat(info->t_dir1_fd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+                       if (tmpfile_fd < 0)
+                               die("failure: create");
+                       /* link the temporary file into the filesystem, making it permanent */
+                       if (linkat(tmpfile_fd, "", info->t_dir1_fd, FILE3, AT_EMPTY_PATH))
+                               die("failure: linkat");
+                       if (close(tmpfile_fd))
+                               die("failure: close");
+                       if (is_setgid(info->t_dir1_fd, FILE3, 0))
+                               die("failure: is_setgid");
+                       if (!is_ixgrp(info->t_dir1_fd, FILE3, 0))
+                               die("failure: is_ixgrp");
+                       if (!expected_uid_gid(info->t_dir1_fd, FILE3, 0, 0, 0))
+                               die("failure: check ownership");
+                       if (unlinkat(info->t_dir1_fd, FILE3, 0))
+                               die("failure: delete");
+               }
+
+               exit(EXIT_SUCCESS);
+       }
+       if (wait_for_pid(pid))
+               goto out;
+
+       fret = 0;
+       log_debug("Ran test");
+out:
+       safe_close(file1_fd);
+
+       return fret;
+}
+
 static int setattr_truncate(const struct vfstest_info *info)
 {
        int fret = -1;
@@ -1805,8 +2317,11 @@ static void usage(void)
        fprintf(stderr, "--test-fscaps-regression            Run fscap regression tests\n");
        fprintf(stderr, "--test-nested-userns                Run nested userns idmapped mount testsuite\n");
        fprintf(stderr, "--test-btrfs                        Run btrfs specific idmapped mount testsuite\n");
+       fprintf(stderr, "--test-tmpfs                        Run tmpfs specific idmapped mount testsuite\n");
        fprintf(stderr, "--test-setattr-fix-968219708108     Run setattr regression tests\n");
        fprintf(stderr, "--test-setxattr-fix-705191b03d50    Run setxattr regression tests\n");
+       fprintf(stderr, "--test-setgid-create-umask          Run setgid with umask tests\n");
+       fprintf(stderr, "--test-setgid-create-acl            Run setgid with acl tests\n");
 
        _exit(EXIT_SUCCESS);
 }
@@ -1825,6 +2340,9 @@ static const struct option longopts[] = {
        {"test-btrfs",                          no_argument,            0,      'b'},
        {"test-setattr-fix-968219708108",       no_argument,            0,      'i'},
        {"test-setxattr-fix-705191b03d50",      no_argument,            0,      'j'},
+       {"test-setgid-create-umask",            no_argument,            0,      'u'},
+       {"test-setgid-create-acl",              no_argument,            0,      'l'},
+       {"test-tmpfs",                          no_argument,            0,      't'},
        {NULL,                                  0,                      0,        0},
 };
 
@@ -1850,6 +2368,24 @@ static const struct test_suite s_basic = {
        .nr_tests = ARRAY_SIZE(t_basic),
 };
 
+static const struct test_struct t_setgid_create_umask[] = {
+       { setgid_create_umask,                                          0,                      "create operations in directories with setgid bit set under umask",                             },
+};
+
+static const struct test_suite s_setgid_create_umask = {
+       .tests = t_setgid_create_umask,
+       .nr_tests = ARRAY_SIZE(t_setgid_create_umask),
+};
+
+static const struct test_struct t_setgid_create_acl[] = {
+       { setgid_create_acl,                                            0,                      "create operations in directories with setgid bit set under posix acl",                         },
+};
+
+static const struct test_suite s_setgid_create_acl = {
+       .tests = t_setgid_create_acl,
+       .nr_tests = ARRAY_SIZE(t_setgid_create_acl),
+};
+
 static bool run_test(struct vfstest_info *info, const struct test_struct suite[], size_t suite_size)
 {
        int i;
@@ -1947,7 +2483,8 @@ int main(int argc, char *argv[])
        bool idmapped_mounts_supported = false, test_btrfs = false,
             test_core = false, test_fscaps_regression = false,
             test_nested_userns = false, test_setattr_fix_968219708108 = false,
-            test_setxattr_fix_705191b03d50 = false;
+            test_setxattr_fix_705191b03d50 = false, test_tmpfs = false,
+            test_setgid_create_umask = false, test_setgid_create_acl = false;
 
        init_vfstest_info(&info);
 
@@ -1989,6 +2526,15 @@ int main(int argc, char *argv[])
                case 'j':
                        test_setxattr_fix_705191b03d50 = true;
                        break;
+               case 'u':
+                       test_setgid_create_umask = true;
+                       break;
+               case 'l':
+                       test_setgid_create_acl = true;
+                       break;
+               case 't':
+                       test_tmpfs = true;
+                       break;
                case 'h':
                        /* fallthrough */
                default:
@@ -2066,6 +2612,27 @@ int main(int argc, char *argv[])
            !run_suite(&info, &s_setxattr_fix_705191b03d50))
                goto out;
 
+       if (test_setgid_create_umask) {
+               if (!run_suite(&info, &s_setgid_create_umask))
+                       goto out;
+
+               if (!run_suite(&info, &s_setgid_create_umask_idmapped_mounts))
+                       goto out;
+       }
+
+       if (test_setgid_create_acl) {
+               if (!run_suite(&info, &s_setgid_create_acl))
+                       goto out;
+
+               if (!run_suite(&info, &s_setgid_create_acl_idmapped_mounts))
+                       goto out;
+       }
+
+       if (test_tmpfs) {
+               if (!run_suite(&info, &s_tmpfs_idmapped_mounts))
+                       goto out;
+       }
+
        fret = EXIT_SUCCESS;
 
 out:
diff --git a/src/vfs/vfstest.h b/src/vfs/vfstest.h
new file mode 100644 (file)
index 0000000..6502d9f
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __VFSTEST_H
+#define __VFSTEST_H
+
+void test_setup(struct vfstest_info *info);
+void test_cleanup(struct vfstest_info *info);
+
+
+#endif /* __VFSTEST_H */
diff --git a/src/xfsfind.c b/src/xfsfind.c
new file mode 100644 (file)
index 0000000..6b0a93e
--- /dev/null
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * find(1) but with special predicates for finding XFS attributes.
+ * Copyright (C) 2022 Oracle.
+ */
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <ftw.h>
+#include <linux/fs.h>
+#include <xfs/xfs.h>
+
+#include "global.h"
+
+static int want_anyfile;
+static int want_datafile;
+static int want_attrfile;
+static int want_dir;
+static int want_regfile;
+static int want_sharedfile;
+static int report_errors = 1;
+
+static int
+check_datafile(
+       const char              *path,
+       int                     fd)
+{
+       off_t                   off;
+
+       off = lseek(fd, 0, SEEK_DATA);
+       if (off >= 0)
+               return 1;
+
+       if (errno == ENXIO)
+               return 0;
+
+       if (report_errors)
+               perror(path);
+
+       return -1;
+}
+
+static int
+check_attrfile(
+       const char              *path,
+       int                     fd)
+{
+       struct fsxattr          fsx;
+       int                     ret;
+
+       ret = ioctl(fd, XFS_IOC_FSGETXATTR, &fsx);
+       if (ret) {
+               if (report_errors)
+                       perror(path);
+               return -1;
+       }
+
+       if (want_attrfile && (fsx.fsx_xflags & XFS_XFLAG_HASATTR))
+               return 1;
+       return 0;
+}
+
+#define BMAP_NR                        33
+static struct getbmapx         bmaps[BMAP_NR];
+
+static int
+check_sharedfile(
+       const char              *path,
+       int                     fd)
+{
+       struct getbmapx         *key = &bmaps[0];
+       unsigned int            i;
+       int                     ret;
+
+       memset(key, 0, sizeof(struct getbmapx));
+       key->bmv_length = ULLONG_MAX;
+       /* no holes and don't flush dirty pages */
+       key->bmv_iflags = BMV_IF_DELALLOC | BMV_IF_NO_HOLES;
+       key->bmv_count = BMAP_NR;
+
+       while ((ret = ioctl(fd, XFS_IOC_GETBMAPX, bmaps)) == 0) {
+               struct getbmapx *p = &bmaps[1];
+               xfs_off_t       new_off;
+
+               for (i = 0; i < key->bmv_entries; i++, p++) {
+                       if (p->bmv_oflags & BMV_OF_SHARED)
+                               return 1;
+               }
+
+               if (key->bmv_entries == 0)
+                       break;
+               p = key + key->bmv_entries;
+               if (p->bmv_oflags & BMV_OF_LAST)
+                       return 0;
+
+               new_off = p->bmv_offset + p->bmv_length;
+               key->bmv_length -= new_off - key->bmv_offset;
+               key->bmv_offset = new_off;
+       }
+       if (ret < 0) {
+               if (report_errors)
+                       perror(path);
+               return -1;
+       }
+
+       return 0;
+}
+
+static void
+print_help(
+       const char              *name)
+{
+       printf("Usage: %s [OPTIONS] path\n", name);
+       printf("\n");
+       printf("Print all file paths matching any of the given predicates.\n");
+       printf("\n");
+       printf("-a      Match files with xattrs.\n");
+       printf("-b      Match files with data blocks.\n");
+       printf("-d      Match directories.\n");
+       printf("-q      Ignore errors while walking directory tree.\n");
+       printf("-r      Match regular files.\n");
+       printf("-s      Match files with shared blocks.\n");
+       printf("\n");
+       printf("If no matching options are given, match all files found.\n");
+}
+
+static int
+visit(
+       const char              *path,
+       const struct stat       *sb,
+       int                     typeflag,
+       struct FTW              *ftwbuf)
+{
+       int                     printme = 1;
+       int                     fd = -1;
+       int                     retval = FTW_CONTINUE;
+
+       if (want_anyfile)
+               goto out;
+       if (want_regfile && typeflag == FTW_F)
+               goto out;
+       if (want_dir && typeflag == FTW_D)
+               goto out;
+
+       /*
+        * We can only open directories and files; screen out everything else.
+        * Note that nftw lies and reports FTW_F for device files, so check the
+        * statbuf mode too.
+        */
+       if (typeflag != FTW_F && typeflag != FTW_D) {
+               printme = 0;
+               goto out;
+       }
+
+       if (!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) {
+               printme = 0;
+               goto out;
+       }
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0) {
+               if (report_errors) {
+                       perror(path);
+                       return FTW_STOP;
+               }
+
+               return FTW_CONTINUE;
+       }
+
+       if (want_datafile && typeflag == FTW_F) {
+               int ret = check_datafile(path, fd);
+               if (ret < 0 && report_errors) {
+                       printme = 0;
+                       retval = FTW_STOP;
+                       goto out_fd;
+               }
+
+               if (ret == 1)
+                       goto out_fd;
+       }
+
+       if (want_attrfile) {
+               int ret = check_attrfile(path, fd);
+               if (ret < 0 && report_errors) {
+                       printme = 0;
+                       retval = FTW_STOP;
+                       goto out_fd;
+               }
+
+               if (ret == 1)
+                       goto out_fd;
+       }
+
+       if (want_sharedfile) {
+               int ret = check_sharedfile(path, fd);
+               if (ret < 0 && report_errors) {
+                       printme = 0;
+                       retval = FTW_STOP;
+                       goto out_fd;
+               }
+
+               if (ret == 1)
+                       goto out_fd;
+       }
+
+       printme = 0;
+out_fd:
+       close(fd);
+out:
+       if (printme)
+               printf("%s\n", path);
+       return retval;
+}
+
+static void
+handle_sigabrt(
+       int             signal,
+       siginfo_t       *info,
+       void            *ucontext)
+{
+       fprintf(stderr, "Signal %u, exiting.\n", signal);
+       exit(2);
+}
+
+int
+main(
+       int                     argc,
+       char                    *argv[])
+{
+       struct rlimit           rlimit;
+       struct sigaction        abrt = {
+               .sa_sigaction   = handle_sigabrt,
+               .sa_flags       = SA_SIGINFO,
+       };
+       int                     c;
+       int                     ret;
+
+       while ((c = getopt(argc, argv, "abdqrs")) >= 0) {
+               switch (c) {
+               case 'a':       want_attrfile = 1;   break;
+               case 'b':       want_datafile = 1;   break;
+               case 'd':       want_dir = 1;        break;
+               case 'q':       report_errors = 0;   break;
+               case 'r':       want_regfile = 1;    break;
+               case 's':       want_sharedfile = 1; break;
+               default:
+                       print_help(argv[0]);
+                       return 1;
+               }
+       }
+
+       ret = getrlimit(RLIMIT_NOFILE, &rlimit);
+       if (ret) {
+               perror("RLIMIT_NOFILE");
+               return 1;
+       }
+
+       if (!want_attrfile && !want_datafile && !want_dir && !want_regfile &&
+           !want_sharedfile)
+               want_anyfile = 1;
+
+       /*
+        * nftw is known to abort() if a directory it is walking disappears out
+        * from under it.  Handle this with grace if the caller wants us to run
+        * quietly.
+        */
+       if (!report_errors) {
+               ret = sigaction(SIGABRT, &abrt, NULL);
+               if (ret) {
+                       perror("SIGABRT handler");
+                       return 1;
+               }
+       }
+
+       for (c = optind; c < argc; c++) {
+               ret = nftw(argv[c], visit, rlimit.rlim_cur - 5,
+                               FTW_ACTIONRETVAL | FTW_CHDIR | FTW_MOUNT |
+                               FTW_PHYS);
+               if (ret && report_errors) {
+                       perror(argv[c]);
+                       break;
+               }
+       }
+
+       if (ret)
+               return 1;
+       return 0;
+}
index cf60573048fbe6b91d2866ec0de6325442a21534..fae6d9d19a389a9d29abcebd2c45740fe75d65db 100755 (executable)
@@ -141,8 +141,9 @@ _test_replace()
        _devmgt_remove ${removed_dev_htl} $ds
        dev_removed=1
 
-       $BTRFS_UTIL_PROG filesystem show $SCRATCH_DEV | grep "Some devices missing" >> $seqres.full || _fail \
-                                                       "btrfs did not report device missing"
+       $BTRFS_UTIL_PROG filesystem show $SCRATCH_DEV | \
+               grep -ie '\bmissing\b' >> $seqres.full || \
+               _fail "btrfs did not report device missing"
 
        # add a new disk to btrfs
        ds=${devs[@]:$(($n)):1}
index 4e767a2f0c43a8f23805a12904d1d124a592b928..381ad072fdcfadea2ead07f32a7c0a294ebf4631 100755 (executable)
@@ -10,7 +10,7 @@
 # We check to end up back at the original file with the correct offset.
 #
 . ./common/preamble
-_begin_fstest auto rw metadata
+_begin_fstest auto rw metadata fiemap logical_resolve compress
 
 noise_pid=0
 
@@ -201,7 +201,7 @@ workout()
        cnt=0
        errcnt=0
        dir="$SCRATCH_MNT/$snap_name/"
-       for file in `find $dir -name f\* -size +0 | sort -R`; do
+       for file in `find $dir -name f\* -size +0 | shuf`; do
                extents=`_check_file_extents $file`
                ret=$?
                if [ $ret -ne 0 ]; then
index 878a8c7c0eb826585954e94328b4ffed99e3f1b3..846347708179b7f1c36e619a73be7eb4cb9719d2 100755 (executable)
@@ -7,7 +7,7 @@
 # Btrfs Online defragmentation tests
 #
 . ./common/preamble
-_begin_fstest auto defrag
+_begin_fstest auto defrag compress
 cnt=119
 filesize=48000
 
index 0665e12627ae8ccf4d6f12aeb5c98d8d58133512..d8b5a97827503296fff6d56683d30c5374cefc6f 100755 (executable)
@@ -20,7 +20,7 @@
 # performed, a btrfsck run, and finally the filesystem is remounted.
 #
 . ./common/preamble
-_begin_fstest auto replace volume
+_begin_fstest auto replace volume scrub
 
 noise_pid=0
 
@@ -48,6 +48,7 @@ _require_scratch_dev_pool 5
 _require_scratch_dev_pool_equal_size
 _require_scratch_size $((10 * 1024 * 1024)) #kB
 _require_command "$WIPEFS_PROG" wipefs
+_btrfs_get_profile_configs dup
 
 rm -f $tmp.*
 
@@ -59,6 +60,7 @@ wait_time=1
 fill_scratch()
 {
        local fssize=$1
+       local with_cancel=$2
        local filler_pid
 
        # Fill inline extents.
@@ -87,7 +89,11 @@ fill_scratch()
        $XFS_IO_PROG -f -d -c "pwrite -b 64k 0 1E" "$SCRATCH_MNT/t_filler" &>\
                $tmp.filler_result &
        filler_pid=$!
-       sleep $((2 * $wait_time))
+       if [ "${with_cancel}" = "cancel" ]; then
+               sleep $((10 * $wait_time))
+       else
+               sleep $((2 * $wait_time))
+       fi
        kill -KILL $filler_pid &> /dev/null
        wait $filler_pid &> /dev/null
 
@@ -124,7 +130,7 @@ workout()
        _scratch_mount
        _require_fs_space $SCRATCH_MNT $((2 * 512 * 1024)) #2.5G
 
-       fill_scratch $fssize
+       fill_scratch $fssize $with_cancel
        _run_btrfs_util_prog filesystem show -m $SCRATCH_MNT
 
        echo -e "Replace from $source_dev to $SPARE_DEV\\n" >> $seqres.full
@@ -133,7 +139,7 @@ workout()
        _run_btrfs_util_prog filesystem show -m $SCRATCH_MNT
 
        # Skip -r test for configs without mirror OR replace cancel
-       if echo $mkfs_options | egrep -qv "raid1|raid5|raid6|raid10" || \
+       if echo $mkfs_options | grep -E -qv "raid1|raid5|raid6|raid10" || \
           [ "${with_cancel}Q" = "cancelQ" ]; then
                _scratch_unmount > /dev/null 2>&1
                _scratch_dev_pool_put
@@ -237,19 +243,22 @@ btrfs_replace_test()
        fi
 }
 
-workout "-m single -d single" 1 no 64
-# Mixed BG & RAID/DUP profiles are not supported on zoned btrfs
-if ! _scratch_btrfs_is_zoned; then
-       workout "-m dup -d single" 1 no 64
-       workout "-m dup -d single" 1 cancel 1024
-       workout "-m raid0 -d raid0" 2 no 64
-       workout "-m raid1 -d raid1" 2 no 2048
-       workout "-m raid10 -d raid10" 4 no 64
-       workout "-m single -d single -M" 1 no 64
-       workout "-m dup -d dup -M" 1 no 64
-       workout "-m raid5 -d raid5" 2 no 64
-       workout "-m raid6 -d raid6" 3 no 64
-fi
+for t in "-m single -d single:1 no 64" \
+       "-m dup -d single:1 no 64" \
+       "-m dup -d single:1 cancel 1024" \
+       "-m raid0 -d raid0:2 no 64" \
+       "-m raid1 -d raid1:2 no 2048" \
+       "-m raid10 -d raid10:4 no 64" \
+       "-m single -d single -M:1 no 64" \
+       "-m dup -d dup -M:1 no 64" \
+       "-m raid5 -d raid5:2 no 64" \
+       "-m raid6 -d raid6:3 no 64"; do
+       mkfs_option=${t%:*}
+       workout_option=${t#*:}
+       if [[ "${_btrfs_profile_configs[@]}" =~ "${mkfs_option/ -M}"( |$) ]]; then
+               workout "$mkfs_option" $workout_option
+       fi
+done
 
 echo "*** done"
 status=0
index 60461a3425451d3bd12831886e423b70b2418fa1..ab90c8a7208b5b46f0a5a4fb51390828902c56cd 100755 (executable)
@@ -31,7 +31,10 @@ _require_command "$E2FSCK_PROG" e2fsck
 # ext4 does not support zoned block device
 _require_non_zoned_device "${SCRATCH_DEV}"
 _require_loop
+_require_extra_fs ext4
 
+SOURCE_DIR=/etc
+BASENAME=$(basename $SOURCE_DIR)
 BLOCK_SIZE=`_get_block_size $TEST_DIR`
 
 # Create & populate an ext4 filesystem
@@ -40,9 +43,9 @@ $MKFS_EXT4_PROG -F -b $BLOCK_SIZE $SCRATCH_DEV > $seqres.full 2>&1 || \
 # Manual mount so we don't use -t btrfs or selinux context
 mount -t ext4 $SCRATCH_DEV $SCRATCH_MNT
 
-_require_fs_space $SCRATCH_MNT $(du -s /lib/modules/`uname -r` | ${AWK_PROG} '{print $1}')
+_require_fs_space $SCRATCH_MNT $(du -s $SOURCE_DIR | ${AWK_PROG} '{print $1}')
 
-cp -aR /lib/modules/`uname -r`/ $SCRATCH_MNT
+$TIMEOUT_PROG 10 cp -aRP $SOURCE_DIR $SCRATCH_MNT
 _scratch_unmount
 
 # Convert it to btrfs, mount it, verify the data
@@ -50,7 +53,7 @@ $BTRFS_CONVERT_PROG $SCRATCH_DEV >> $seqres.full 2>&1 || \
        _fail "btrfs-convert failed"
 _try_scratch_mount || _fail "Could not mount new btrfs fs"
 # (Ignore the symlinks which may be broken/nonexistent)
-diff -r /lib/modules/`uname -r`/ $SCRATCH_MNT/`uname -r`/ 2>&1 | grep -vw "source\|build"
+diff --no-dereference -r $SOURCE_DIR $SCRATCH_MNT/$BASENAME/ 2>&1
 
 # Old ext4 image file should exist & be consistent
 $E2FSCK_PROG -fn $SCRATCH_MNT/ext2_saved/image >> $seqres.full 2>&1 || \
@@ -61,12 +64,12 @@ mkdir -p $SCRATCH_MNT/mnt
 mount -o loop $SCRATCH_MNT/ext2_saved/image $SCRATCH_MNT/mnt || \
        _fail "could not loop mount saved ext4 image"
 # Ignore the symlinks which may be broken/nonexistent
-diff -r /lib/modules/`uname -r`/ $SCRATCH_MNT/mnt/`uname -r`/ 2>&1 | grep -vw "source\|build"
+diff --no-dereference -r $SOURCE_DIR $SCRATCH_MNT/mnt/$BASENAME/ 2>&1
 umount $SCRATCH_MNT/mnt
 
 # Now put some fresh data on the btrfs fs
 mkdir -p $SCRATCH_MNT/new 
-cp -aR /lib/modules/`uname -r`/ $SCRATCH_MNT/new
+$TIMEOUT_PROG 10 cp -aRP $SOURCE_DIR $SCRATCH_MNT/new
 
 _scratch_unmount
 
@@ -81,7 +84,7 @@ $E2FSCK_PROG -fn $SCRATCH_DEV >> $seqres.full 2>&1 || \
 # Mount the un-converted ext4 device & check the contents
 mount -t ext4 $SCRATCH_DEV $SCRATCH_MNT
 # (Ignore the symlinks which may be broken/nonexistent)
-diff -r /lib/modules/`uname -r`/ $SCRATCH_MNT/`uname -r`/ 2>&1 | grep -vw "source\|build"
+diff --no-dereference -r $SOURCE_DIR $SCRATCH_MNT/$BASENAME/ 2>&1
 
 _scratch_unmount
 
index 1335b8cb321ded48cbf310728407ad25ad87d387..459b6e80a312f51a1a9a8b0fd98058a4d0feaee9 100755 (executable)
@@ -11,7 +11,7 @@
 # dmesg to see if there was a csum error.
 #
 . ./common/preamble
-_begin_fstest auto quick balance
+_begin_fstest auto quick balance prealloc
 
 # Import common functions.
 . ./common/filter
index 7398f9bfd84ccda6a212d94109f816208761b67c..aeec8cc917c5dbe07d1260a2abac299575814717 100755 (executable)
@@ -8,7 +8,7 @@
 # readonly and remounting rw.
 #
 . ./common/preamble
-_begin_fstest auto quick snapshot
+_begin_fstest auto quick snapshot remount
 
 # Import common functions.
 . ./common/filter
index 6d05da54800840b2407674caa50d856f2f466ae0..37119363af26921e52826611e3b5d283b1e20bfd 100755 (executable)
@@ -7,63 +7,53 @@
 # btrfs send hole punch test
 #
 . ./common/preamble
-_begin_fstest auto quick send
+_begin_fstest auto quick send prealloc
 
 tmp=`mktemp -d`
 tmp_dir=send_temp_$seq
 
-# Override the default cleanup function.
-_cleanup()
-{
-       $BTRFS_UTIL_PROG subvolume delete $TEST_DIR/$tmp_dir/snap > /dev/null 2>&1
-       $BTRFS_UTIL_PROG subvolume delete $TEST_DIR/$tmp_dir/snap1 > /dev/null 2>&1
-       $BTRFS_UTIL_PROG subvolume delete $TEST_DIR/$tmp_dir/send > /dev/null 2>&1
-       rm -rf $TEST_DIR/$tmp_dir
-       rm -f $tmp.*
-}
-
 # Import common functions.
 . ./common/filter
 
 # real QA test starts here
 _supported_fs btrfs
-_require_test
 _require_scratch
 _require_fssum
 _require_xfs_io_command "falloc"
 
 _scratch_mkfs > /dev/null 2>&1
-
-#receive needs to be able to setxattrs, including the selinux context, if we use
-#the normal nfs context thing it screws up our ability to set the
-#security.selinux xattrs so we need to disable this for this test
-export SELINUX_MOUNT_OPTIONS=""
-
 _scratch_mount
 
-mkdir $TEST_DIR/$tmp_dir
-$BTRFS_UTIL_PROG subvolume create $TEST_DIR/$tmp_dir/send \
+mkdir $SCRATCH_MNT/$tmp_dir
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/$tmp_dir/send \
        > $seqres.full 2>&1 || _fail "failed subvolume create"
 
-_ddt of=$TEST_DIR/$tmp_dir/send/foo bs=1M count=10 >> $seqres.full \
+_ddt of=$SCRATCH_MNT/$tmp_dir/send/foo bs=1M count=10 >> $seqres.full \
        2>&1 || _fail "dd failed"
-$BTRFS_UTIL_PROG subvolume snapshot -r $TEST_DIR/$tmp_dir/send \
-       $TEST_DIR/$tmp_dir/snap >> $seqres.full 2>&1 || _fail "failed snap"
-$XFS_IO_PROG -c "fpunch 1m 1m" $TEST_DIR/$tmp_dir/send/foo
-$BTRFS_UTIL_PROG subvolume snapshot -r $TEST_DIR/$tmp_dir/send \
-       $TEST_DIR/$tmp_dir/snap1 >> $seqres.full 2>&1 || _fail "failed snap"
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT/$tmp_dir/send \
+       $SCRATCH_MNT/$tmp_dir/snap >> $seqres.full 2>&1 || _fail "failed snap"
+$XFS_IO_PROG -c "fpunch 1m 1m" $SCRATCH_MNT/$tmp_dir/send/foo
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT/$tmp_dir/send \
+       $SCRATCH_MNT/$tmp_dir/snap1 >> $seqres.full 2>&1 || _fail "failed snap"
 
-$FSSUM_PROG -A -f -w $tmp/fssum.snap $TEST_DIR/$tmp_dir/snap >> $seqres.full \
+# -A disable access time check.
+# And -T disable xattrs to prevent SELinux changes causing false alerts, and the
+# test case only cares about hole punching.
+$FSSUM_PROG -AT -f -w $tmp/fssum.snap $SCRATCH_MNT/$tmp_dir/snap >> $seqres.full \
        2>&1 || _fail "fssum gen failed"
-$FSSUM_PROG -A -f -w $tmp/fssum.snap1 $TEST_DIR/$tmp_dir/snap1 >> $seqres.full \
+$FSSUM_PROG -AT -f -w $tmp/fssum.snap1 $SCRATCH_MNT/$tmp_dir/snap1 >> $seqres.full \
        2>&1 || _fail "fssum gen failed"
 
-$BTRFS_UTIL_PROG send -f $tmp/send.snap $TEST_DIR/$tmp_dir/snap >> \
+$BTRFS_UTIL_PROG send -f $tmp/send.snap $SCRATCH_MNT/$tmp_dir/snap >> \
        $seqres.full 2>&1 || _fail "failed send"
-$BTRFS_UTIL_PROG send -p $TEST_DIR/$tmp_dir/snap \
-       -f $tmp/send.snap1 $TEST_DIR/$tmp_dir/snap1 \
+$BTRFS_UTIL_PROG send -p $SCRATCH_MNT/$tmp_dir/snap \
+       -f $tmp/send.snap1 $SCRATCH_MNT/$tmp_dir/snap1 \
        >> $seqres.full 2>&1 || _fail "failed send"
 
+_scratch_unmount
+_scratch_mkfs > /dev/null 2>&1
+_scratch_mount
+
 $BTRFS_UTIL_PROG receive -f $tmp/send.snap $SCRATCH_MNT >> $seqres.full 2>&1 \
        || _fail "failed recv"
 $BTRFS_UTIL_PROG receive -f $tmp/send.snap1 $SCRATCH_MNT >> $seqres.full 2>&1 \
@@ -75,4 +65,5 @@ $FSSUM_PROG -r $tmp/fssum.snap1 $SCRATCH_MNT/snap1 >> $seqres.full 2>&1 \
        || _fail "fssum failed"
 
 echo "Silence is golden"
-status=0 ; exit
+status=0
+exit
index 6220710181bb17dedb0323043ffb2a213fae953d..496cc7df1b199eec1bd3413a65ad7824fd4ae29b 100755 (executable)
@@ -22,6 +22,7 @@ _begin_fstest auto quick qgroup
 
 _supported_fs btrfs
 _require_scratch
+_require_scratch_qgroup
 _require_cloner
 
 # Currently in btrfs the node/leaf size can not be smaller than the page
index 5943da2f0c2b6e6f98da8cd13725e253d7152820..1b55834aa99cd6ffed08d006312b60ffa2f12e15 100755 (executable)
@@ -22,8 +22,10 @@ run_test()
 
        sleep 0.5
 
+       # In new versions of btrfs-progs (6.0+), the defrag command outputs to
+       # stdout the path of the files it operates on. So ignore that.
        find $SCRATCH_MNT -type f -print0 | xargs -0 \
-       $BTRFS_UTIL_PROG filesystem defrag -f
+               $BTRFS_UTIL_PROG filesystem defrag -f > /dev/null
 
        sync
        wait
index e2d37b0949b018dd26ec5b3c004c3cc3efb41bb8..32ad80bf9c64e8c5f6819f2c01c4b153d1f7b62e 100755 (executable)
@@ -4,7 +4,7 @@
 #
 # FS QA Test No. 022
 #
-# Test the basic functionality of qgroups
+# Test the basic qgroup exceed case
 #
 . ./common/preamble
 _begin_fstest auto qgroup limit
@@ -14,61 +14,11 @@ _begin_fstest auto qgroup limit
 
 _supported_fs btrfs
 _require_scratch
+_require_qgroup_rescan
 _require_btrfs_qgroup_report
 
-# Test to make sure we can actually turn it on and it makes sense
-_basic_test()
-{
-       echo "=== basic test ===" >> $seqres.full
-       _run_btrfs_util_prog subvolume create $SCRATCH_MNT/a
-       _run_btrfs_util_prog quota enable $SCRATCH_MNT/a
-       _run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
-       subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT a)
-       $BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep $subvolid >> \
-               $seqres.full 2>&1
-       [ $? -eq 0 ] || _fail "couldn't find our subvols quota group"
-       run_check $FSSTRESS_PROG -d $SCRATCH_MNT/a -w -p 1 -n 2000 \
-               $FSSTRESS_AVOID
-       _run_btrfs_util_prog subvolume snapshot $SCRATCH_MNT/a \
-               $SCRATCH_MNT/b
-
-       # the shared values of both the original subvol and snapshot should
-       # match
-       a_shared=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
-       a_shared=$(echo $a_shared | $AWK_PROG '{ print $2 }')
-       echo "subvol a id=$subvolid" >> $seqres.full
-       subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT b)
-       echo "subvol b id=$subvolid" >> $seqres.full
-       b_shared=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
-       b_shared=$(echo $b_shared | $AWK_PROG '{ print $2 }')
-       $BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT >> $seqres.full
-       [ $b_shared -eq $a_shared ] || _fail "shared values don't match"
-}
-
-#enable quotas, do some work, check our values and then rescan and make sure we
-#come up with the same answer
-_rescan_test()
-{
-       echo "=== rescan test ===" >> $seqres.full
-       # first with a blank subvol
-       _run_btrfs_util_prog subvolume create $SCRATCH_MNT/a
-       _run_btrfs_util_prog quota enable $SCRATCH_MNT/a
-       subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT a)
-       run_check $FSSTRESS_PROG -d $SCRATCH_MNT/a -w -p 1 -n 2000 \
-               $FSSTRESS_AVOID
-       sync
-       output=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
-       echo "qgroup values before rescan: $output" >> $seqres.full
-       refer=$(echo $output | $AWK_PROG '{ print $2 }')
-       excl=$(echo $output | $AWK_PROG '{ print $3 }')
-       _run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
-       output=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
-       echo "qgroup values after rescan: $output" >> $seqres.full
-       [ $refer -eq $(echo $output | $AWK_PROG '{ print $2 }') ] || \
-               _fail "reference values don't match after rescan"
-       [ $excl -eq $(echo $output | $AWK_PROG '{ print $3 }') ] || \
-               _fail "exclusive values don't match after rescan"
-}
+# This test requires specific data usage, skip if we have compression enabled
+_require_no_compress
 
 #basic exceed limit testing
 _limit_test_exceed()
@@ -81,43 +31,14 @@ _limit_test_exceed()
        _ddt of=$SCRATCH_MNT/a/file bs=10M count=1 >> $seqres.full 2>&1
        [ $? -ne 0 ] || _fail "quota should have limited us"
 }
-
-#basic noexceed limit testing
-_limit_test_noexceed()
-{
-       echo "=== limit not exceed test ===" >> $seqres.full
-       _run_btrfs_util_prog subvolume create $SCRATCH_MNT/a
-       _run_btrfs_util_prog quota enable $SCRATCH_MNT
-       subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT a)
-       _run_btrfs_util_prog qgroup limit 5M 0/$subvolid $SCRATCH_MNT
-       _ddt of=$SCRATCH_MNT/a/file bs=4M count=1 >> $seqres.full 2>&1
-       [ $? -eq 0 ] || _fail "should have been allowed to write"
-}
-
 units=`_btrfs_qgroup_units`
 
-_scratch_mkfs > /dev/null 2>&1
-_scratch_mount
-_basic_test
-_scratch_unmount
-_check_scratch_fs
-
-_scratch_mkfs > /dev/null 2>&1
-_scratch_mount
-_rescan_test
-_scratch_unmount
-_check_scratch_fs
-
 _scratch_mkfs > /dev/null 2>&1
 _scratch_mount
 _limit_test_exceed
 _scratch_unmount
 _check_scratch_fs
 
-_scratch_mkfs > /dev/null 2>&1
-_scratch_mount
-_limit_test_noexceed
-
 # success, all done
 echo "Silence is golden"
 status=0
index 95c734eca1248d0a9efa3ea17a867696adb8b018..a8ca0e1d32d6bb9c22b8775429125e114b6dcec4 100755 (executable)
@@ -17,6 +17,7 @@ _begin_fstest auto quick compress
 # real QA test starts here
 _supported_fs btrfs
 _require_scratch
+_require_btrfs_no_nodatacow
 
 __workout()
 {
index b9ffd8cc5b8591905532a72e56fe14b820ff352e..26f95c7d9a1eb836f58e6cb7a966d19b551657c0 100755 (executable)
@@ -11,7 +11,7 @@
 # causing the receive command to abort immediately.
 #
 . ./common/preamble
-_begin_fstest auto quick send clone
+_begin_fstest auto quick send clone prealloc
 
 tmp=`mktemp -d`
 
index 46c14b9c1c1f5a9b9aecd18bbb2ee7c43c2097fb..dbf12c26d0cdd8f38c2793ea069f232c92ebf033 100755 (executable)
@@ -7,7 +7,7 @@
 # Test replace of a missing device on various data and metadata profiles.
 #
 . ./common/preamble
-_begin_fstest auto replace volume
+_begin_fstest auto replace volume scrub
 
 # Import common functions.
 . ./common/filter
index fe0678f867dce8f32f637bf03c2368be55a44831..d860974ec207320d21929092d7c0eedf0c38b95f 100755 (executable)
@@ -25,7 +25,7 @@ _scratch_mkfs >/dev/null
 _scratch_mount
 
 _run_btrfs_util_prog quota enable $SCRATCH_MNT
-_run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT
 
 # Increase the probability of generating de-refer extent, and decrease
 # other.
@@ -35,7 +35,7 @@ args=`_scale_fsstress_args -z \
        -f fsync=10 -n 100000 -p 2 \
        -d $SCRATCH_MNT/stress_dir`
 echo "Run fsstress $args" >>$seqres.full
-$FSSTRESS_PROG $args >/dev/null 2>&1 &
+$FSSTRESS_PROG $args >>$seqres.full &
 fsstress_pid=$!
 
 echo "Start balance" >>$seqres.full
index 92ad5c38530978ad75e2f887a284d7929babf035..abda75db76a97f906c7437939f5c587b5ea948f9 100755 (executable)
@@ -8,7 +8,7 @@
 # bad detection of file holes.
 #
 . ./common/preamble
-_begin_fstest auto quick send
+_begin_fstest auto quick send prealloc
 
 tmp=`mktemp -d`
 
index 9ea43358109f5bf7a8461053abee5a9865b457d1..61193fdd2a7baaf1ed4b808a610c43d7094a04d3 100755 (executable)
@@ -15,7 +15,7 @@
 #   Btrfs: fix data corruption when reading/updating compressed extents
 #
 . ./common/preamble
-_begin_fstest auto quick compress
+_begin_fstest auto quick compress prealloc
 
 tmp=`mktemp -d`
 
index f654adaec308eac228deb66ee9bbdb95d675b1ae..8b65fb1f1ecbe40dafda65b5f123746c3fc089f4 100755 (executable)
@@ -14,7 +14,7 @@
 #   Btrfs: send, fix data corruption due to incorrect hole detection
 #
 . ./common/preamble
-_begin_fstest auto quick send
+_begin_fstest auto quick send preallocrw
 
 tmp=`mktemp -d`
 
index 93d4a17164023d08ef885e0ea3963be3343bbf4b..aa2030b12df679aff63463458bc3288ee773cf24 100755 (executable)
@@ -11,7 +11,7 @@
 #  btrfs: fix zstd compression parameter
 #
 . ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick compress subvol snapshot send
 
 # Override the default cleanup function.
 _cleanup()
@@ -30,6 +30,7 @@ _require_test
 _require_scratch
 _require_btrfs_command "property"
 _require_btrfs_command inspect-internal dump-super
+_require_btrfs_no_nodatacow
 
 send_files_dir=$TEST_DIR/btrfs-test-$seq
 
index 6993050b72027bc8cbcb8313b70002301634e864..c48e4087cc88b68b7d13d8208649677b9d4d344d 100755 (executable)
@@ -23,7 +23,7 @@ _spare_dev_get
 swapfile="$SCRATCH_MNT/swap"
 _scratch_pool_mkfs >/dev/null
 _scratch_mount
-_format_swapfile "$swapfile" $(($(get_page_size) * 10)) >/dev/null
+_format_swapfile "$swapfile" $(($(_get_page_size) * 10)) >/dev/null
 
 check_exclusive_ops()
 {
@@ -42,7 +42,7 @@ args=`_scale_fsstress_args -z \
        -f write=10 -f creat=10 \
        -n 1000 -p 2 -d $SCRATCH_MNT/stress_dir`
 echo "Run fsstress $args" >>$seqres.full
-$FSSTRESS_PROG $args >/dev/null 2>&1
+$FSSTRESS_PROG $args >>$seqres.full
 
 # Start and pause balance to ensure it will be restored on remount
 echo "Start balance" >>$seqres.full
index 4408de21b88e1c48c9540c79117563f3a315eeea..0d60d413250aecae0cc7485b8ca44412d4380556 100755 (executable)
@@ -9,7 +9,7 @@
 # file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone compress
 
 # Override the default cleanup function.
 _cleanup()
index fbd2e7d992cd78d93b20500efca34928e137cb7d..67239f10d3ab93ffe2a41a1f6518cfebdb2550e1 100755 (executable)
@@ -44,7 +44,7 @@ send_files_dir=$TEST_DIR/btrfs-test-$seq
 rm -fr $send_files_dir
 mkdir $send_files_dir
 
-_scratch_mkfs "-l $leaf_size" >/dev/null 2>&1
+_scratch_mkfs "--nodesize $leaf_size" >> $seqres.full 2>&1 || _fail "mkfs failed"
 _scratch_mount
 
 echo "hello world" > $SCRATCH_MNT/foobar
@@ -72,7 +72,7 @@ _run_btrfs_util_prog send -p $SCRATCH_MNT/mysnap1 -f $send_files_dir/2.snap \
 _scratch_unmount
 _check_scratch_fs
 
-_scratch_mkfs "-l $leaf_size" >/dev/null 2>&1
+_scratch_mkfs "--nodesize $leaf_size" >> $seqres.full 2>&1 || _fail "mkfs failed"
 _scratch_mount
 
 _run_btrfs_util_prog receive -f $send_files_dir/1.snap $SCRATCH_MNT
index e5ac516d77e801bc5f2af06cc682b2e0dd150d57..e126398327c4b12f1f2689c654eeff3882b496b2 100755 (executable)
@@ -15,7 +15,7 @@
 #    Btrfs: make fsync work after cloning into a file
 #
 . ./common/preamble
-_begin_fstest auto quick clone log
+_begin_fstest auto quick clone log compress
 
 # Override the default cleanup function.
 _cleanup()
index 782d854a057f82855d403efedc96fd97fd0cc192..e932a6572177efaba57e893468d148dbf8c497e2 100755 (executable)
@@ -15,6 +15,7 @@ _begin_fstest auto quick
 # real QA test starts here
 _supported_fs btrfs
 _require_scratch
+_require_qgroup_rescan
 
 _scratch_mkfs_sized $((1024 * 1024 * 1024)) >> $seqres.full 2>&1
 
index d2ced0ae4d549c12afa79f37f919c4360018428e..8b458b341f37b8c4ea7c43c43221e101876ded8d 100755 (executable)
@@ -11,7 +11,7 @@
 #     Btrfs: add missing compression property remove in btrfs_ioctl_setflags
 #
 . ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick compress
 
 # Override the default cleanup function.
 _cleanup()
@@ -28,6 +28,7 @@ _supported_fs btrfs
 _require_test
 _require_scratch
 _require_btrfs_command "property"
+_require_btrfs_no_nodatacow
 
 _scratch_mkfs >> $seqres.full 2>&1
 _scratch_mount
index 26db8a9bee20c2ad22a5820c22cdd6de7c16dedd..a018489160273d5bba6bb4658e1c74e5f9284d93 100755 (executable)
@@ -8,7 +8,7 @@
 # with fsstress running in background.
 #
 . ./common/preamble
-_begin_fstest auto balance subvol
+_begin_fstest auto balance subvol scrub
 
 # Import common functions.
 . ./common/filter
@@ -38,7 +38,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        echo -n "Start balance worker: " >>$seqres.full
index 55f5625b54909fef73c2e0f9dfdfce2852e0cd0b..c101041360cebb4d6a0e278ac0d0a063b2a9a98b 100755 (executable)
@@ -36,7 +36,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        echo -n "Start balance worker: " >>$seqres.full
index 47b0b9373f33926234fa23ad872cd202fc454975..818a0156668d8c89aa1148de26683b488610191d 100755 (executable)
@@ -8,7 +8,7 @@
 # running in background.
 #
 . ./common/preamble
-_begin_fstest auto balance defrag compress
+_begin_fstest auto balance defrag compress scrub
 
 # Import common functions.
 . ./common/filter
@@ -37,7 +37,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        echo -n "Start balance worker: " >>$seqres.full
index c96390b9315c08fb475f4c9510b511262ac48769..2f771baf7cea2b0e4b59f57421a698c0e0dee043 100755 (executable)
@@ -8,7 +8,7 @@
 # simultaneously, with fsstress running in background.
 #
 . ./common/preamble
-_begin_fstest auto balance remount compress
+_begin_fstest auto balance remount compress scrub
 
 # Import common functions.
 . ./common/filter
@@ -36,7 +36,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        echo -n "Start balance worker: " >>$seqres.full
index 74116188915005fb1e9ce2b890385ff346bb9718..e9b46ce665775de5f64dad77698b07f30dd496e3 100755 (executable)
@@ -10,7 +10,7 @@
 # run simultaneously. One of them is expected to fail when the other is running.
 
 . ./common/preamble
-_begin_fstest auto balance replace volume
+_begin_fstest auto balance replace volume scrub
 
 # Import common functions.
 . ./common/filter
@@ -46,7 +46,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        # Start both balance and replace in the background.
index 4ebf93267a59b667162ef59b94f2f54d30631c3c..c4b6aafe7d263e9b85b7ec521cc28ede2995db2f 100755 (executable)
@@ -8,7 +8,7 @@
 # operation simultaneously, with fsstress running in background.
 #
 . ./common/preamble
-_begin_fstest auto subvol replace volume
+_begin_fstest auto subvol replace volume scrub
 
 # Import common functions.
 . ./common/filter
@@ -46,7 +46,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        # make sure the stop sign is not there
index 8d12af616d89a11b6cd12e8f0f1e196af31d3f32..a29034bb3a7eecbbf9d93af0db782833a8c90b44 100755 (executable)
@@ -38,7 +38,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        # make sure the stop sign is not there
index 44803f9faf7f83f681501c9fcf288b8580d29818..709db155d20934888371966b171f3bcc68374197 100755 (executable)
@@ -8,7 +8,7 @@
 # operation simultaneously, with fsstress running in background.
 #
 . ./common/preamble
-_begin_fstest auto subvol defrag compress
+_begin_fstest auto subvol defrag compress scrub
 
 # Import common functions.
 . ./common/filter
@@ -39,7 +39,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        # make sure the stop sign is not there
index e03a4891ec890b9748b013ddefcf1588d3fc8933..15fd41db97b40d97914e1047f70ad0094cc2976e 100755 (executable)
@@ -9,7 +9,7 @@
 # in background.
 #
 . ./common/preamble
-_begin_fstest auto subvol remount compress
+_begin_fstest auto subvol remount compress scrub
 
 # Import common functions.
 . ./common/filter
@@ -39,7 +39,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        # make sure the stop sign is not there
index 6e798a2e5061129767528f7663c8da472300f382..139dde48a5f44c7c218c824fdd6c42f2bcce69f4 100755 (executable)
@@ -44,7 +44,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        echo -n "Start replace worker: " >>$seqres.full
index dcf978b36b0c15d41c664446759cd0645fd005e1..54aa275c7d0f724bd98019aede9a97614f121b70 100755 (executable)
@@ -8,7 +8,7 @@
 # running in background.
 #
 . ./common/preamble
-_begin_fstest auto replace defrag compress volume
+_begin_fstest auto replace defrag compress volume scrub
 
 # Import common functions.
 . ./common/filter
@@ -45,7 +45,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        echo -n "Start replace worker: " >>$seqres.full
index cd1de2642a96020b462a302791fac9845fdde875..6ebbd8cc3c54fcca66e2f69e83f06dbc3a47b5c0 100755 (executable)
@@ -8,7 +8,7 @@
 # algorithms simultaneously with fsstress running in background.
 #
 . ./common/preamble
-_begin_fstest auto replace remount compress volume
+_begin_fstest auto replace remount compress volume scrub
 
 # Import common functions.
 . ./common/filter
@@ -44,7 +44,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        echo -n "Start replace worker: " >>$seqres.full
index bcb0ea2546a65b35a1fc09f62f5bc466600d82f9..4b6b6fb57231729713a09d03b686ee02a7fed64d 100755 (executable)
@@ -37,7 +37,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        echo -n "Start scrub worker: " >>$seqres.full
index 26c5deb6c2a27a04f3b40e0eb4692c0809041ed7..b1604f94989aafdc503074bb4243c1ca3de839d6 100755 (executable)
@@ -36,7 +36,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        echo -n "Start scrub worker: " >>$seqres.full
index dc26d8c024976aaaa375d4035098543e1474b6b6..9b22c62029f0bb75960747dd9cc6572cc348a4da 100755 (executable)
@@ -8,7 +8,7 @@
 # simultaneously with fsstress running in background.
 #
 . ./common/preamble
-_begin_fstest auto defrag remount compress
+_begin_fstest auto defrag remount compress scrub
 
 # Import common functions.
 . ./common/filter
@@ -37,7 +37,7 @@ run_test()
 
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
 
        echo -n "Start defrag worker: " >>$seqres.full
index 89e9672d09e28e3a71ae53a83be7682d5be33beb..894a1ac7fa5dbca047fbf444c336de7151e256b9 100755 (executable)
@@ -5,10 +5,8 @@
 # FS QA Test No. btrfs/076
 #
 # Regression test for btrfs incorrect inode ratio detection.
-# This was fixed in the following linux kernel patch:
-#
-#     Btrfs: fix incorrect compression ratio detection
 #
+
 . ./common/preamble
 _begin_fstest auto quick compress
 
@@ -27,6 +25,20 @@ _cleanup()
 _supported_fs btrfs
 _require_test
 _require_scratch
+_fixed_by_kernel_commit 4bcbb3325513 \
+       "Btrfs: fix incorrect compression ratio detection"
+
+# An extent size can be up to BTRFS_MAX_UNCOMPRESSED
+max_extent_size=$(( 128 * 1024 ))
+if _scratch_btrfs_is_zoned; then
+       zone_append_max=$(cat "/sys/block/$(_short_dev $SCRATCH_DEV)/queue/zone_append_max_bytes")
+       if [[ $zone_append_max -gt 0 && $zone_append_max -lt $max_extent_size ]]; then
+               # Round down to PAGE_SIZE
+               max_extent_size=$(( $zone_append_max / 4096 * 4096 ))
+       fi
+fi
+file_size=$(( 10 * 1024 * 1024 ))
+expect=$(( (file_size + max_extent_size - 1) / max_extent_size ))
 
 _scratch_mkfs >> $seqres.full 2>&1
 _scratch_mount "-o compress=lzo"
@@ -34,7 +46,10 @@ _scratch_mount "-o compress=lzo"
 $XFS_IO_PROG -f -c "pwrite 0 10M" -c "fsync" \
        $SCRATCH_MNT/data >> $seqres.full 2>&1
 
-_extent_count $SCRATCH_MNT/data
+res=$(_extent_count $SCRATCH_MNT/data)
+if [[ $res -ne $expect ]]; then
+       _fail "Expected $expect extents, got $res"
+fi
 
 $XFS_IO_PROG -f -c "pwrite 0 $((4096*33))" -c "fsync" \
        $SCRATCH_MNT/data >> $seqres.full 2>&1
@@ -42,7 +57,11 @@ $XFS_IO_PROG -f -c "pwrite 0 $((4096*33))" -c "fsync" \
 $XFS_IO_PROG -f -c "pwrite 0 10M" -c "fsync" \
        $SCRATCH_MNT/data >> $seqres.full 2>&1
 
-_extent_count $SCRATCH_MNT/data
+res=$(_extent_count $SCRATCH_MNT/data)
+if [[ $res -ne $expect ]]; then
+       _fail "Expected $expect extents, got $res"
+fi
 
+echo "Silence is golden"
 status=0
 exit
index b99f7eb10a162c5007cfbb029df5266b38f2d575..248e095d91af6ad3f5fabab5f50802687024f621 100644 (file)
@@ -1,3 +1,2 @@
 QA output created by 076
-80
-80
+Silence is golden
index 1acd1855f4b94ab71bc93a08d823ad716f6cffab..22f57396e7b164dd9b44aeac3d4f4911d44507e1 100755 (executable)
@@ -18,7 +18,7 @@
 # btrfs: Fix the wrong condition judgment about subset extent map
 #
 . ./common/preamble
-_begin_fstest auto rw metadata
+_begin_fstest auto rw metadata fiemap prealloc
 
 # Override the default cleanup function.
 _cleanup()
index 45f5ad1919719673ec573bfd4ef9a0a1c6d7b03b..ac9d9b6422d196a27b417ca40893a8cc683dfae7 100755 (executable)
@@ -76,7 +76,7 @@ workout()
 # created fs doesn't get that feature enabled. With it enabled, the below fsck
 # call wouldn't fail. This feature hasn't been enabled by default since it was
 # introduced, but be safe and explicitly disable it.
-_scratch_mkfs -O list-all 2>&1 | grep -q '\bno\-holes\b'
+_scratch_mkfs -O list-all 2>&1 | grep -q '\bno-holes\b'
 if [ $? -eq 0 ]; then
        mkfs_options="-O ^no-holes"
 fi
index d9c5528b37be83e5442378fab70837a5a09e6fdf..59972ae7a117bbe6807631eb89c67e6557c52e33 100755 (executable)
@@ -18,27 +18,23 @@ _begin_fstest auto quick metadata
 
 # Import common functions.
 . ./common/filter
+. ./common/fail_make_request
 
 # real QA test starts here
 _supported_fs btrfs
 _require_scratch
 _require_fail_make_request
 
-SYSFS_BDEV=`_sysfs_dev $SCRATCH_DEV`
-
 enable_io_failure()
 {
-       echo 100 > $DEBUGFS_MNT/fail_make_request/probability
-       echo 1000 > $DEBUGFS_MNT/fail_make_request/times
-       echo 0 > $DEBUGFS_MNT/fail_make_request/verbose
-       echo 1 > $SYSFS_BDEV/make-it-fail
+       _allow_fail_make_request 100 1000 > /dev/null
+       _start_fail_scratch_dev > /dev/null
 }
 
 disable_io_failure()
 {
-       echo 0 > $SYSFS_BDEV/make-it-fail
-       echo 0 > $DEBUGFS_MNT/fail_make_request/probability
-       echo 0 > $DEBUGFS_MNT/fail_make_request/times
+       _stop_fail_scratch_dev > /dev/null
+       _disallow_fail_make_request > /dev/null
 }
 
 # We will abort a btrfs transaction later, which always produces a warning in
index f2cd00b2e26fb444afa00cc53bcfac70821cafdf..a71e03406f8c598f78f292b9ff51e647189d3276 100755 (executable)
@@ -19,6 +19,7 @@ _begin_fstest auto quick qgroup
 _supported_fs btrfs
 _require_scratch
 _require_cp_reflink
+_require_scratch_qgroup
 
 # use largest node/leaf size (64K) to allow the test to be run on arch with
 # page size > 4k.
@@ -35,7 +36,7 @@ _run_btrfs_util_prog subvolume create $SCRATCH_MNT/subv2
 _run_btrfs_util_prog subvolume create $SCRATCH_MNT/subv3
 
 _run_btrfs_util_prog quota enable $SCRATCH_MNT
-_run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT
 
 $XFS_IO_PROG -f -c "pwrite 0 256K" $SCRATCH_MNT/subv1/file1 | _filter_xfs_io
 cp --reflink $SCRATCH_MNT/subv1/file1 $SCRATCH_MNT/subv2/file1
index e94cf17b7ec40dc3c273d7e0718ae45cdb70a393..75804465d0ad17324289945f0a1c83b4946f5724 100755 (executable)
@@ -18,7 +18,7 @@
 #   Btrfs: incremental send, fix clone operations for compressed extents
 #
 . ./common/preamble
-_begin_fstest auto quick send
+_begin_fstest auto quick send compress
 
 # Override the default cleanup function.
 _cleanup()
index b3a5fc14e8d53e3e49725d8d18dae3d5466d5641..3bd34c72e8eb39a4dc5b46fb1c3dc1c0e3e3c31f 100755 (executable)
@@ -13,7 +13,7 @@
 # The regression was introduced in the 4.2-rc1 Linux kernel.
 #
 . ./common/preamble
-_begin_fstest auto quick metadata log
+_begin_fstest auto quick metadata log preallocrw
 
 # Override the default cleanup function.
 _cleanup()
index 9986a06e1d109d73ce16bd9ab0efce53a2708e0a..410e83ba176194f8409ef2f1416f76866f81559d 100755 (executable)
@@ -37,7 +37,7 @@ _run_btrfs_util_prog filesystem show -m $SCRATCH_MNT
 $BTRFS_UTIL_PROG filesystem show -m $SCRATCH_MNT | _filter_btrfs_filesystem_show
 
 error_devid=`$BTRFS_UTIL_PROG filesystem show -m $SCRATCH_MNT |\
-                       egrep $DMERROR_DEV | $AWK_PROG '{print $2}'`
+                       grep -E $DMERROR_DEV | $AWK_PROG '{print $2}'`
 
 snapshot_cmd="$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT"
 snapshot_cmd="$snapshot_cmd $SCRATCH_MNT/snap_\`date +'%H_%M_%S_%N'\`"
index f0436f9f508221acd7c96e8f8c6d863986fbdab0..e8ed8c5c735133fe19668f3b9a756fb535d6c506 100755 (executable)
@@ -38,7 +38,7 @@ _run_btrfs_util_prog filesystem show -m $SCRATCH_MNT
 $BTRFS_UTIL_PROG filesystem show -m $SCRATCH_MNT | _filter_btrfs_filesystem_show
 
 error_devid=`$BTRFS_UTIL_PROG filesystem show -m $SCRATCH_MNT |\
-                       egrep $DMERROR_DEV | $AWK_PROG '{print $2}'`
+                       grep -E $DMERROR_DEV | $AWK_PROG '{print $2}'`
 
 snapshot_cmd="$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT"
 snapshot_cmd="$snapshot_cmd $SCRATCH_MNT/snap_\`date +'%H_%M_%S_%N'\`"
index 499de6bfb89a7e67f6b32b1260a4f12f89c43326..c9528eb13d610d793ded40b51d4c51a583b96f1c 100755 (executable)
@@ -94,7 +94,7 @@ _explode_fs_tree 1 $SCRATCH_MNT/snap2/files-snap2
 # Enable qgroups now that we have our filesystem prepared. This
 # will kick off a scan which we will have to wait for.
 _run_btrfs_util_prog quota enable $SCRATCH_MNT
-_run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT
 
 # Remount to clear cache, force everything to disk
 _scratch_cycle_mount
index db295e7074046e27ac824b72ed2dd0bb2df96665..7444e4d5a7cb128d0e3e6bb7865994788aaadad1 100755 (executable)
@@ -26,7 +26,7 @@ test_clone_and_read_compressed_extent()
        _scratch_mkfs >>$seqres.full 2>&1
        _scratch_mount $mount_opts
 
-       PAGE_SIZE=$(get_page_size)
+       PAGE_SIZE=$(_get_page_size)
 
        # Create our test file with 16 pages worth of data in a single extent
        # that is going to be compressed no matter which compression algorithm
@@ -38,8 +38,9 @@ test_clone_and_read_compressed_extent()
        $CLONER_PROG -s 0 -d $((16 * $PAGE_SIZE)) -l $((16 * $PAGE_SIZE)) \
                $SCRATCH_MNT/foo $SCRATCH_MNT/foo
 
-       echo "File contents before unmount:"
-       od -t x1 $SCRATCH_MNT/foo | _filter_od
+       echo "Hash before unmount:" >> $seqres.full
+       old_md5=$(_md5_checksum "$SCRATCH_MNT/foo")
+       echo "$old_md5" >> $seqres.full
 
        # Remount the fs or clear the page cache to trigger the bug in btrfs.
        # Because the extent has an uncompressed length that is a multiple of 16
@@ -52,9 +53,15 @@ test_clone_and_read_compressed_extent()
        # correctly.
        _scratch_cycle_mount
 
-       echo "File contents after remount:"
+       echo "Hash after remount:" >> $seqres.full
        # Must match the digest we got before.
-       od -t x1 $SCRATCH_MNT/foo | _filter_od
+       new_md5=$(_md5_checksum "$SCRATCH_MNT/foo")
+       echo "$new_md5" >> $seqres.full
+       if [ "$old_md5" != "$new_md5" ]; then
+               echo "Hash mismatches after remount"
+       else
+               echo "Hash matches after remount"
+       fi
 }
 
 echo -e "\nTesting with zlib compression..."
index 1144a82fd7cba50cb743d07c960dada5bd932b4f..cd69cdd7f58a7320123fe9c4a5414c28b683a038 100644 (file)
@@ -2,22 +2,8 @@ QA output created by 106
 
 Testing with zlib compression...
 Pages modified: [0 - 15]
-File contents before unmount:
-0 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
-*
-40
-File contents after remount:
-0 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
-*
-40
+Hash matches after remount
 
 Testing with lzo compression...
 Pages modified: [0 - 15]
-File contents before unmount:
-0 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
-*
-40
-File contents after remount:
-0 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
-*
-40
+Hash matches after remount
index dac6b5b055f2c7a9c249fa15b559c2419804a77d..fd7aaad41ffb9b4d6959eac61b963dedc9bdcb67 100755 (executable)
@@ -8,7 +8,7 @@
 # corruption or data loss.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc compress
 
 # Import common functions.
 . ./common/filter
index f4d54962f2ea7402c4d9279af804d2a59de5131e..009f3b72172826a6c0085cc384bdc9c61d7a649f 100755 (executable)
@@ -24,8 +24,14 @@ _require_scratch
 _scratch_mkfs >/dev/null
 _scratch_mount
 _run_btrfs_util_prog quota enable $SCRATCH_MNT
-# The qgroup '1/10' does not exist and should be silently ignored
-_run_btrfs_util_prog subvolume snapshot -i 1/10 $SCRATCH_MNT $SCRATCH_MNT/snap1
+# The qgroup '1/10' does not exist. The kernel should either gives an error
+# (newer kernel with invalid qgroup detection) or ignore it (older kernel with
+# above fix).
+# Either way, we just ignore the output completely, and we will check if the fs
+# is still RW later.
+$BTRFS_UTIL_PROG subvolume snapshot -i 1/10 $SCRATCH_MNT $SCRATCH_MNT/snap1 >> $seqres.full 2>&1
+
+touch $SCRATCH_MNT/foobar
 
 echo "Silence is golden"
 
index 345317536f40fd835a3ce3b500d374e8e1060114..0d585a99972a3c22298f4f9c2df95c4cb0414f96 100755 (executable)
@@ -18,9 +18,7 @@ _supported_fs btrfs
 _require_scratch
 _require_btrfs_qgroup_report
 
-# Force a small leaf size to make it easier to blow out our root
-# subvolume tree
-_scratch_mkfs "--nodesize 16384" >/dev/null
+_scratch_mkfs >> $seqres.full
 _scratch_mount
 _run_btrfs_util_prog quota enable $SCRATCH_MNT
 
index c179eeec97d2ba9455fdad3476a41c19c44783a2..4c5b7e116a4d9ea811738f15c193ea4483d6a9c5 100755 (executable)
@@ -39,7 +39,7 @@ sync
 
 # enable quota and rescan to get correct number
 _run_btrfs_util_prog quota enable $SCRATCH_MNT
-_run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT
 
 # now balance data block groups to corrupt qgroup
 _run_btrfs_balance_start -d $SCRATCH_MNT >> $seqres.full
index b58f2aa282bd3441ce85479d06afe379a4702982..1b6e5d78b2f1f09475f0087e49d63a8ccbba7e34 100755 (executable)
@@ -22,7 +22,7 @@
 # Verify if all three checkpoints match
 #
 . ./common/preamble
-_begin_fstest replace volume balance
+_begin_fstest replace volume balance auto quick
 
 # Override the default cleanup function.
 _cleanup()
index 2b0edb65b4bfcfdcc60e71336ae624bb88f0d649..1bb24e00f98ace30fcd636e9b4cfcf5460b4e55e 100755 (executable)
@@ -28,7 +28,7 @@ _scratch_mkfs >/dev/null
 _scratch_mount "-o enospc_debug"
 
 _run_btrfs_util_prog quota enable $SCRATCH_MNT
-_run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT
 _run_btrfs_util_prog qgroup limit 512K 0/5 $SCRATCH_MNT
 
 # The amount of written data may change due to different nodesize at mkfs time,
index 1e072b285ecfeaaafa4ad8abe10d052a9d8f9ac3..d34fc8d53801b34436a5323081587e72df6ee321 100755 (executable)
@@ -20,6 +20,12 @@ _require_btrfs_command inspect-internal dump-super
 _require_btrfs_fs_feature free_space_tree
 # Zoned btrfs does not support space_cache(v1)
 _require_non_zoned_device "${SCRATCH_DEV}"
+# Block group tree does not support space_cache(v1)
+_require_btrfs_no_block_group_tree
+
+_scratch_mkfs >/dev/null 2>&1
+[ "$(_get_page_size)" -gt "$(_scratch_btrfs_sectorsize)" ] && \
+       _notrun "cannot run with subpage sectorsize"
 
 mkfs_v1()
 {
index b9ab8270f039108907d0c9a8a631139651b3af26..70e836a50c7b2c06ff6a8a428a94d174ae58a1a5 100755 (executable)
@@ -39,7 +39,7 @@ populate_data(){
        mkdir -p $data_path
        args=`_scale_fsstress_args -p 20 -n 100 $FSSTRESS_AVOID -d $data_path`
        echo "Run fsstress $args" >>$seqres.full
-       $FSSTRESS_PROG $args >/dev/null 2>&1 &
+       $FSSTRESS_PROG $args >>$seqres.full &
        fsstress_pid=$!
        wait $fsstress_pid
 }
index 64ed2450d30f649527d28c16ab6216255640f110..7d03c2602bacf72c2d2b7033fa9114094e325d69 100755 (executable)
@@ -7,7 +7,7 @@
 # Test that both incremental and full send operations preserve file holes.
 #
 . ./common/preamble
-_begin_fstest auto quick send
+_begin_fstest auto quick send fiemap
 
 # Override the default cleanup function.
 _cleanup()
index f99e58e706b95f95c0d767eb9cb0bf4b21e37e22..427fdede01b2430feb16ff9de595042e8d3d7a0f 100755 (executable)
@@ -18,6 +18,7 @@ _begin_fstest auto compress
 _supported_fs btrfs
 _require_scratch
 _require_btrfs_command property
+_require_btrfs_no_nodatacow
 
 algos=($(_btrfs_compression_algos))
 
index c4b09f9fc1daca12e0997eb40961bc5fe519f67b..f3d92ba468b6d18e5e8bbc01e0e4c59e1beaafd6 100755 (executable)
@@ -30,7 +30,7 @@ SUBVOL=$SCRATCH_MNT/subvol
 
 _run_btrfs_util_prog subvolume create $SUBVOL
 _run_btrfs_util_prog quota enable $SCRATCH_MNT
-_run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT
 _run_btrfs_util_prog qgroup limit -e 1G $SUBVOL
 
 # Write and delete files within 1G limits, multiple times
index c680fe0aef4153323eec36c7389d3603b4e4f537..5ce658860c055d880e11e90e12b0b8bcf86ada77 100755 (executable)
@@ -12,7 +12,7 @@
 #      commit 2e949b0a5592 ("Btrfs: fix invalid dereference in btrfs_retry_endio")
 #
 . ./common/preamble
-_begin_fstest auto quick read_repair
+_begin_fstest auto quick read_repair fiemap
 
 # Import common functions.
 . ./common/filter
@@ -22,9 +22,9 @@ _begin_fstest auto quick read_repair
 # Modify as appropriate.
 _supported_fs btrfs
 _require_scratch_dev_pool 2
-
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
 _require_btrfs_command inspect-internal dump-tree
-_require_command "$FILEFRAG_PROG" filefrag
 _require_odirect
 # Overwriting data is forbidden on a zoned block device
 _require_non_zoned_device "${SCRATCH_DEV}"
@@ -71,7 +71,7 @@ $XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 128K 0 128K" "$SCRATCH_MNT/foobar" |\
 echo "step 2......corrupt file extent" >>$seqres.full
 
 ${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar >> $seqres.full
-logical_in_btrfs=`${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar | _filter_filefrag | cut -d '#' -f 1`
+logical_in_btrfs=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
 physical=$(get_physical ${logical_in_btrfs} 1)
 devid=$(get_devid ${logical_in_btrfs} 1)
 devpath=$(get_device_path ${devid})
@@ -87,15 +87,7 @@ _scratch_mount
 # step 3, 128k dio read (this read can repair bad copy)
 echo "step 3......repair the bad copy" >>$seqres.full
 
-# since raid1 consists of two copies, and the bad copy was put on stripe #1
-# while the good copy lies on stripe #0, the bad copy only gets access when the
-# reader's pid % 2 == 1 is true
-while true; do
-       $XFS_IO_PROG -d -c "pread -b 128K 0 128K" "$SCRATCH_MNT/foobar" > /dev/null &
-       pid=$!
-       wait
-       [ $((pid % 2)) == 1 ] && break
-done
+_btrfs_direct_read_on_mirror 1 2 "$SCRATCH_MNT/foobar" 0 128K
 
 _scratch_unmount
 
index 9fdcb2aba6837f79858319bd942f8c9cc30ae43a..e1adb91e4d0a68e864c3d589cfe82357b01eea25 100755 (executable)
@@ -22,10 +22,11 @@ _begin_fstest auto quick read_repair
 
 # Modify as appropriate.
 _supported_fs btrfs
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
 _require_scratch_dev_pool 2
 
 _require_btrfs_command inspect-internal dump-tree
-_require_command "$FILEFRAG_PROG" filefrag
 
 get_physical()
 {
@@ -69,8 +70,7 @@ $XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 128K 0 128K" "$SCRATCH_MNT/foobar" |\
 # one in $SCRATCH_DEV_POOL
 echo "step 2......corrupt file extent" >>$seqres.full
 
-${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar >> $seqres.full
-logical_in_btrfs=`${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar | _filter_filefrag | cut -d '#' -f 1`
+logical_in_btrfs=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
 physical=$(get_physical ${logical_in_btrfs} 1)
 devid=$(get_devid ${logical_in_btrfs} 1)
 devpath=$(get_device_path ${devid})
@@ -85,16 +85,7 @@ _scratch_mount
 # step 3, 128k buffered read (this read can repair bad copy)
 echo "step 3......repair the bad copy" >>$seqres.full
 
-# since raid1 consists of two copies, and the bad copy was put on stripe #1
-# while the good copy lies on stripe #0, the bad copy only gets access when the
-# reader's pid % 2 == 1 is true
-while true; do
-       echo 3 > /proc/sys/vm/drop_caches
-       $XFS_IO_PROG -c "pread -b 128K 0 128K" "$SCRATCH_MNT/foobar" > /dev/null &
-       pid=$!
-       wait
-       [ $((pid % 2)) == 1 ] && break
-done
+_btrfs_buffered_read_on_mirror 1 2 "$SCRATCH_MNT/foobar" 0 128K
 
 _scratch_unmount
 
index 58d01addc4410b22a9accbc5a724664be7f15a44..7a63fb83fb10a2fd1112e066ee795f0597c6c349 100755 (executable)
@@ -27,7 +27,6 @@ _require_scratch_dev_pool 2
 _require_dm_target dust
 
 _require_btrfs_command inspect-internal dump-tree
-_require_command "$FILEFRAG_PROG" filefrag
 
 _scratch_dev_pool_get 2
 # step 1, create a raid1 btrfs which contains one 128k file.
@@ -46,8 +45,7 @@ $XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 128K 0 128K" "$SCRATCH_MNT/foobar" |\
 # step 2, corrupt the first 64k of stripe #1
 echo "step 2......corrupt file extent" >>$seqres.full
 
-${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar >> $seqres.full
-logical_in_btrfs=`${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar | _filter_filefrag | cut -d '#' -f 1`
+logical_in_btrfs=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
 echo "Logical offset is $logical_in_btrfs" >>$seqres.full
 _scratch_unmount
 
@@ -67,10 +65,8 @@ echo "step 3......repair the bad copy" >>$seqres.full
 
 $DMSETUP_PROG message dust-test 0 addbadblock $((physical / 512))
 $DMSETUP_PROG message dust-test 0 enable
-while [[ -z $( (( BASHPID % 2 == stripe )) &&
-              exec $XFS_IO_PROG -d -c "pread -b 128K 0 128K" "$SCRATCH_MNT/foobar") ]]; do
-       :
-done
+
+_btrfs_direct_read_on_mirror $stripe 2 "$SCRATCH_MNT/foobar" 0 128K
 
 _cleanup_dust
 
index 71db861d05718320a0d41a1a6ebcbf5e8204a077..b6ff2a424af6266c883f0aa5e9d33e119f0243b7 100755 (executable)
@@ -34,7 +34,6 @@ _require_scratch_dev_pool 2
 _require_dm_target dust
 
 _require_btrfs_command inspect-internal dump-tree
-_require_command "$FILEFRAG_PROG" filefrag
 
 _scratch_dev_pool_get 2
 # step 1, create a raid1 btrfs which contains one 128k file.
@@ -53,8 +52,7 @@ $XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 128K 0 128K" "$SCRATCH_MNT/foobar" |\
 # step 2, corrupt the first 64k of stripe #1
 echo "step 2......corrupt file extent" >>$seqres.full
 
-${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar >> $seqres.full
-logical_in_btrfs=`${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar | _filter_filefrag | cut -d '#' -f 1`
+logical_in_btrfs=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
 echo "Logical offset is $logical_in_btrfs" >>$seqres.full
 _scratch_unmount
 
@@ -74,10 +72,8 @@ echo "step 3......repair the bad copy" >>$seqres.full
 
 $DMSETUP_PROG message dust-test 0 addbadblock $((physical / 512))
 $DMSETUP_PROG message dust-test 0 enable
-while [[ -z $( (( BASHPID % 2 == stripe )) &&
-              exec $XFS_IO_PROG -c "pread -b 128K 0 128K" "$SCRATCH_MNT/foobar") ]]; do
-       :
-done
+
+_btrfs_buffered_read_on_mirror $stripe 2 "$SCRATCH_MNT/foobar" 0 128K
 
 _cleanup_dust
 
index 510e46dc082618277a8201d5ab869c6a081848a5..65a262922569bcda7effb5e67cebd8602e7ac989 100755 (executable)
@@ -7,7 +7,7 @@
 # Test that direct IO writes work on RAID5 and RAID6 filesystems.
 #
 . ./common/preamble
-_begin_fstest auto quick rw
+_begin_fstest auto quick rw scrub
 
 # Import common functions.
 . ./common/filter
index c5e9c709804fe797b79e1f16ed521d707c4ee13d..c21e0a66b144b4545c9e303c7706f989db2d51d7 100755 (executable)
 #      Btrfs: fix kernel oops while reading compressed data
 #
 . ./common/preamble
-_begin_fstest auto quick dangerous read_repair
+_begin_fstest auto quick dangerous read_repair compress
 
 # Import common functions.
 . ./common/filter
+. ./common/fail_make_request
 
 # real QA test starts here
 
@@ -25,22 +26,18 @@ _require_fail_make_request
 _require_scratch_dev_pool 2
 _scratch_dev_pool_get 2
 
-SYSFS_BDEV=`_sysfs_dev $SCRATCH_DEV`
 enable_io_failure()
 {
-       echo 100 > $DEBUGFS_MNT/fail_make_request/probability
-       echo 1000 > $DEBUGFS_MNT/fail_make_request/times
-       echo 0 > $DEBUGFS_MNT/fail_make_request/verbose
+       _allow_fail_make_request 100 1000 > /dev/null
        echo 1 > $DEBUGFS_MNT/fail_make_request/task-filter
-       echo 1 > $SYSFS_BDEV/make-it-fail
+       _start_fail_scratch_dev > /dev/null
 }
 
 disable_io_failure()
 {
-       echo 0 > $DEBUGFS_MNT/fail_make_request/probability
-       echo 0 > $DEBUGFS_MNT/fail_make_request/times
+       _disallow_fail_make_request > /dev/null
        echo 0 > $DEBUGFS_MNT/fail_make_request/task-filter
-       echo 0 > $SYSFS_BDEV/make-it-fail
+       _stop_fail_scratch_dev > /dev/null
 }
 
 _check_minimal_fs_size $(( 1024 * 1024 * 1024 ))
index 4c28a2b8a27d42491e06007142573a60f68fce93..4a5fe2b8ce4faeee5c9dcc2930314ffa5c8d0e66 100755 (executable)
@@ -7,7 +7,7 @@
 # Test for leaking quota reservations on preallocated files.
 #
 . ./common/preamble
-_begin_fstest auto quick qgroup limit
+_begin_fstest auto quick qgroup limit preallocrw
 
 # Import common functions.
 . ./common/filter
@@ -24,7 +24,7 @@ _scratch_mkfs >/dev/null
 _scratch_mount
 
 _run_btrfs_util_prog quota enable $SCRATCH_MNT
-_run_btrfs_util_prog quota rescan -w $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT
 _run_btrfs_util_prog qgroup limit 100M 0/5 $SCRATCH_MNT
 
 testfile1=$SCRATCH_MNT/testfile1
index 240c504c8ee88e119ba2da145699c910d341e410..6be2d5f6420924cb071cdf80895408ae4dc119ca 100755 (executable)
@@ -21,7 +21,7 @@ _begin_fstest auto quick
 
 _supported_fs btrfs
 _require_scratch
-_require_command $PYTHON2_PROG python2
+_require_command $PYTHON3_PROG python3
 
 # Currently in btrfs the node/leaf size can not be smaller than the page
 # size (but it can be greater than the page size). So use the largest
@@ -42,7 +42,7 @@ _scratch_mount
 #    ...
 #
 
-$PYTHON2_PROG $here/src/btrfs_crc32c_forged_name.py -d $SCRATCH_MNT -c 310
+$PYTHON3_PROG $here/src/btrfs_crc32c_forged_name.py -d $SCRATCH_MNT -c 310
 echo "Silence is golden"
 
 # success, all done
index 343178b7c06b1b1a49afbbf2514914b154c174fc..648db0d09d3df0258500306e1e8796773cc75427 100755 (executable)
@@ -30,6 +30,8 @@ _begin_fstest auto quick raid read_repair
 
 # Modify as appropriate.
 _supported_fs btrfs
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
 _require_scratch_dev_pool 4
 _require_btrfs_command inspect-internal dump-tree
 _require_btrfs_fs_feature raid56
@@ -71,7 +73,7 @@ _scratch_mount $(_btrfs_no_v1_cache_opt)
 $XFS_IO_PROG -f -d -c "pwrite -S 0xaa 0 128K" -c "fsync" \
        "$SCRATCH_MNT/foobar" | _filter_xfs_io
 
-logical=`${FILEFRAG_PROG} -v $SCRATCH_MNT/foobar | _filter_filefrag | cut -d '#' -f 1`
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
 _scratch_unmount
 
 phy0=$(get_physical 0)
index aa85835a0425b6655e7ecb9ccc42be553bb216ae..28599d096305ec6d828985ee650c5103b6e93e92 100755 (executable)
@@ -22,6 +22,8 @@ _begin_fstest auto quick raid scrub
 
 # Modify as appropriate.
 _supported_fs btrfs
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
 _require_scratch_dev_pool 4
 _require_btrfs_command inspect-internal dump-tree
 _require_btrfs_fs_feature raid56
index c2a04791aa673fba4e38709023587afdf305ec21..da31cfb8f1dd038166ee515de243328b1642f858 100755 (executable)
@@ -14,7 +14,7 @@
 #   c6a5d954950c btrfs: fail replace of seed device
 
 . ./common/preamble
-_begin_fstest auto quick volume seed
+_begin_fstest auto quick volume seed remount
 
 # Override the default cleanup function.
 _cleanup()
index 43c55c163cf237c5ac787799c82d743497cad06f..bc62b535ca0c66eedac0641b60be759f6ee07531 100755 (executable)
@@ -10,7 +10,7 @@
 #  Remount RW
 #  Run device delete on the seed device
 . ./common/preamble
-_begin_fstest auto quick volume
+_begin_fstest auto quick volume remount
 
 # Override the default cleanup function.
 _cleanup()
index fb271cfa1369e3d02125545495b4e9da4ac98357..7ab2fdbaa60e1ca164af59809c51fb36ebf5534b 100755 (executable)
@@ -11,7 +11,7 @@
 # ac0b4145d662 ("btrfs: scrub: Don't use inode pages for device replace")
 #
 . ./common/preamble
-_begin_fstest auto quick replace volume
+_begin_fstest auto quick replace volume remount compress
 
 # Import common functions.
 . ./common/filter
index a3f823fee1b7e5dd0ed17497b68b95e6dd66a965..009fdaee7c466d19b709ceca48647d6857711187 100755 (executable)
@@ -9,7 +9,7 @@
 # in a section of that prealloc extent.
 #
 . ./common/preamble
-_begin_fstest auto quick send
+_begin_fstest auto quick send prealloc punch
 
 # Override the default cleanup function.
 _cleanup()
index 8700be07c30734e8526277920cff7e55b0905e90..ab105d36fb969e576772ed2a0dfe4de25ef6afac 100755 (executable)
@@ -9,7 +9,7 @@
 # subvolume, after a clean shutdown the data was not lost.
 #
 . ./common/preamble
-_begin_fstest auto quick snapshot
+_begin_fstest auto quick snapshot prealloc
 
 # Import common functions.
 . ./common/filter
index 461cdd0fa4d6467829cf2013b47f82107bb7c67f..6a1ed1c1a9d4f4035d889096687a7f26b85f62c8 100755 (executable)
@@ -35,7 +35,7 @@ $BTRFS_UTIL_PROG subvolume snapshot "$SCRATCH_MNT/subvol" \
        "$SCRATCH_MNT/snapshot" > /dev/null
 
 $BTRFS_UTIL_PROG quota enable "$SCRATCH_MNT" > /dev/null
-$BTRFS_UTIL_PROG quota rescan -w "$SCRATCH_MNT" > /dev/null
+_qgroup_rescan $SCRATCH_MNT > /dev/null
 
 # Create high level qgroup
 $BTRFS_UTIL_PROG qgroup create 1/0 "$SCRATCH_MNT"
@@ -45,7 +45,7 @@ $BTRFS_UTIL_PROG qgroup assign "$SCRATCH_MNT/snapshot" 1/0 "$SCRATCH_MNT" \
 
 # Above assignment will mark qgroup inconsistent due to the shared extents
 # between subvol/snapshot/high level qgroup, do rescan here.
-$BTRFS_UTIL_PROG quota rescan -w "$SCRATCH_MNT" > /dev/null
+_qgroup_rescan $SCRATCH_MNT > /dev/null
 
 # Now remove the qgroup relationship and make 1/0 childless
 # Due to the shared extent outside of 1/0, we will mark qgroup inconsistent
@@ -54,7 +54,7 @@ $BTRFS_UTIL_PROG qgroup remove "$SCRATCH_MNT/snapshot" 1/0 "$SCRATCH_MNT" \
        2>&1 | _filter_btrfs_qgroup_assign_warnings
 
 # Above removal also marks qgroup inconsistent, rescan again
-$BTRFS_UTIL_PROG quota rescan -w "$SCRATCH_MNT" > /dev/null
+_qgroup_rescan $SCRATCH_MNT > /dev/null
 
 # After the test, btrfs check will verify qgroup numbers to catch any
 # corruption.
index f5acc6982cd719d2d1bcc9f9b27227199727ad30..964251b449de209ca1d9d98d0b47df0f1bacecd3 100755 (executable)
 . ./common/preamble
 _begin_fstest auto quick log replay recoveryloop
 
+_cleanup()
+{
+       cd /
+       _log_writes_cleanup &> /dev/null
+       rm -f $tmp.*
+}
+
 # Import common functions.
 . ./common/filter
 . ./common/dmlogwrites
@@ -25,6 +32,9 @@ _require_scratch
 _require_log_writes
 _require_xfs_io_command "sync_range"
 
+# block-group-tree requires no-holes
+_require_btrfs_no_block_group_tree
+
 _log_writes_init $SCRATCH_DEV
 _log_writes_mkfs "-O ^no-holes" >> $seqres.full 2>&1
 
index 9f53143eccc4dfae43b23c411fede59809ca7125..42af2d2663e81d332f55ac936f5dd35b6c73f1c2 100755 (executable)
@@ -8,7 +8,7 @@
 # CoW file nor compressed file.
 #
 . ./common/preamble
-_begin_fstest auto quick swap
+_begin_fstest auto quick swap compress
 
 . ./common/filter
 
@@ -23,15 +23,20 @@ echo "COW file"
 # unset it after the swap file has been created.
 rm -f "$SCRATCH_MNT/swap"
 touch "$SCRATCH_MNT/swap"
+# Make sure we have a COW file if we were mounted with "-o nodatacow".
+if _normalize_mount_options "$MOUNT_OPTIONS" | grep -q "nodatacow"; then
+       _require_chattr C
+       $CHATTR_PROG -C "$SCRATCH_MNT/swap"
+fi
 chmod 0600 "$SCRATCH_MNT/swap"
-_pwrite_byte 0x61 0 $(($(get_page_size) * 10)) "$SCRATCH_MNT/swap" >> $seqres.full
+_pwrite_byte 0x61 0 $(($(_get_page_size) * 10)) "$SCRATCH_MNT/swap" >> $seqres.full
 $MKSWAP_PROG "$SCRATCH_MNT/swap" >> $seqres.full
 swapon "$SCRATCH_MNT/swap" 2>&1 | _filter_scratch
 swapoff "$SCRATCH_MNT/swap" >/dev/null 2>&1
 
 echo "Compressed file"
 rm -f "$SCRATCH_MNT/swap"
-_format_swapfile "$SCRATCH_MNT/swap" $(($(get_page_size) * 10)) > /dev/null
+_format_swapfile "$SCRATCH_MNT/swap" $(($(_get_page_size) * 10)) > /dev/null
 $CHATTR_PROG +c "$SCRATCH_MNT/swap" 2>&1 | grep -o "Invalid argument while setting flags"
 
 status=0
index 3bb5e7f91809e73fe09f3577cf37c2adce6550c6..16305c1874923bf4d10d6694dfee0c2dc57e4175 100755 (executable)
@@ -8,7 +8,7 @@
 # specific to Btrfs.
 #
 . ./common/preamble
-_begin_fstest auto quick swap
+_begin_fstest auto quick swap compress
 
 . ./common/filter
 
@@ -20,7 +20,7 @@ _scratch_mount
 
 $BTRFS_UTIL_PROG subvolume create "$SCRATCH_MNT/swapvol" >> $seqres.full
 swapfile="$SCRATCH_MNT/swapvol/swap"
-_format_swapfile "$swapfile" $(($(get_page_size) * 10)) > /dev/null
+_format_swapfile "$swapfile" $(($(_get_page_size) * 10)) > /dev/null
 swapon "$swapfile"
 
 # Turning off nocow doesn't do anything because the file is not empty, not
index db877d419676f06d90aca0917cba14655c48f486..de52c71ee2ef28c0f8afce8c6c19189906913f1a 100755 (executable)
@@ -17,7 +17,7 @@ _require_scratch_swapfile
 _check_minimal_fs_size $((1024 * 1024 * 1024))
 
 cycle_swapfile() {
-       local sz=${1:-$(($(get_page_size) * 10))}
+       local sz=${1:-$(($(_get_page_size) * 10))}
        _format_swapfile "$SCRATCH_MNT/swap" "$sz" > /dev/null
        swapon "$SCRATCH_MNT/swap" 2>&1 | _filter_scratch
        swapoff "$SCRATCH_MNT/swap" > /dev/null 2>&1
@@ -47,7 +47,7 @@ _scratch_mkfs >> $seqres.full 2>&1
 _scratch_mount
 # Create the swap file, then add the device. That way we know it's all on one
 # device.
-_format_swapfile "$SCRATCH_MNT/swap" $(($(get_page_size) * 10)) > /dev/null
+_format_swapfile "$SCRATCH_MNT/swap" $(($(_get_page_size) * 10)) > /dev/null
 scratch_dev2="$(echo "${SCRATCH_DEV_POOL}" | $AWK_PROG '{ print $2 }')"
 $BTRFS_UTIL_PROG device add -f "$scratch_dev2" "$SCRATCH_MNT" >> $seqres.full
 swapon "$SCRATCH_MNT/swap" 2>&1 | _filter_scratch
index 7080d8608b61dbf2a5bf3a0f64c527dd4a264d49..0ddff8d8e62d22483f9bf57ec3a97bf5f33cc474 100755 (executable)
@@ -29,7 +29,7 @@ scratch_dev3="$(echo "${SCRATCH_DEV_POOL}" | $AWK_PROG '{ print $3 }')"
 echo "Remove device"
 _scratch_mkfs >> $seqres.full 2>&1
 _scratch_mount
-_format_swapfile "$SCRATCH_MNT/swap" $(($(get_page_size) * 10)) > /dev/null
+_format_swapfile "$SCRATCH_MNT/swap" $(($(_get_page_size) * 10)) > /dev/null
 $BTRFS_UTIL_PROG device add -f "$scratch_dev2" "$SCRATCH_MNT" >> $seqres.full
 swapon "$SCRATCH_MNT/swap" 2>&1 | _filter_scratch
 # We know the swap file is on device 1 because we added device 2 after it was
@@ -47,7 +47,7 @@ _check_scratch_fs "$scratch_dev2"
 echo "Replace device"
 _scratch_mkfs >> $seqres.full 2>&1
 _scratch_mount
-_format_swapfile "$SCRATCH_MNT/swap" $(($(get_page_size) * 10)) > /dev/null
+_format_swapfile "$SCRATCH_MNT/swap" $(($(_get_page_size) * 10)) > /dev/null
 $BTRFS_UTIL_PROG device add -f "$scratch_dev2" "$SCRATCH_MNT" >> $seqres.full
 swapon "$SCRATCH_MNT/swap" 2>&1 | _filter_scratch
 # Again, we know the swap file is on device 1.
index 2fd11e89ca350364287d7ce6a7d7352e5e410d10..7b004b83c15fd9dee9e3c2c3565b1bdd813b86c2 100755 (executable)
@@ -10,7 +10,6 @@
 _begin_fstest auto quick swap balance
 
 . ./common/filter
-. ./common/btrfs
 
 # Modify as appropriate.
 _supported_fs btrfs
index 2f17c9f9fb4adf5f53a857d1a0be2ec6d678f043..00013af0a19038fff8cd9d108a5b34a74bf9cec9 100755 (executable)
@@ -33,7 +33,7 @@ _scratch_mount
 mkdir -p "$SCRATCH_MNT/snapshots"
 $BTRFS_UTIL_PROG subvolume create "$SCRATCH_MNT/src" > /dev/null
 $BTRFS_UTIL_PROG quota enable "$SCRATCH_MNT" > /dev/null
-$BTRFS_UTIL_PROG quota rescan -w "$SCRATCH_MNT" > /dev/null
+_qgroup_rescan "$SCRATCH_MNT" > /dev/null
 
 fill_workload()
 {
@@ -45,8 +45,8 @@ fill_workload()
 
                # Randomly remove some files for every 5 loop
                if [ $(( $i % 5 )) -eq 0 ]; then
-                       victim=$(ls "$SCRATCH_MNT/src" | sort -R | head -n1)
-                       rm "$SCRATCH_MNT/src/$victim"
+                       victim=$(_random_file "$SCRATCH_MNT/src")
+                       rm "$victim"
                fi
                i=$((i + 1))
        done
@@ -69,13 +69,12 @@ delete_workload()
        trap "wait; exit" SIGTERM
        while true; do
                sleep $((sleep_time * 2))
-               victim=$(ls "$SCRATCH_MNT/snapshots" | sort -R | head -n1)
+               victim=$(_random_file "$SCRATCH_MNT/snapshots")
                if [ -z "$victim" ]; then
                        # No snapshots available, sleep and retry later.
                        continue
                fi
-               $BTRFS_UTIL_PROG subvolume delete \
-                       "$SCRATCH_MNT/snapshots/$victim" > /dev/null
+               $BTRFS_UTIL_PROG subvolume delete "$victim" > /dev/null
        done
 }
 
index 68e382f667374576183c0200a8e07cf70b98a973..aa195f7b40085dbfcb06b5494cb8d35a45238c84 100755 (executable)
@@ -11,7 +11,7 @@
 # "btrfs: qgroup: Make qgroup async transaction commit more aggressive"
 #
 . ./common/preamble
-_begin_fstest auto quick qgroup limit
+_begin_fstest auto quick qgroup limit prealloc
 
 # Import common functions.
 . ./common/filter
@@ -27,7 +27,7 @@ _scratch_mkfs > /dev/null
 _scratch_mount
 
 $BTRFS_UTIL_PROG quota enable "$SCRATCH_MNT" > /dev/null
-$BTRFS_UTIL_PROG quota rescan -w "$SCRATCH_MNT" > /dev/null
+_qgroup_rescan "$SCRATCH_MNT" > /dev/null
 $BTRFS_UTIL_PROG qgroup limit -e 1G "$SCRATCH_MNT"
 
 $XFS_IO_PROG -f -c "falloc 0 900M" "$SCRATCH_MNT/padding"
index ee97b62661a3331753b64c2ec21e606f6b10f980..c7b8d2d46951d532628e3698aa33f221d45ff983 100755 (executable)
@@ -9,12 +9,13 @@
 #  a9261d4125c9 ("btrfs: harden agaist duplicate fsid on scanned devices")
 #
 . ./common/preamble
-_begin_fstest volume
+_begin_fstest volume auto quick
 
 mnt=$TEST_DIR/$seq.mnt
 # Override the default cleanup function.
 _cleanup()
 {
+       $UMOUNT_PROG $mnt > /dev/null 2>&1
        rm -rf $mnt > /dev/null 2>&1
        cd /
        rm -f $tmp.*
@@ -27,6 +28,8 @@ _cleanup()
 _supported_fs btrfs
 _require_scratch_dev_pool 2
 _scratch_dev_pool_get 2
+_fixed_by_kernel_commit a9261d4125c9 \
+       "btrfs: harden agaist duplicate fsid on scanned devices"
 
 device_1=$(echo $SCRATCH_DEV_POOL | $AWK_PROG '{print $1}')
 device_2=$(echo $SCRATCH_DEV_POOL | $AWK_PROG '{print $2}')
@@ -49,9 +52,9 @@ for sb_bytenr in 65536 67108864; do
        echo ..:$? >> $seqres.full
 done
 
-# Original device is mounted, scan of its clone should fail
+# Original device is mounted, scan of its clone must not alter the
+# filesystem device path
 $BTRFS_UTIL_PROG device scan $device_2 >> $seqres.full 2>&1
-[[ $? != 1 ]] && _fail "cloned device scan should fail"
 
 [[ $(findmnt $mnt | grep -v TARGET | $AWK_PROG '{print $2}') != $device_1 ]] && \
                                                _fail "mounted device changed"
index 574636a61313789f5ae4d1bea51c362c7dfb7167..d3cf05a1bd92f5d6402c40bcb704536299de4e4c 100755 (executable)
@@ -219,7 +219,7 @@ wait $balance_pid
 # BTRFS error (device sdc): parent transid verify failed on 32243712 wanted 24 \
 #     found 27
 #
-_dmesg_since_test_start | egrep -e '\bBTRFS error \(device .*?\):'
+_dmesg_since_test_start | grep -E -e '\bBTRFS error \(device .*?\):'
 
 status=0
 exit
index 974438c15055e451e3947e771629f36dcf26947f..f78c14fe446458dadf408072fc72665b5a7c04cf 100755 (executable)
@@ -30,7 +30,7 @@ _log_writes_mkfs >> $seqres.full 2>&1
 
 _log_writes_mount
 $BTRFS_UTIL_PROG quota enable $SCRATCH_MNT >> $seqres.full
-$BTRFS_UTIL_PROG quota rescan -w $SCRATCH_MNT >> $seqres.full
+_qgroup_rescan $SCRATCH_MNT >> $seqres.full
 
 # Create enough metadata for later balance
 for ((i = 0; i < $nr_files; i++)); do
index bcf14ebb8e3b789ca2c883276c92152df2add06c..00ea1478ab1a71772db4084e3ef280074b2ce47c 100755 (executable)
@@ -41,7 +41,7 @@ _require_scratch
 _require_attrs
 
 # We require a 4K nodesize to ensure the test isn't too slow
-if [ $(get_page_size) -ne 4096 ]; then
+if [ $(_get_page_size) -ne 4096 ]; then
        _notrun "This test doesn't support non-4K page size yet"
 fi
 
@@ -69,12 +69,6 @@ $BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/src > /dev/null
 mkdir -p $SCRATCH_MNT/snapshots
 mkdir -p $SCRATCH_MNT/src/padding
 
-random_file()
-{
-       local basedir=$1
-       echo "$basedir/$(ls $basedir | sort -R | tail -1)"
-}
-
 snapshot_workload()
 {
        trap "wait; exit" SIGTERM
@@ -85,9 +79,9 @@ snapshot_workload()
                        $SCRATCH_MNT/src $SCRATCH_MNT/snapshots/$i \
                        > /dev/null
                # Do something small to make snapshots different
-               rm -f "$(random_file $SCRATCH_MNT/src/padding)"
-               rm -f "$(random_file $SCRATCH_MNT/src/padding)"
-               touch "$(random_file $SCRATCH_MNT/src/padding)"
+               rm -f "$(_random_file $SCRATCH_MNT/src/padding)"
+               rm -f "$(_random_file $SCRATCH_MNT/src/padding)"
+               touch "$(_random_file $SCRATCH_MNT/src/padding)"
                touch "$SCRATCH_MNT/src/padding/random_$RANDOM"
 
                i=$(($i + 1))
@@ -102,7 +96,7 @@ delete_workload()
        while true; do
                sleep 2
                $BTRFS_UTIL_PROG subvolume delete \
-                       "$(random_file $SCRATCH_MNT/snapshots)" \
+                       "$(_random_file $SCRATCH_MNT/snapshots)" \
                        > /dev/null 2>&1
        done
 }
@@ -127,7 +121,7 @@ log_writes_fast_replay_check()
                --replay $blkdev --check $check_point --fsck "$fsck_command" \
                &> $tmp.full_fsck
        ret=$?
-       tail -n 150 $tmp.full_fsck > $seqres.full
+       tail -n 150 $tmp.full_fsck >> $seqres.full
        [ $ret -ne 0 ] && _fail "fsck failed during replay"
 }
 
@@ -146,7 +140,7 @@ pid1=$!
 delete_workload &
 pid2=$!
 
-"$FSSTRESS_PROG" $fsstress_args > /dev/null &
+"$FSSTRESS_PROG" $fsstress_args >> $seqres.full &
 sleep $runtime
 
 "$KILLALL_PROG" -q "$FSSTRESS_PROG" &> /dev/null
index ae293f2e2852487b7eb697ebb359cd800a79b70c..67220c7a5c68dffe59a76907d2971abea83d26e8 100755 (executable)
@@ -10,7 +10,7 @@
 # "btrfs: qgroup: Fix the wrong target io_tree when freeing reserved data space"
 #
 . ./common/preamble
-_begin_fstest auto quick qgroup enospc limit
+_begin_fstest auto quick qgroup enospc limit prealloc
 
 # Import common functions.
 . ./common/filter
@@ -26,7 +26,7 @@ _scratch_mkfs > /dev/null
 _scratch_mount
 
 $BTRFS_UTIL_PROG quota enable "$SCRATCH_MNT" > /dev/null
-$BTRFS_UTIL_PROG quota rescan -w "$SCRATCH_MNT" > /dev/null
+_qgroup_rescan "$SCRATCH_MNT" > /dev/null
 $BTRFS_UTIL_PROG qgroup limit -e 256M "$SCRATCH_MNT"
 
 # Create a file with the following layout:
index 747345973244711443af343966f310792897ef83..df8f5ed6e47eac430eefc3df79aef86a4cf08bff 100755 (executable)
@@ -8,7 +8,7 @@
 # source profiles just rely on being able to read the data and metadata.
 #
 . ./common/preamble
-_begin_fstest auto volume balance
+_begin_fstest auto volume balance scrub
 
 # Import common functions.
 . ./common/filter
@@ -21,6 +21,9 @@ _require_scratch_dev_pool 4
 # Zoned btrfs only supports SINGLE profile
 _require_non_zoned_device "${SCRATCH_DEV}"
 
+# Load up the available configs
+_btrfs_get_profile_configs
+
 declare -a TEST_VECTORS=(
 # $nr_dev_min:$data:$metadata:$data_convert:$metadata_convert
 "4:single:raid1"
@@ -38,6 +41,11 @@ run_testcase() {
        src_type=${args[1]}
        dst_type=${args[2]}
 
+       if [[ ! "${_btrfs_profile_configs[@]}" =~ "$dst_type" ]]; then
+               echo "=== Skipping test: $1 ===" >> $seqres.full
+               return
+       fi
+
        _scratch_dev_pool_get $num_disks
 
        echo "=== Running test: $1 ===" >> $seqres.full
index 1edc83306515048ca728b3c769483bcb7bb8e180..7d23ffcee3c5be46beee98f59ab0924a65845514 100755 (executable)
@@ -4,12 +4,10 @@
 #
 # FS QA Test 198
 #
-# Test stale and alien non-btrfs device in the fs devices list.
-#  Bug fixed in:
-#    btrfs: remove identified alien device in open_fs_devices
+# Test outdated and foreign non-btrfs devices in the device listing.
 #
 . ./common/preamble
-_begin_fstest quick volume
+_begin_fstest auto quick volume
 
 # Import common functions.
 . ./common/filter
@@ -22,6 +20,8 @@ _require_scratch
 _require_scratch_dev_pool 4
 # Zoned btrfs only supports SINGLE profile
 _require_non_zoned_device ${SCRATCH_DEV}
+_fixed_by_kernel_commit 96c2e067ed3e3e \
+       "btrfs: skip devices without magic signature when mounting"
 
 workout()
 {
index 2024447c0a0c6dada30926d76fbcd420e97efc14..a4920b99ef97ec2079e3da3aff82388ff5c2113c 100755 (executable)
 # There is a long existing bug that btrfs doesn't discard all space for
 # above mentioned case.
 #
-# The fix is: "btrfs: extent-tree: Ensure we trim ranges across block group
-# boundary"
-#
 . ./common/preamble
-_begin_fstest auto quick trim
+_begin_fstest auto quick trim fiemap
 
 # Override the default cleanup function.
 _cleanup()
@@ -34,6 +31,9 @@ _cleanup()
 
 # Modify as appropriate.
 _supported_fs btrfs
+_fixed_by_kernel_commit 6b7faadd985c \
+       "btrfs: Ensure we trim ranges across block group boundary"
+
 _require_loop
 _require_xfs_io_command "fiemap"
 
index 48cd7046760216812f96f8b633048109b44da341..5ce3775f2222f901e86f9e018db55ec8e8fa1c2d 100755 (executable)
@@ -8,7 +8,7 @@
 # operations for extents that are shared between the same file.
 #
 . ./common/preamble
-_begin_fstest auto quick send clone
+_begin_fstest auto quick send clone fiemap
 
 # Override the default cleanup function.
 _cleanup()
index c1fa3c3ffd6d897e6b39e4fecbdf8a09b8aa26fe..5f0429f18bf976d71d9abc6e9c91a11178080bdc 100755 (executable)
@@ -11,6 +11,7 @@ _begin_fstest auto quick subvol snapshot
 . ./common/filter
 
 _supported_fs btrfs
+_require_scratch
 
 _scratch_mkfs >> $seqres.full 2>&1
 _scratch_mount
index dae8c68f72433d3aee1913ff0a00778e27a4dc84..728f9a7c748773d8829d5bfb7beddb90d80bd6bd 100755 (executable)
@@ -14,7 +14,7 @@
 #   "Btrfs: implement full reflink support for inline extents"
 #
 . ./common/preamble
-_begin_fstest auto quick clone compress
+_begin_fstest auto quick clone compress prealloc
 
 # Import common functions.
 . ./common/filter
index efb07b4b5f02dedabc33612a80633778dad81b5c..02a5082046f4a5fb881e50d2a55d1a2e8b853089 100755 (executable)
 #      btrfs: replace all uses of btrfs_ordered_update_i_size
 #
 . ./common/preamble
-_begin_fstest auto quick log replay recoveryloop
+_begin_fstest auto quick log replay recoveryloop punch prealloc
+
+_cleanup()
+{
+       cd /
+       _log_writes_cleanup &> /dev/null
+       rm -f $tmp.*
+}
 
 # Import common functions.
 . ./common/filter
@@ -26,6 +33,9 @@ _require_log_writes
 _require_xfs_io_command "falloc" "-k"
 _require_xfs_io_command "fpunch"
 
+# block-group-tree requires no-holes
+_require_btrfs_no_block_group_tree
+
 _log_writes_init $SCRATCH_DEV
 _log_writes_mkfs "-O ^no-holes" >> $seqres.full 2>&1
 
index 909f9fa40803e6c089f6059bd51d3f8d45b4ff55..d58803e2f801a073a0307a2042b615fcd46f6e87 100755 (executable)
@@ -28,7 +28,7 @@ _delete_and_list()
        local msg="$2"
 
        SUBVOLID=$(_btrfs_get_subvolid $SCRATCH_MNT "$subvol_name")
-       $BTRFS_UTIL_PROG subvolume delete --subvolid $SUBVOLID $SCRATCH_MNT | _filter_scratch
+       $BTRFS_UTIL_PROG subvolume delete --subvolid $SUBVOLID $SCRATCH_MNT | _filter_btrfs_subvol_delete
 
        echo "$msg"
        $BTRFS_UTIL_PROG subvolume list $SCRATCH_MNT | $AWK_PROG '{ print $NF }'
index 9b660699a4b2a2b44465f1e208ee4e2af4b5141b..dc5761ba1c87125d6e61145f15492c7fbb18e8f4 100644 (file)
@@ -6,12 +6,12 @@ Current subvolume ids:
 subvol1
 subvol2
 subvol3
-Delete subvolume (no-commit): 'SCRATCH_MNT/subvol1'
+Delete subvolume 'SCRATCH_MNT/subvol1'
 After deleting one subvolume:
 subvol2
 subvol3
-Delete subvolume (no-commit): 'SCRATCH_MNT/subvol3'
+Delete subvolume 'SCRATCH_MNT/subvol3'
 Last remaining subvolume:
 subvol2
-Delete subvolume (no-commit): 'SCRATCH_MNT/subvol2'
+Delete subvolume 'SCRATCH_MNT/subvol2'
 All subvolumes removed.
index 383a307ff02d188c90d384c169aa6fb77c1ab424..f3d769fa09de6baab00446dbb500fc64e9ec3ef5 100755 (executable)
@@ -29,7 +29,7 @@ _pwrite_byte 0xcd 0 16M "$SCRATCH_MNT/src/file" > /dev/null
 # by qgroup
 sync
 $BTRFS_UTIL_PROG quota enable "$SCRATCH_MNT"
-$BTRFS_UTIL_PROG quota rescan -w "$SCRATCH_MNT" > /dev/null
+_qgroup_rescan "$SCRATCH_MNT" > /dev/null
 $BTRFS_UTIL_PROG qgroup create 1/0 "$SCRATCH_MNT"
 
 # Create a snapshot with qgroup inherit
index 60ef3d1bfc4baf0641a2a9bd52d425a46d80de2a..f5744b75f4e840ad61cad300df6c4a4a1e228907 100755 (executable)
@@ -10,7 +10,7 @@
 # the NO_HOLES feature.
 #
 . ./common/preamble
-_begin_fstest auto quick log prealloc
+_begin_fstest auto quick log prealloc fiemap
 
 # Override the default cleanup function.
 _cleanup()
index 8a10355cc3bd90a9ffb42cbdd8f9dd20b1642744..816041a0cc2ea019eddbd5b6df02b4ffc16c6614 100755 (executable)
@@ -7,7 +7,7 @@
 # Test if canceling a running balance can lead to dead looping balance
 #
 . ./common/preamble
-_begin_fstest auto balance dangerous
+_begin_fstest auto balance
 
 # Override the default cleanup function.
 _cleanup()
@@ -25,25 +25,38 @@ _supported_fs btrfs
 _require_scratch
 _require_xfs_io_command pwrite -D
 
+_fixed_by_kernel_commit 1dae7e0e58b4 \
+       "btrfs: reloc: clear DEAD_RELOC_TREE bit for orphan roots to prevent runaway balance"
+
 _scratch_mkfs >> $seqres.full
 _scratch_mount
 
-runtime=4
+max_space=$(_get_total_space $SCRATCH_MNT)
+max_space=$(( max_space / 2 ))
 
-# Create enough IO so that we need around $runtime seconds to relocate it.
-#
-# Here we don't want any wrapper, as we want full control of the process.
-$XFS_IO_PROG -f -c "pwrite -D -b 1M 0 1024T" "$SCRATCH_MNT/file" &> /dev/null &
-write_pid=$!
-sleep $runtime
-kill $write_pid
-wait $write_pid
+# Create enough IO so that we need around 8 seconds to relocate it.
+start_ts=$(_wallclock)
+$TIMEOUT_PROG 8s $XFS_IO_PROG -f -c "pwrite -D -b 1M 0 $max_space" \
+       "$SCRATCH_MNT/file" > /dev/null 2>&1
+stop_ts=$(_wallclock)
+
+runtime=$(( stop_ts - start_ts ))
+
+# Unmount and mount again the fs to clear any cached data and metadata, so that
+# it's less likely balance has already finished when we try to cancel it below.
+_scratch_cycle_mount
 
 # Now balance should take at least $runtime seconds, we can cancel it at
-# $runtime/2 to ensure a success cancel.
+# $runtime/4 to ensure a success cancel.
 _run_btrfs_balance_start -d --bg "$SCRATCH_MNT"
-sleep $(($runtime / 2))
-$BTRFS_UTIL_PROG balance cancel "$SCRATCH_MNT"
+sleep $(($runtime / 4))
+# It's possible that balance has already completed. It's unlikely but often
+# it may happen due to virtualization, caching and other factors, so ignore
+# any error about no balance currently running.
+$BTRFS_UTIL_PROG balance cancel "$SCRATCH_MNT" 2>&1 | grep -iq 'not in progress'
+if [ $? -eq 0 ]; then
+       _notrun "balance finished before we could cancel it"
+fi
 
 # Now check if we can finish relocating metadata, which should finish very
 # quickly.
index 0dcbce2a888ecdb8792f4dee288dd4cfb429ee15..6fa226fe1e056da592efbecb5cf9a4feba61677c 100755 (executable)
@@ -24,6 +24,9 @@ get_physical()
 
 # Modify as appropriate.
 _supported_fs btrfs
+_require_scratch
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
 # Overwriting data is forbidden on a zoned block device
 _require_non_zoned_device $SCRATCH_DEV
 
@@ -32,7 +35,7 @@ _scratch_mkfs > /dev/null
 # blobk group
 _scratch_mount $(_btrfs_no_v1_cache_opt)
 
-pagesize=$(get_page_size)
+pagesize=$(_get_page_size)
 blocksize=$(_get_block_size $SCRATCH_MNT)
 
 # For subpage case, since we still do read in full page size, if have 8 corrupted
@@ -56,7 +59,7 @@ fi
 #create an 8 block file
 $XFS_IO_PROG -d -f -c "pwrite -S 0xbb -b $filesize 0 $filesize" "$SCRATCH_MNT/foobar" > /dev/null
 
-logical_extent=$($FILEFRAG_PROG -v "$SCRATCH_MNT/foobar" | _filter_filefrag | cut -d '#' -f 1)
+logical_extent=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
 physical_extent=$(get_physical $logical_extent)
 
 echo "logical = $logical_extent physical=$physical_extent" >> $seqres.full
index 2ed4866887f745719fd38c090581856e680f4523..979dcb73f09742aa637ad82512dc44b395cefe3d 100755 (executable)
@@ -6,8 +6,6 @@
 #
 # Test if the show_devname() returns sprout device instead of seed device.
 #
-# Fixed in kernel patch:
-#   btrfs: btrfs_show_devname don't traverse into the seed fsid
 
 . ./common/preamble
 _begin_fstest auto quick seed
@@ -17,6 +15,8 @@ _begin_fstest auto quick seed
 
 # real QA test starts here
 _supported_fs btrfs
+_fixed_by_kernel_commit 4faf55b03823 \
+       "btrfs: don't traverse into the seed devices in show_devname"
 _require_scratch_dev_pool 2
 
 _scratch_dev_pool_get 2
index 672ad0ff61f078d636dc2a4cedf3ecd227bc833d..b0434834ff653690e9de2000b07d7e6ff6b7cc72 100755 (executable)
@@ -4,10 +4,6 @@
 #
 # FS QA Test 218
 #
-# Regression test for the problem fixed by the patch
-#
-#  btrfs: init device stats for seed devices
-#
 # Make a seed device, add a sprout to it, and then make sure we can still read
 # the device stats for both devices after we remount with the new sprout device.
 #
@@ -22,6 +18,8 @@ _begin_fstest auto quick volume
 
 # Modify as appropriate.
 _supported_fs btrfs
+_fixed_by_kernel_commit 124604eb50f8 \
+       "btrfs: init device stats for seed devices"
 _require_test
 _require_scratch_dev_pool 2
 
index 528175b8a4b901a79886495fde7685880ed360ca..9da835dcab10fc60af85d4aa85b8e9d0916afc91 100755 (executable)
@@ -6,11 +6,8 @@
 #
 # Test a variety of stale device usecases.  We cache the device and generation
 # to make sure we do not allow stale devices, which can end up with some wonky
-# behavior for loop back devices.  This was changed with
-#
-#   btrfs: allow single disk devices to mount with older generations
-#
-# But I've added a few other test cases so it's clear what we expect to happen
+# behavior for loop back devices.
+# And, added a few other test cases so it's clear what we expect to happen
 # currently.
 #
 
@@ -22,14 +19,19 @@ _cleanup()
 {
        cd /
        rm -f $tmp.*
-       if [ ! -z "$loop_mnt" ]; then
-               $UMOUNT_PROG $loop_mnt
-               rm -rf $loop_mnt
-       fi
-       [ ! -z "$loop_mnt1" ] && rm -rf $loop_mnt1
-       [ ! -z "$fs_img1" ] && rm -rf $fs_img1
-       [ ! -z "$fs_img2" ] && rm -rf $fs_img2
-       [ ! -z "$loop_dev" ] && _destroy_loop_device $loop_dev
+
+       # The variables are set before the test case can fail.
+       $UMOUNT_PROG ${loop_mnt1} &> /dev/null
+       $UMOUNT_PROG ${loop_mnt2} &> /dev/null
+       rm -rf $loop_mnt1
+       rm -rf $loop_mnt2
+
+       [ ! -z $loop_dev1 ] && _destroy_loop_device $loop_dev1
+       [ ! -z $loop_dev1 ] && _destroy_loop_device $loop_dev2
+
+       rm -f $fs_img1
+       rm -f $fs_img2
+
        _btrfs_rescan_devices
 }
 
@@ -39,55 +41,64 @@ _cleanup()
 # real QA test starts here
 
 _supported_fs btrfs
+
+loop_mnt1=$TEST_DIR/$seq/mnt1
+loop_mnt2=$TEST_DIR/$seq/mnt2
+fs_img1=$TEST_DIR/$seq/img1
+fs_img2=$TEST_DIR/$seq/img2
+loop_dev1=""
+loop_dev2=""
+
 _require_test
 _require_loop
+_require_btrfs_fs_sysfs
 _require_btrfs_forget_or_module_loadable
+_fixed_by_kernel_commit 5f58d783fd78 \
+       "btrfs: free device in btrfs_close_devices for a single device filesystem"
 
-loop_mnt=$TEST_DIR/$seq.mnt
-loop_mnt1=$TEST_DIR/$seq.mnt1
-fs_img1=$TEST_DIR/$seq.img1
-fs_img2=$TEST_DIR/$seq.img2
-
-mkdir $loop_mnt
-mkdir $loop_mnt1
+mkdir -p $loop_mnt1
+mkdir -p $loop_mnt2
 
 $XFS_IO_PROG -f -c "truncate 256m" $fs_img1 >>$seqres.full 2>&1
 
 _mkfs_dev $fs_img1 >>$seqres.full 2>&1
 cp $fs_img1 $fs_img2
 
+loop_dev1=`_create_loop_device $fs_img1`
+loop_dev2=`_create_loop_device $fs_img2`
+
 # Normal single device case, should pass just fine
-_mount -o loop $fs_img1 $loop_mnt > /dev/null  2>&1 || \
+_mount $loop_dev1 $loop_mnt1 > /dev/null  2>&1 || \
        _fail "Couldn't do initial mount"
-$UMOUNT_PROG $loop_mnt
+$UMOUNT_PROG $loop_mnt1
 
 _btrfs_forget_or_module_reload
 
 # Now mount the new version again to get the higher generation cached, umount
 # and try to mount the old version.  Mount the new version again just for good
 # measure.
-loop_dev=`_create_loop_device $fs_img1`
-
-_mount $loop_dev $loop_mnt > /dev/null 2>&1 || \
+_mount $loop_dev1 $loop_mnt1 > /dev/null 2>&1 || \
        _fail "Failed to mount the second time"
-$UMOUNT_PROG $loop_mnt
+$UMOUNT_PROG $loop_mnt1
 
-_mount -o loop $fs_img2 $loop_mnt > /dev/null 2>&1 || \
+_mount $loop_dev2 $loop_mnt2 > /dev/null 2>&1 || \
        _fail "We couldn't mount the old generation"
-$UMOUNT_PROG $loop_mnt
+$UMOUNT_PROG $loop_mnt2
 
-_mount $loop_dev $loop_mnt > /dev/null 2>&1 || \
+_mount $loop_dev1 $loop_mnt1 > /dev/null 2>&1 || \
        _fail "Failed to mount the second time"
-$UMOUNT_PROG $loop_mnt
+$UMOUNT_PROG $loop_mnt1
 
-# Now we definitely can't mount them at the same time, because we're still tied
-# to the limitation of one fs_devices per fsid.
+# Now try mount them at the same time, if kernel does not support
+# temp-fsid feature then mount will fail.
 _btrfs_forget_or_module_reload
 
-_mount $loop_dev $loop_mnt > /dev/null 2>&1 || \
+_mount $loop_dev1 $loop_mnt1 > /dev/null 2>&1 || \
        _fail "Failed to mount the third time"
-_mount -o loop $fs_img2 $loop_mnt1 > /dev/null 2>&1 && \
-       _fail "We were allowed to mount when we should have failed"
+if ! _has_btrfs_sysfs_feature_attr temp_fsid; then
+       _mount $loop_dev2 $loop_mnt2 > /dev/null 2>&1 && \
+               _fail "We were allowed to mount when we should have failed"
+fi
 
 _btrfs_rescan_devices
 # success, all done
index fa91a38493af7c50a6397de57b06c6eff9b64368..a7ec6558b4ca98c27c0f39df3337c80745c1ba0e 100755 (executable)
@@ -8,7 +8,7 @@
 # * device= argument is already being test by btrfs/125
 # * space cache test already covered by test btrfs/131
 . ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick remount
 
 _register_cleanup "cleanup"
 
@@ -212,20 +212,14 @@ test_non_revertible_options()
        # nologreplay should be used only with readonly
        test_should_fail "nologreplay"
 
-       # norecovery should be used only with readonly.
-       # This options is an alias to nologreplay
-       test_should_fail "norecovery"
-
        if [ "$enable_rescue_nologreplay" = true ]; then
                #rescue=nologreplay should be used only with readonly
                test_should_fail "rescue=nologreplay"
 
                test_mount_opt "nologreplay,ro" "ro,rescue=nologreplay"
-               test_mount_opt "norecovery,ro" "ro,rescue=nologreplay"
                test_mount_opt "rescue=nologreplay,ro" "ro,rescue=nologreplay"
        else
                test_mount_opt "nologreplay,ro" "ro,nologreplay"
-               test_mount_opt "norecovery,ro" "ro,nologreplay"
        fi
 
        test_mount_opt "rescan_uuid_tree" "rescan_uuid_tree"
@@ -265,23 +259,27 @@ test_revertible_options()
        test_roundtrip_mount "compress=zlib:20" "compress=zlib:9" "compress=zstd:16" "compress=zstd:15"
        test_roundtrip_mount "compress-force=lzo" "compress-force=lzo" "compress-force=zlib:4" "compress-force=zlib:4"
 
-       # on remount, if we only pass datacow after nodatacow was used it will remain with nodatasum
-       test_roundtrip_mount "nodatacow" "nodatasum,nodatacow" "datacow,datasum" "$DEFAULT_OPTS"
-       # nodatacow disabled compression
-       test_roundtrip_mount "compress-force" "compress-force=zlib:3" "nodatacow" "nodatasum,nodatacow"
+       if ! _scratch_btrfs_is_zoned; then
+               # on remount, if we only pass datacow after nodatacow was used it will remain with nodatasum
+               test_roundtrip_mount "nodatacow" "nodatasum,nodatacow" "datacow,datasum" "$DEFAULT_OPTS"
+               # nodatacow disabled compression
+               test_roundtrip_mount "compress-force" "compress-force=zlib:3" "nodatacow" "nodatasum,nodatacow"
 
-       # nodatacow disabled both datacow and datasum, and datasum enabled datacow and datasum
-       test_roundtrip_mount "nodatacow" "nodatasum,nodatacow" "datasum" "$DEFAULT_OPTS"
-       test_roundtrip_mount "nodatasum" "nodatasum" "datasum" "$DEFAULT_OPTS"
+               # nodatacow disabled both datacow and datasum, and datasum enabled datacow and datasum
+               test_roundtrip_mount "nodatacow" "nodatasum,nodatacow" "datasum" "$DEFAULT_OPTS"
+               test_roundtrip_mount "nodatasum" "nodatasum" "datasum" "$DEFAULT_OPTS"
+       fi
 
        test_should_fail "discard=invalid"
        if [ "$enable_discard_sync" = true ]; then
                test_roundtrip_mount "discard" "discard" "discard=sync" "discard"
-               test_roundtrip_mount "discard=async" "discard=async" "discard=sync" "discard"
-               test_roundtrip_mount "discard=sync" "discard" "nodiscard" "$DEFAULT_OPTS"
+               if ! _scratch_btrfs_is_zoned; then
+                       test_roundtrip_mount "discard=async" "discard=async" "discard=sync" "discard"
+               fi
+               test_roundtrip_mount "discard=sync" "discard" "nodiscard" "$DEFAULT_NODISCARD_OPTS"
        else
                test_roundtrip_mount "discard" "discard" "discard" "discard"
-               test_roundtrip_mount "discard" "discard" "nodiscard" "$DEFAULT_OPTS"
+               test_roundtrip_mount "discard" "discard" "nodiscard" "$DEFAULT_NODISCARD_OPTS"
        fi
 
        test_roundtrip_mount "enospc_debug" "enospc_debug" "noenospc_debug" "$DEFAULT_OPTS"
@@ -342,6 +340,12 @@ _scratch_mount
 DEFAULT_OPTS=$(cat /proc/self/mounts | grep $SCRATCH_MNT | \
                $AWK_PROG '{ print $4 }')
 
+# Since 63a7cb130718 ("btrfs: auto enable discard=async when possible"),
+# "discard=async" will be automatically enabled if the device supports.
+# This can screw up our test against nodiscard options, thus remove the
+# default "discard=async" mount option for "nodiscard" tests.
+DEFAULT_NODISCARD_OPTS=$(echo -n "$DEFAULT_OPTS" | $SED_PROG 's/,discard=async//')
+
 $BTRFS_UTIL_PROG subvolume create "$SCRATCH_MNT/vol1" > /dev/null
 touch "$SCRATCH_MNT/vol1/file.txt"
 _scratch_unmount
index d7ec573600c518581a60b49ff661363ae45edad0..611df3ab4217c2d78bb76387e18f1b75758fe3b2 100755 (executable)
@@ -30,7 +30,7 @@ assign_shared_test()
 
        echo "=== qgroup assign shared test ===" >> $seqres.full
        $BTRFS_UTIL_PROG quota enable $SCRATCH_MNT
-       $BTRFS_UTIL_PROG quota rescan -w $SCRATCH_MNT >> $seqres.full
+       _qgroup_rescan $SCRATCH_MNT >> $seqres.full
 
        $BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/a >> $seqres.full
        $BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/b >> $seqres.full
@@ -54,7 +54,7 @@ assign_no_shared_test()
 
        echo "=== qgroup assign no shared test ===" >> $seqres.full
        $BTRFS_UTIL_PROG quota enable $SCRATCH_MNT
-       $BTRFS_UTIL_PROG quota rescan -w $SCRATCH_MNT >> $seqres.full
+       _qgroup_rescan $SCRATCH_MNT >> $seqres.full
 
        $BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/a >> $seqres.full
        $BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/b >> $seqres.full
@@ -67,7 +67,7 @@ assign_no_shared_test()
        _check_scratch_fs
 }
 
-# Test snapshot with assigning qgroup for submodule
+# Test snapshot with assigning qgroup for higher level qgroup
 snapshot_test()
 {
        _scratch_mkfs > /dev/null 2>&1
@@ -75,12 +75,12 @@ snapshot_test()
 
        echo "=== qgroup snapshot test ===" >> $seqres.full
        $BTRFS_UTIL_PROG quota enable $SCRATCH_MNT
-       $BTRFS_UTIL_PROG quota rescan -w $SCRATCH_MNT >> $seqres.full
+       _qgroup_rescan $SCRATCH_MNT >> $seqres.full
 
        $BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/a >> $seqres.full
+       $BTRFS_UTIL_PROG qgroup create 1/0 $SCRATCH_MNT >> $seqres.full
        _ddt of="$SCRATCH_MNT"/a/file1 bs=1M count=1 >> $seqres.full 2>&1
-       subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT a)
-       $BTRFS_UTIL_PROG subvolume snapshot -i 0/$subvolid $SCRATCH_MNT/a $SCRATCH_MNT/b >> $seqres.full
+       $BTRFS_UTIL_PROG subvolume snapshot -i 1/0 $SCRATCH_MNT/a $SCRATCH_MNT/b >> $seqres.full
 
        _scratch_unmount
        _check_scratch_fs
index cfb64a342644e4090aa2df87e966f841323ce963..677c162cb63aec78cc150ae549d3e3a29c6e9a4e 100755 (executable)
@@ -5,8 +5,6 @@
 # FS QA Test 225
 #
 # Test for seed device-delete on a sprouted FS.
-# Requires kernel patch
-#    b5ddcffa3777  btrfs: fix put of uninitialized kobject after seed device delete
 #
 # Steps:
 #  Create a seed FS. Add a RW device to make it sprout FS and then delete
@@ -30,6 +28,8 @@ _cleanup()
 
 # Modify as appropriate.
 _supported_fs btrfs
+_fixed_by_kernel_commit b5ddcffa3777 \
+       "btrfs: fix put of uninitialized kobject after seed device delete"
 _require_test
 _require_scratch_dev_pool 2
 _require_btrfs_forget_or_module_loadable
index fde623fcc657b252aaec52e0d5c590fbad91e93b..5ef1dfd74188f89bd363ee5bf17bd2fa45d5cd8a 100755 (executable)
@@ -28,6 +28,11 @@ _scratch_mount
 $BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/newvol >> $seqres.full 2>&1 \
        || _fail "couldn't create subvol"
 
+# Subvolume creation used to commit the transaction used to create it, but after
+# the patch "btrfs: don't commit transaction for every subvol create", that
+# changed, so sync the fs to commit any open transaction.
+$BTRFS_UTIL_PROG filesystem sync $SCRATCH_MNT
+
 $BTRFS_UTIL_PROG inspect-internal dump-tree -t1 $SCRATCH_DEV \
        | grep -q "256 ROOT_ITEM"  ||   _fail "First subvol with id 256 doesn't exist"
 
index 46b0c63693a3130c55c37c9a5610304936bc9e67..7a4cd18e926872dd0316652422000a2bb3a1bbe1 100755 (executable)
@@ -31,7 +31,7 @@ _pwrite_byte 0xcd 0 1G $SCRATCH_MNT/file >> $seqres.full
 sync
 
 $BTRFS_UTIL_PROG quota enable $SCRATCH_MNT
-$BTRFS_UTIL_PROG quota rescan -w $SCRATCH_MNT >> $seqres.full
+_qgroup_rescan $SCRATCH_MNT >> $seqres.full
 
 # Set the limit to just 512MiB, which is way below the existing usage
 $BTRFS_UTIL_PROG qgroup limit  512M 0/5 $SCRATCH_MNT
index 02c7e49de8ceec49576e69a06b87b8b8e3630068..84c39c071d5296c198cc18223f6401e292e1401f 100755 (executable)
@@ -25,7 +25,7 @@ writer()
 
        while true; do
                args=`_scale_fsstress_args -p 20 -n 1000 $FSSTRESS_AVOID -d $SCRATCH_MNT/stressdir`
-               $FSSTRESS_PROG $args >/dev/null 2>&1
+               $FSSTRESS_PROG $args >> $seqres.full
        done
 }
 
@@ -46,7 +46,7 @@ _pwrite_byte 0xcd 0 900m $SCRATCH_MNT/file >> $seqres.full
 sync
 
 $BTRFS_UTIL_PROG quota enable $SCRATCH_MNT
-$BTRFS_UTIL_PROG quota rescan -w $SCRATCH_MNT >> $seqres.full
+_qgroup_rescan $SCRATCH_MNT >> $seqres.full
 # set the limit to 1 g, leaving us just 100mb of slack space
 $BTRFS_UTIL_PROG qgroup limit 1G 0/5 $SCRATCH_MNT
 
index 6a414443307301f9fdfe0bfa27feff2be77322a8..f2c1eba090be6ed35451e1b181c13d3f6099684b 100755 (executable)
@@ -9,7 +9,7 @@
 # performed.
 #
 . ./common/preamble
-_begin_fstest auto quick subvol
+_begin_fstest auto quick subvol remount
 
 # Override the default cleanup function.
 _cleanup()
@@ -21,6 +21,7 @@ _cleanup()
 
 # Import common functions.
 . ./common/filter
+. ./common/filter.btrfs
 . ./common/dmflakey
 
 # real QA test starts here
@@ -77,7 +78,7 @@ create_subvol_with_orphan()
        # open, delete the subvolume, then 'sync' to durably persist the orphan
        # item for the subvolume.
        exec 73< $SCRATCH_MNT/testsv/foobar
-       $BTRFS_UTIL_PROG subvolume delete $SCRATCH_MNT/testsv | _filter_scratch
+       $BTRFS_UTIL_PROG subvolume delete $SCRATCH_MNT/testsv | _filter_btrfs_subvol_delete
        sync
 
        # Now silently drop writes on the device, close the file descriptor and
index 492e959d895c74da1a15bde8572075a63ca7e7a0..2754e900834eecc93921f6b1134a7a1c71ae8f1a 100644 (file)
@@ -1,5 +1,5 @@
 QA output created by 233
 Create subvolume 'SCRATCH_MNT/testsv'
-Delete subvolume (no-commit): 'SCRATCH_MNT/testsv'
+Delete subvolume 'SCRATCH_MNT/testsv'
 Create subvolume 'SCRATCH_MNT/testsv'
-Delete subvolume (no-commit): 'SCRATCH_MNT/testsv'
+Delete subvolume 'SCRATCH_MNT/testsv'
index c045ea6c205626d025bcd042b4b399fb550ab37c..542515e81525d3007220551bcc514c24e6e5e847 100755 (executable)
@@ -18,6 +18,7 @@ _begin_fstest auto quick compress rw
 _supported_fs btrfs
 _require_scratch
 _require_odirect
+_require_btrfs_no_nodatacow
 _require_chattr c
 
 _scratch_mkfs >>$seqres.full 2>&1
index f96031d51d8bda5da7f4d9c175336e678199ceba..367019b6025d2567263dd102dcca27ffe8c86eb5 100755 (executable)
@@ -13,7 +13,7 @@
 _begin_fstest auto quick zone balance
 
 # Import common functions.
-. ./common/filter
+. ./common/zoned
 
 # real QA test starts here
 
@@ -43,7 +43,16 @@ get_data_bg_physical()
                grep -Eo 'offset [[:digit:]]+'| cut -d ' ' -f 2
 }
 
-_scratch_mkfs >/dev/null 2>&1
+sdev="$(_short_dev $SCRATCH_DEV)"
+zone_size=$(($(cat /sys/block/${sdev}/queue/chunk_sectors) << 9))
+fssize=$((zone_size * 16))
+devsize=$(($(_get_device_size $SCRATCH_DEV) * 1024))
+# Create a minimal FS to kick the reclaim process
+if [[ $devsize -gt $fssize ]]; then
+       _scratch_mkfs_sized $fssize >> $seqres.full 2>&1
+else
+       _scratch_mkfs >> $seqres.full 2>&1
+fi
 _scratch_mount -o commit=1 # 1s commit time to speed up test
 
 uuid=$($BTRFS_UTIL_PROG filesystem show $SCRATCH_DEV |grep uuid: |\
@@ -56,14 +65,18 @@ fi
 
 start_data_bg_phy=$(get_data_bg_physical)
 start_data_bg_phy=$((start_data_bg_phy >> 9))
-
-size=$($BLKZONE_PROG report -o $start_data_bg_phy -l 1 $SCRATCH_DEV |\
-       _filter_blkzone_report |\
-       grep -Po "cap 0x[[:xdigit:]]+" | cut -d ' ' -f 2)
-size=$((size << 9))
+size=$(_zone_capacity $start_data_bg_phy)
 
 reclaim_threshold=75
-echo $reclaim_threshold > /sys/fs/btrfs/"$uuid"/bg_reclaim_threshold
+if [[ -f /sys/fs/btrfs/"$uuid"/allocation/data/bg_reclaim_threshold ]]; then
+       fs_fill=1
+       echo $fs_fill > /sys/fs/btrfs/"$uuid"/bg_reclaim_threshold ||
+               _notrun "Need CONFIG_BTRFS_DEBUG to lower the reclaim threshold"
+       echo $reclaim_threshold > /sys/fs/btrfs/"$uuid"/allocation/data/bg_reclaim_threshold
+else
+       echo $reclaim_threshold > /sys/fs/btrfs/"$uuid"/bg_reclaim_threshold
+fi
+
 fill_percent=$((reclaim_threshold + 2))
 rest_percent=$((90 - fill_percent)) # make sure we're not creating a new BG
 fill_size=$((size * fill_percent / 100))
index 57245917e16a7d42f748d68552bc56e52262677a..3a711ea7a1a829dc89a9775861968296222587b8 100755 (executable)
@@ -6,9 +6,6 @@
 #
 # Check seed device integrity after fstrim on the sprout device.
 #
-#  Kernel bug is fixed by the commit:
-#    btrfs: fix unmountable seed device after fstrim
-
 . ./common/preamble
 _begin_fstest auto quick seed trim
 
@@ -19,6 +16,8 @@ _begin_fstest auto quick seed trim
 
 # Modify as appropriate.
 _supported_fs btrfs
+_fixed_by_kernel_commit 5e753a817b2d \
+       "btrfs: fix unmountable seed device after fstrim"
 _require_command "$BTRFS_TUNE_PROG" btrfstune
 _require_fstrim
 _require_scratch_dev_pool 2
index 3fbeaedd2c39c1fa6ddbe6fdafb585d36b8d9172..5a2dbe58d5fb288f8983a074e1f8f5bcec91cfbb 100755 (executable)
@@ -83,6 +83,18 @@ done
 #
 mkdir $SCRATCH_MNT/testdir/dira
 
+# This fsync is for the zoned mode. On the zoned mode, we use a dedicated block
+# group for tree-log. That block group is created on-demand or assigned to a
+# metadata block group if there is none. On the first mount of a file system, we
+# need to create one because there is only one metadata block group available
+# for the regular metadata. That creation of a new block group forces tree-log
+# to be a full commit on that transaction, which prevents logging "testdir" and
+# "dira" and screws up the result.
+#
+# Calling fsync here will create the dedicated block group, and let them be
+# logged.
+$XFS_IO_PROG -c "fsync" $SCRATCH_MNT
+
 # Make sure everything done so far is durably persisted.
 sync
 
index 0dcc7c0d1a4334256d739bca98469a18f95d2160..2fe54f9590480c099ce74bd74a2f45619ae66234 100755 (executable)
@@ -23,6 +23,9 @@ _cleanup()
 _supported_fs btrfs
 _require_scratch
 
+# If it's subpage case, we don't support inline extents creation for now.
+_require_btrfs_inline_extents_creation
+
 _scratch_mkfs > /dev/null
 _scratch_mount -o compress,max_inline=2048
 
index 7cc4996e387b769d756243cc479c74f811f27e7b..0355434b4b5be9e95ab17c03274e9c069b2538df 100755 (executable)
@@ -12,9 +12,6 @@
 #  Create a sprout filesystem (an rw device on top of a seed device)
 #  Dump 'btrfs filesystem usage', check it didn't fail
 #
-# Tests btrfs-progs bug fixed by the kernel patch and a btrfs-prog patch
-#   btrfs: sysfs add devinfo/fsid to retrieve fsid from the device
-#   btrfs-progs: read fsid from the sysfs in device_is_seed
 
 . ./common/preamble
 _begin_fstest auto quick seed volume
@@ -29,6 +26,10 @@ _supported_fs btrfs
 _require_scratch_dev_pool 3
 _require_command "$WIPEFS_PROG" wipefs
 _require_btrfs_forget_or_module_loadable
+_wants_kernel_commit a26d60dedf9a \
+       "btrfs: sysfs: add devinfo/fsid to retrieve actual fsid from the device"
+_fixed_by_git_commit btrfs-progs 32c2e57c65b9 \
+       "btrfs-progs: read fsid from the sysfs in device_is_seed"
 
 _scratch_dev_pool_get 2
 # use the scratch devices as seed devices
index 4b6edd6cbeee582194296bd20df8eb6ab10e0f7f..af01095828f5ebe830b4673c16f8d30ecd9d56d2 100755 (executable)
@@ -19,7 +19,7 @@ _begin_fstest auto quick compress dangerous
 _supported_fs btrfs
 _require_scratch
 
-pagesize=$(get_page_size)
+pagesize=$(_get_page_size)
 
 # Read the content from urandom to a known safe location
 $XFS_IO_PROG -f -c "pwrite -i /dev/urandom 0 $pagesize" "$tmp.good" > /dev/null
index 65ebe5713ddbc59911c49485927ccc429f1b3cd8..8b4809b7481e0c504a300013ed72948f6d9f66f8 100755 (executable)
@@ -180,7 +180,7 @@ for i in "${!snapshots[@]}"; do
        # case, so we don't have a mismatch with the golden output in case we
        # run with a non default $LOAD_FACTOR (default is 1). We only want the
        # mismatch with the golden output in case there's a checksum failure.
-       $FSSUM_PROG -r "$snap_csum" "$snap_copy" | egrep -v '^OK$'
+       $FSSUM_PROG -r "$snap_csum" "$snap_copy" | grep -E -v '^OK$'
 done
 
 echo "Silence is golden"
index fbbb81fae75480524cebb788b0170455c40d0ce6..5fbce070f5d11c53d326d62a8edf1ec3c68de8e8 100755 (executable)
@@ -81,6 +81,8 @@ alloc_size() {
 _supported_fs btrfs
 _require_test
 _require_scratch
+# The chunk size on zoned mode is fixed to the zone size
+_require_non_zoned_device "$SCRATCH_DEV"
 
 # Delete log file if it exists.
 rm -f "${seqres}.full"
@@ -105,7 +107,21 @@ SCRATCH_BDEV=$(_real_dev $SCRATCH_DEV)
 
 # Get chunk sizes.
 echo "Capture default chunk sizes."
-FIRST_DATA_CHUNK_SIZE_B=$(_get_fs_sysfs_attr ${SCRATCH_BDEV} allocation/data/chunk_size)
+
+# The sysfs interface has only exported chunk_size (10G by default),
+# but no stripe_size (1G by default) exported.
+#
+# Btrfs calculate the real chunk to be allocated using both limits,
+# Thus the default 10G chunk size can only be fulfilled by something
+# like 10 disk RAID0
+#
+# The proper solution should be exporting the stripe_size limit too,
+# but that may take several cycles, here we just use hard coded 1G
+# as the expected chunk size.
+FIRST_DATA_CHUNK_SIZE_B=$((1024 * 1024 * 1024))
+
+# For metadata, we are safe to use the exported value, as the default
+# metadata chunk size limit is already smaller than its stripe size.
 FIRST_METADATA_CHUNK_SIZE_B=$(_get_fs_sysfs_attr ${SCRATCH_BDEV} allocation/metadata/chunk_size)
 
 echo "Orig Data chunk size    = ${FIRST_DATA_CHUNK_SIZE_B}"     >> ${seqres}.full
@@ -224,7 +240,7 @@ FIRST_METADATA_CHUNK_SIZE_MB=$(expr ${FIRST_METADATA_CHUNK_SIZE_B} / 1024 / 1024
 
 # if [[ $(expr ${FIRST_DATA_CHUNK_SIZE_MB} + ${DATA_SIZE_START_MB}) -ne $(expr ${FIRST_DATA_SIZE_MB}) ]]; then
 if [[ $(expr ${FIRST_DATA_CHUNK_SIZE_MB} + ${DATA_SIZE_START_MB}) -ne ${FIRST_DATA_SIZE_MB} ]]; then
-       echo "tInitial data allocation not correct."
+       echo "Initial data allocation not correct."
 fi
 
 if [[ $(expr ${FIRST_METADATA_CHUNK_SIZE_MB} + ${METADATA_SIZE_START_MB}) -ne ${FIRST_METADATA_SIZE_MB} ]]; then
index 5d6e33f40bb51e7698f9bffa42bb249fb2199a3b..a358e374a21aefaf5b9a1c886c936ad8d03752fa 100755 (executable)
@@ -7,9 +7,6 @@
 #
 # Test if the kernel can free the stale device entries.
 #
-# Tests bug fixed by the kernel patch:
-#      btrfs: harden identification of the stale device
-#
 . ./common/preamble
 _begin_fstest auto quick volume
 
@@ -40,6 +37,10 @@ _require_dm_target linear
 _require_btrfs_forget_or_module_loadable
 _require_scratch_nocheck
 _require_command "$WIPEFS_PROG" wipefs
+_check_minimal_fs_size $((1024 * 1024 * 1024))
+
+_fixed_by_kernel_commit 770c79fb6550 \
+       "btrfs: harden identification of a stale device"
 
 _scratch_dev_pool_get 3
 
index 1360c2c2021d8d21c11f316c9df7908fe0c51928..acbbc6fa5d4c0f16f3a5bb010b1188b42f2daee9 100755 (executable)
@@ -50,7 +50,9 @@ $FSSUM_PROG -A -f -w "$checksums_file" "$SCRATCH_MNT"
 # Now defrag each file.
 for sz in ${file_sizes[@]}; do
        echo "Defragging file with $sz bytes..." >> $seqres.full
-       $BTRFS_UTIL_PROG filesystem defragment "$SCRATCH_MNT/f_$sz"
+       # In new versions of btrfs-progs (6.0+), the defrag command outputs to
+       # stdout the path of the files it operates on. So ignore that.
+       $BTRFS_UTIL_PROG filesystem defragment "$SCRATCH_MNT/f_$sz" > /dev/null
 done
 
 # Verify the checksums after the defrag operations.
index 5c5cdcc3ddcbc665a521ec7b5b9e226e01f91c41..f8e69f7a6a56ed114359ed3230036e1262ff68a5 100755 (executable)
@@ -9,7 +9,7 @@
 #
 
 . ./common/preamble
-_begin_fstest auto quick defrag prealloc
+_begin_fstest auto quick defrag prealloc fiemap
 
 # Override the default cleanup function.
 # _cleanup()
@@ -27,9 +27,13 @@ _begin_fstest auto quick defrag prealloc
 _supported_fs btrfs
 _require_scratch
 
+# We rely on specific extent layout, don't run on compress
+_require_btrfs_no_compress
+
 # Needs 4K sectorsize
 _require_btrfs_support_sectorsize 4096
 _require_xfs_io_command "falloc"
+_require_xfs_io_command "fiemap"
 
 _scratch_mkfs -s 4k >> $seqres.full 2>&1
 
index 158eaf79a92c87752fd60c32ddadbb4573e32649..a4d15c431e6c926476da0c0d8862451d0e49821f 100755 (executable)
@@ -8,9 +8,8 @@
 # in the middle
 #
 . ./common/preamble
-_begin_fstest auto defrag quick
+_begin_fstest auto defrag quick fiemap remount
 
-. ./common/btrfs
 . ./common/filter
 
 # real QA test starts here
@@ -18,6 +17,7 @@ _begin_fstest auto defrag quick
 # Modify as appropriate.
 _supported_fs generic
 _require_scratch
+_require_xfs_io_command "fiemap"
 
 # Needs 4K sectorsize, as larger sectorsize can change the file layout.
 _require_btrfs_support_sectorsize 4096
index e7bd8ddd7f8b52aabb001e7af61b9a6da4cee00c..7f053ac938339fab7c0a29c69dd6f39fa40de761 100755 (executable)
@@ -8,17 +8,17 @@
 # at their max capacity.
 #
 . ./common/preamble
-_begin_fstest auto quick defrag
+_begin_fstest auto quick defrag fiemap compress
 
 # Import common functions.
 . ./common/filter
-. ./common/btrfs
 
 # real QA test starts here
 
 # Modify as appropriate.
 _supported_fs btrfs
 _require_scratch
+_require_xfs_io_command "fiemap"
 
 _scratch_mkfs >> $seqres.full
 
@@ -40,7 +40,7 @@ sync
 
 new_csum=$(_md5_checksum $SCRATCH_MNT/foobar)
 
-echo "=== File extent layout before defrag ===" >> $seqres.full
+echo "=== File extent layout after defrag ===" >> $seqres.full
 $XFS_IO_PROG -c "fiemap -v" "$SCRATCH_MNT/foobar" >> $seqres.full
 $XFS_IO_PROG -c "fiemap -v" "$SCRATCH_MNT/foobar" > $tmp.after
 
index 8042ec4df15f0c94c7ab207d9ccc2c0ac228725d..111a3bd6127c69a3af707009f5c31b9bebc1188e 100755 (executable)
@@ -8,7 +8,7 @@
 # algorithm of all regular extents.
 #
 . ./common/preamble
-_begin_fstest auto quick defrag compress prealloc
+_begin_fstest auto quick defrag compress prealloc fiemap
 
 # Override the default cleanup function.
 # _cleanup()
diff --git a/tests/btrfs/261 b/tests/btrfs/261
new file mode 100755 (executable)
index 0000000..58fa8e7
--- /dev/null
@@ -0,0 +1,89 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 261
+#
+# Make sure btrfs raid profiles can handling one corrupted device
+# without affecting the consistency of the fs.
+#
+. ./common/preamble
+_begin_fstest auto volume raid scrub
+
+_supported_fs btrfs
+_require_scratch_dev_pool 4
+_btrfs_get_profile_configs replace-missing
+_require_fssum
+
+prepare_fs()
+{
+       local mkfs_opts=$1
+
+       # We don't want too large fs which can take too long to populate
+       # And the extra redirection of stderr is to avoid the RAID56 warning
+       # message to polluate the golden output
+       _scratch_pool_mkfs $mkfs_opts -b 1G >> $seqres.full 2>&1
+       if [ $? -ne 0 ]; then
+               _fail "mkfs $mkfs_opts failed"
+       fi
+
+       # Disable compression, as compressed read repair is known to have problems
+       _scratch_mount -o compress=no
+
+       # Fill some part of the fs first
+       $XFS_IO_PROG -f -c "pwrite -S 0xfe 0 400M" $SCRATCH_MNT/garbage > /dev/null 2>&1
+
+       # Then use fsstress to generate some extra contents.
+       # Disable setattr related operations, as it may set NODATACOW which will
+       # not allow us to use btrfs checksum to verify the content.
+       $FSSTRESS_PROG -f setattr=0 -d $SCRATCH_MNT -w -n 3000 >> $seqres.full
+       sync
+
+       # Save the fssum of this fs
+       $FSSUM_PROG -A -f -w $tmp.saved_fssum $SCRATCH_MNT
+       _scratch_unmount
+}
+
+workload()
+{
+       local mkfs_opts=$1
+       local num_devs=$2
+
+       _scratch_dev_pool_get 4
+       echo "=== Testing profile $mkfs_opts ===" >> $seqres.full
+       rm -f -- $tmp.saved_fssum
+       prepare_fs "$mkfs_opts"
+
+       # $SCRATCH_DEV is always the first device of dev pool.
+       # Corrupt the disk but keep the primary superblock.
+       $XFS_IO_PROG -c "pwrite 1M 1023M" $SCRATCH_DEV > /dev/null 2>&1
+
+       _scratch_mount
+
+       # All content should be fine
+       $FSSUM_PROG -r $tmp.saved_fssum $SCRATCH_MNT > /dev/null
+
+       # Scrub to fix the fs, this is known to report various correctable
+       # errors
+       $BTRFS_UTIL_PROG scrub start -B $SCRATCH_MNT >> $seqres.full 2>&1
+
+       # Make sure above scrub fixed the fs
+       # Redirect the stderr to seqres.full as well to avoid warnings if
+       # /var/lib filesystem is readonly, as scrub fails to write status.
+       $BTRFS_UTIL_PROG scrub start -Br $SCRATCH_MNT >> $seqres.full 2>&1
+       if [ $? -ne 0 ]; then
+               echo "scrub failed to fix the fs for profile $mkfs_opts"
+       fi
+       _scratch_unmount
+       _scratch_dev_pool_put
+}
+
+for t in "${_btrfs_profile_configs[@]}"; do
+       workload "$t"
+done
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/261.out b/tests/btrfs/261.out
new file mode 100644 (file)
index 0000000..679ddc0
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 261
+Silence is golden
index d201f14e9e87ea9d465c7575521732e76a46bb73..5bf8d49998f39a60afeacf59e3ceff1dca8c6d1c 100755 (executable)
@@ -8,7 +8,7 @@
 # defragmentation.
 #
 . ./common/preamble
-_begin_fstest auto quick defrag
+_begin_fstest auto quick defrag fiemap remount
 
 # Import common functions.
 . ./common/filter
diff --git a/tests/btrfs/265 b/tests/btrfs/265
new file mode 100755 (executable)
index 0000000..127da7a
--- /dev/null
@@ -0,0 +1,87 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2017 Liu Bo.  All Rights Reserved.
+# Copyright (c) 2022 Christoph Hellwig.
+#
+# FS QA Test 265
+#
+# Test that btrfs raid repair on a raid1c3 profile can repair corruption on two
+# mirrors for the same logical offset.
+#
+. ./common/preamble
+_begin_fstest auto quick read_repair
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+_supported_fs btrfs
+_require_scratch_dev_pool 3
+
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
+_require_odirect
+# Overwriting data is forbidden on a zoned block device
+_require_non_zoned_device "${SCRATCH_DEV}"
+
+_scratch_dev_pool_get 3
+# step 1, create a raid1 btrfs which contains one 128k file.
+echo "step 1......mkfs.btrfs"
+
+mkfs_opts="-d raid1c3 -b 1G"
+_scratch_pool_mkfs $mkfs_opts >>$seqres.full 2>&1
+
+_scratch_mount
+
+$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 128K 0 128K" \
+       "$SCRATCH_MNT/foobar" | \
+       _filter_xfs_io_offset
+
+# step 2, corrupt the first 64k of one copy (on SCRATCH_DEV which is the first
+# one in $SCRATCH_DEV_POOL
+echo "step 2......corrupt file extent"
+
+# ensure btrfs-map-logical sees the tree updates
+sync
+
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+
+physical1=$(_btrfs_get_physical ${logical} 1)
+devpath1=$(_btrfs_get_device_path ${logical} 1)
+
+physical2=$(_btrfs_get_physical ${logical} 2)
+devpath2=$(_btrfs_get_device_path ${logical} 2)
+
+_scratch_unmount
+
+echo " corrupt stripe #1, devpath $devpath1 physical $physical1" \
+       >> $seqres.full
+$XFS_IO_PROG -d -c "pwrite -S 0xbf -b 64K $physical1 64K" $devpath1 \
+       > /dev/null
+
+echo " corrupt stripe #2, devpath $devpath2 physical $physical2" \
+       >> $seqres.full
+$XFS_IO_PROG -d -c "pwrite -S 0xbf -b 64K $physical2 64K" $devpath2 \
+       > /dev/null
+
+_scratch_mount
+
+# step 3, 128k dio read (this read can repair bad copy)
+echo "step 3......repair the bad copy"
+
+_btrfs_direct_read_on_mirror 0 3 "$SCRATCH_MNT/foobar" 0 128K
+_btrfs_direct_read_on_mirror 1 3 "$SCRATCH_MNT/foobar" 0 128K
+
+_scratch_unmount
+
+echo "step 4......check if the repair worked"
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical1 512" $devpath1 |\
+       _filter_xfs_io_offset
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical2 512" $devpath2 |\
+       _filter_xfs_io_offset
+
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/265.out b/tests/btrfs/265.out
new file mode 100644 (file)
index 0000000..c62c7a3
--- /dev/null
@@ -0,0 +1,75 @@
+QA output created by 265
+step 1......mkfs.btrfs
+wrote 131072/131072 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+step 2......corrupt file extent
+step 3......repair the bad copy
+step 4......check if the repair worked
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
diff --git a/tests/btrfs/266 b/tests/btrfs/266
new file mode 100755 (executable)
index 0000000..acfb1d5
--- /dev/null
@@ -0,0 +1,95 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2017 Liu Bo.  All Rights Reserved.
+# Copyright (c) 2022 Christoph Hellwig.
+#
+# FS QA Test 266
+#
+# Test that btrfs raid repair on a raid1c3 profile can repair interleaving
+# errors on all mirrors.
+#
+
+. ./common/preamble
+_begin_fstest auto quick read_repair
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+_supported_fs btrfs
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
+_require_scratch_dev_pool 3
+
+_require_odirect
+# Overwriting data is forbidden on a zoned block device
+_require_non_zoned_device "${SCRATCH_DEV}"
+
+_scratch_dev_pool_get 3
+# step 1, create a raid1 btrfs which contains one 128k file.
+echo "step 1......mkfs.btrfs"
+
+mkfs_opts="-d raid1c3 -b 1G"
+_scratch_pool_mkfs $mkfs_opts >>$seqres.full 2>&1
+
+_scratch_mount
+
+$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 256K 0 256K" \
+       "$SCRATCH_MNT/foobar" | \
+       _filter_xfs_io_offset
+
+# step 2, corrupt 64k in each copy
+echo "step 2......corrupt file extent"
+
+# ensure btrfs-map-logical sees the tree updates
+sync
+
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+
+physical1=$(_btrfs_get_physical ${logical} 1)
+devpath1=$(_btrfs_get_device_path ${logical} 1)
+
+physical2=$(_btrfs_get_physical ${logical} 2)
+devpath2=$(_btrfs_get_device_path ${logical} 2)
+
+physical3=$(_btrfs_get_physical ${logical} 3)
+devpath3=$(_btrfs_get_device_path ${logical} 3)
+
+_scratch_unmount
+
+$XFS_IO_PROG -d -c "pwrite -S 0xbd -b 64K $physical3 64K" \
+       $devpath3 > /dev/null
+
+$XFS_IO_PROG -d -c "pwrite -S 0xba -b 64K $physical1 128K" \
+       $devpath1 > /dev/null
+
+$XFS_IO_PROG -d -c "pwrite -S 0xbb -b 64K $((physical2 + 65536)) 128K" \
+       $devpath2 > /dev/null
+
+$XFS_IO_PROG -d -c "pwrite -S 0xbc -b 64K $((physical3 + (2 * 65536))) 128K"  \
+       $devpath3 > /dev/null
+
+_scratch_mount
+
+# step 3, 128k dio read (this read can repair bad copy)
+echo "step 3......repair the bad copy"
+
+_btrfs_buffered_read_on_mirror 0 3 "$SCRATCH_MNT/foobar" 0 256K
+_btrfs_buffered_read_on_mirror 1 3 "$SCRATCH_MNT/foobar" 0 256K
+_btrfs_buffered_read_on_mirror 2 3 "$SCRATCH_MNT/foobar" 0 256K
+
+_scratch_unmount
+
+echo "step 4......check if the repair worked"
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical1 512" $devpath1 |\
+       _filter_xfs_io_offset
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical2 512" $devpath2 |\
+       _filter_xfs_io_offset
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical3 512" $devpath3 |\
+       _filter_xfs_io_offset
+
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/266.out b/tests/btrfs/266.out
new file mode 100644 (file)
index 0000000..fcf2f5b
--- /dev/null
@@ -0,0 +1,109 @@
+QA output created by 266
+step 1......mkfs.btrfs
+wrote 262144/262144 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+step 2......corrupt file extent
+step 3......repair the bad copy
+step 4......check if the repair worked
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
diff --git a/tests/btrfs/267 b/tests/btrfs/267
new file mode 100755 (executable)
index 0000000..51b28d9
--- /dev/null
@@ -0,0 +1,95 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2017 Liu Bo.  All Rights Reserved.
+# Copyright (c) 2022 Christoph Hellwig.
+#
+# FS QA Test 267
+#
+# Test that btrfs buffered read repair on a raid1c3 profile can repair
+# interleaving errors on all mirrors.
+#
+
+. ./common/preamble
+_begin_fstest auto quick read_repair
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+_supported_fs btrfs
+_require_scratch_dev_pool 3
+
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
+_require_odirect
+# Overwriting data is forbidden on a zoned block device
+_require_non_zoned_device "${SCRATCH_DEV}"
+
+_scratch_dev_pool_get 3
+# step 1, create a raid1 btrfs which contains one 128k file.
+echo "step 1......mkfs.btrfs"
+
+mkfs_opts="-d raid1c3 -b 1G"
+_scratch_pool_mkfs $mkfs_opts >>$seqres.full 2>&1
+
+_scratch_mount
+
+$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 256K 0 256K" \
+       "$SCRATCH_MNT/foobar" | \
+       _filter_xfs_io_offset
+
+# step 2, corrupt 64k in each copy
+echo "step 2......corrupt file extent"
+
+# ensure btrfs-map-logical sees the tree updates
+sync
+
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+
+physical1=$(_btrfs_get_physical ${logical} 1)
+devpath1=$(_btrfs_get_device_path ${logical} 1)
+
+physical2=$(_btrfs_get_physical ${logical} 2)
+devpath2=$(_btrfs_get_device_path ${logical} 2)
+
+physical3=$(_btrfs_get_physical ${logical} 3)
+devpath3=$(_btrfs_get_device_path ${logical} 3)
+
+_scratch_unmount
+
+$XFS_IO_PROG -d -c "pwrite -S 0xbd -b 64K $physical3 64K" \
+       $devpath3 > /dev/null
+
+$XFS_IO_PROG -d -c "pwrite -S 0xba -b 64K $physical1 128K" \
+       $devpath1 > /dev/null
+
+$XFS_IO_PROG -d -c "pwrite -S 0xbb -b 64K $((physical2 + 65536)) 128K" \
+       $devpath2 > /dev/null
+
+$XFS_IO_PROG -d -c "pwrite -S 0xbc -b 64K $((physical3 + (2 * 65536))) 128K"  \
+       $devpath3 > /dev/null
+
+_scratch_mount
+
+# step 3, 128k dio read (this read can repair bad copy)
+echo "step 3......repair the bad copy"
+
+_btrfs_direct_read_on_mirror 0 3 "$SCRATCH_MNT/foobar" 0 256K
+_btrfs_direct_read_on_mirror 1 3 "$SCRATCH_MNT/foobar" 0 256K
+_btrfs_direct_read_on_mirror 2 3 "$SCRATCH_MNT/foobar" 0 256K
+
+_scratch_unmount
+
+echo "step 4......check if the repair worked"
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical1 512" $devpath1 |\
+       _filter_xfs_io_offset
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical2 512" $devpath2 |\
+       _filter_xfs_io_offset
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical3 512" $devpath3 |\
+       _filter_xfs_io_offset
+
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/267.out b/tests/btrfs/267.out
new file mode 100644 (file)
index 0000000..2bdd32e
--- /dev/null
@@ -0,0 +1,109 @@
+QA output created by 267
+step 1......mkfs.btrfs
+wrote 262144/262144 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+step 2......corrupt file extent
+step 3......repair the bad copy
+step 4......check if the repair worked
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
diff --git a/tests/btrfs/268 b/tests/btrfs/268
new file mode 100755 (executable)
index 0000000..d122ee3
--- /dev/null
@@ -0,0 +1,65 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Christoph Hellwig.
+#
+# FS QA Test 268
+#
+# Test that btrfs read repair on a raid1 profile won't loop forever if data
+# is corrupted on both mirrors and can't be recovered.
+#
+
+. ./common/preamble
+_begin_fstest auto quick read_repair
+
+. ./common/filter
+
+_supported_fs btrfs
+_require_scratch
+_require_odirect
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
+_require_non_zoned_device "${SCRATCH_DEV}" # no overwrites on zoned devices
+_require_scratch_dev_pool 2
+_scratch_dev_pool_get 2
+
+echo "step 1......mkfs.btrfs"
+
+_scratch_pool_mkfs "-d raid1 -b 1G" >>$seqres.full 2>&1
+_scratch_mount
+
+$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 256K 0 256K" \
+       "$SCRATCH_MNT/foobar" | \
+       _filter_xfs_io_offset
+
+# ensure btrfs-map-logical sees the tree updates
+sync
+
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+
+physical1=$(_btrfs_get_physical ${logical} 1)
+devpath1=$(_btrfs_get_device_path ${logical} 1)
+
+physical2=$(_btrfs_get_physical ${logical} 2)
+devpath2=$(_btrfs_get_device_path ${logical} 2)
+
+physical3=$(_btrfs_get_physical ${logical} 3)
+devpath3=$(_btrfs_get_device_path ${logical} 3)
+
+_scratch_unmount
+
+echo "step 2......corrupt file extent"
+$XFS_IO_PROG -d -c "pwrite -S 0xba -b 4K $physical1 4K" \
+       $devpath1 > /dev/null
+$XFS_IO_PROG -d -c "pwrite -S 0xba -b 4K $physical2 4K" \
+       $devpath2 > /dev/null
+
+_scratch_mount
+
+echo "step 3......try to repair"
+$XFS_IO_PROG -d -c "pread -b 4K 0 4K" $SCRATCH_MNT/foobar
+
+_scratch_unmount
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/268.out b/tests/btrfs/268.out
new file mode 100644 (file)
index 0000000..d28b37b
--- /dev/null
@@ -0,0 +1,7 @@
+QA output created by 268
+step 1......mkfs.btrfs
+wrote 262144/262144 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+step 2......corrupt file extent
+step 3......try to repair
+pread: Input/output error
diff --git a/tests/btrfs/269 b/tests/btrfs/269
new file mode 100755 (executable)
index 0000000..7ffad12
--- /dev/null
@@ -0,0 +1,76 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Christoph Hellwig.
+#
+# FS QA Test 269
+#
+# Test btrfs read repair over tricky stripe boundaries on the raid10 profile:
+#
+#             | stripe 0 | stripe 2
+#   --------------------------------
+#    mirror 1 | I/O FAIL | GOOD    
+#    mirror 2 | GOOD     | CSUM FAIL
+#
+
+. ./common/preamble
+_begin_fstest auto quick read_repair
+
+. ./common/filter
+
+_supported_fs btrfs
+_require_scratch
+_require_odirect
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
+_require_non_zoned_device "${SCRATCH_DEV}" # no overwrites on zoned devices
+_require_scratch_dev_pool 4
+_scratch_dev_pool_get 4
+
+echo "step 1......mkfs.btrfs"
+
+_scratch_pool_mkfs "-d raid10 -b 1G" >>$seqres.full 2>&1
+_scratch_mount
+
+$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 128K 0 128K" \
+       "$SCRATCH_MNT/foobar" | \
+       _filter_xfs_io_offset
+
+# ensure btrfs-map-logical sees the tree updates
+sync
+
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+
+physical1=$(_btrfs_get_physical ${logical} 1)
+devpath1=$(_btrfs_get_device_path ${logical} 1)
+
+physical4=$(_btrfs_get_physical ${logical} 3)
+devpath4=$(_btrfs_get_device_path ${logical} 3)
+
+_scratch_unmount
+
+echo "step 2......corrupt file extent"
+
+$XFS_IO_PROG -d -c "pwrite -S 0xbd -b 64K $physical1 64K" \
+       $devpath1 > /dev/null
+$XFS_IO_PROG -d -c "pwrite -S 0xbb -b 64K $((physical4 + 65536)) 64K" \
+       $devpath4 > /dev/null
+
+_scratch_mount
+
+echo "step 3......repair the bad copy"
+
+_btrfs_direct_read_on_mirror 0 2 "$SCRATCH_MNT/foobar" 0 128K
+_btrfs_direct_read_on_mirror 1 2 "$SCRATCH_MNT/foobar" 0 128K
+
+_scratch_unmount
+
+echo "step 4......check if the repair worked"
+$XFS_IO_PROG -d -c "pread -v -b 512 $physical1 512" $devpath1 |\
+       _filter_xfs_io_offset
+$XFS_IO_PROG -d -c "pread -v -b 512 $((physical4 + 65536)) 512" $devpath4 |\
+       _filter_xfs_io_offset
+
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/269.out b/tests/btrfs/269.out
new file mode 100644 (file)
index 0000000..d3ad7f0
--- /dev/null
@@ -0,0 +1,41 @@
+QA output created by 269
+step 1......mkfs.btrfs
+wrote 131072/131072 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+step 2......corrupt file extent
+step 3......repair the bad copy
+step 4......check if the repair worked
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+read 512/512 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
diff --git a/tests/btrfs/270 b/tests/btrfs/270
new file mode 100755 (executable)
index 0000000..221ef7d
--- /dev/null
@@ -0,0 +1,83 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2017 Liu Bo.  All Rights Reserved.
+#
+# FS QA Test 270
+#
+# Regression test for btrfs buffered read repair of compressed data.
+#
+. ./common/preamble
+_begin_fstest auto quick read_repair compress
+
+. ./common/filter
+
+_supported_fs btrfs
+_require_scratch
+_require_btrfs_command inspect-internal dump-tree
+_require_non_zoned_device "${SCRATCH_DEV}" # no overwrites on zoned devices
+_require_scratch_dev_pool 2
+_scratch_dev_pool_get 2
+
+get_physical()
+{
+       local logical=$1
+       local stripe=$2
+       $BTRFS_UTIL_PROG inspect-internal dump-tree -t 3 $SCRATCH_DEV | \
+               grep $logical -A 6 | \
+               $AWK_PROG "(\$1 ~ /stripe/ && \$3 ~ /devid/ && \$2 ~ /$stripe/) { print \$6 }"
+}
+
+get_devid()
+{
+       local logical=$1
+       local stripe=$2
+       $BTRFS_UTIL_PROG inspect-internal dump-tree -t 3 $SCRATCH_DEV | \
+               grep $logical -A 6 | \
+               $AWK_PROG "(\$1 ~ /stripe/ && \$3 ~ /devid/ && \$2 ~ /$stripe/) { print \$4 }"
+}
+
+get_device_path()
+{
+       local devid=$1
+       echo "$SCRATCH_DEV_POOL" | $AWK_PROG "{print \$$devid}"
+}
+
+
+echo "step 1......mkfs.btrfs"
+_check_minimal_fs_size $(( 1024 * 1024 * 1024 ))
+_scratch_pool_mkfs "-d raid1 -b 1G" >>$seqres.full 2>&1
+_scratch_mount -ocompress
+
+# Create a file with all data being compressed
+$XFS_IO_PROG -f -c "pwrite -S 0xaa -W -b 128K 0 128K" \
+       "$SCRATCH_MNT/foobar" | _filter_xfs_io_offset
+
+logical_in_btrfs=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+physical=$(get_physical ${logical_in_btrfs} 1)
+devid=$(get_devid ${logical_in_btrfs} 1)
+devpath=$(get_device_path ${devid})
+
+_scratch_unmount
+echo "step 2......corrupt file extent"
+echo " corrupt stripe #1, devid $devid devpath $devpath physical $physical" \
+       >> $seqres.full
+dd if=$devpath of=$TEST_DIR/$seq.dump.good skip=$physical bs=1 count=4096 \
+       2>/dev/null
+$XFS_IO_PROG -c "pwrite -S 0xbb -b 4K $physical 4K" $devpath > /dev/null
+
+_scratch_mount
+
+echo "step 3......repair the bad copy"
+_btrfs_buffered_read_on_mirror 1 2 "$SCRATCH_MNT/foobar" 0 128K
+
+_scratch_unmount
+
+echo "step 4......check if the repair worked"
+dd if=$devpath of=$TEST_DIR/$seq.dump skip=$physical bs=1 count=4096 \
+       2>/dev/null
+cmp -bl $TEST_DIR/$seq.dump.good $TEST_DIR/$seq.dump
+
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/270.out b/tests/btrfs/270.out
new file mode 100644 (file)
index 0000000..6d744c0
--- /dev/null
@@ -0,0 +1,7 @@
+QA output created by 270
+step 1......mkfs.btrfs
+wrote 131072/131072 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+step 2......corrupt file extent
+step 3......repair the bad copy
+step 4......check if the repair worked
diff --git a/tests/btrfs/271 b/tests/btrfs/271
new file mode 100755 (executable)
index 0000000..a342af3
--- /dev/null
@@ -0,0 +1,61 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Christoph Hellwig.
+#
+# FS QA Test btrfs/271
+#
+# Test btrfs write error propagation and reporting on the raid1 profile.
+#
+. ./common/preamble
+_begin_fstest auto quick raid
+
+. ./common/filter
+. ./common/fail_make_request
+
+_supported_fs btrfs
+_require_scratch
+_require_fail_make_request
+_require_scratch_dev_pool 2
+_scratch_dev_pool_get 2
+
+_check_minimal_fs_size $(( 1024 * 1024 * 1024 ))
+_scratch_pool_mkfs "-d raid1 -b 1G" >> $seqres.full 2>&1
+
+_scratch_mount
+
+dev2=`echo $SCRATCH_DEV_POOL | $AWK_PROG '{print $2}'`
+
+_allow_fail_make_request
+
+echo "Step 1: writing with one failing mirror:"
+_bdev_fail_make_request $SCRATCH_DEV 1
+$XFS_IO_PROG -f -c "pwrite -W -S 0xaa 0 8K" $SCRATCH_MNT/foobar | _filter_xfs_io
+_bdev_fail_make_request $SCRATCH_DEV 0
+
+# btrfs counts errors per IO, assuming the data is merged that'll be 1 IO, then
+# the log tree block and then the log root tree block and then the super block.
+# We should see at least 4 failed IO's, but with subpage blocksize we could see
+# more if the log blocks end up on the same page, or if the data IO gets split
+# at all.
+errs=$($BTRFS_UTIL_PROG device stats $SCRATCH_DEV | \
+       $AWK_PROG '/write_io_errs/ { print $2 }')
+if [ $errs -lt 4 ]; then
+        _fail "Errors: $errs expected: 4"
+fi
+
+echo "Step 2: verify that the data reads back fine:"
+$XFS_IO_PROG -c "pread -v 0 8K" $SCRATCH_MNT/foobar | _filter_xfs_io_offset
+
+echo "Step 3: writing with two failing mirrors (should fail):"
+_bdev_fail_make_request $SCRATCH_DEV 1
+_bdev_fail_make_request $dev2 1
+$XFS_IO_PROG -f -c "pwrite -W -S 0xbb 0 8K" $SCRATCH_MNT/foobar | _filter_xfs_io
+_bdev_fail_make_request $dev2 0
+_bdev_fail_make_request $SCRATCH_DEV 0
+
+_disallow_fail_make_request
+
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/271.out b/tests/btrfs/271.out
new file mode 100644 (file)
index 0000000..d58c92f
--- /dev/null
@@ -0,0 +1,523 @@
+QA output created by 271
+Allow global fail_make_request feature
+Step 1: writing with one failing mirror:
+wrote 8192/8192 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Step 2: verify that the data reads back fine:
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
+read 8192/8192 bytes
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Step 3: writing with two failing mirrors (should fail):
+fsync: Input/output error
+Disallow global fail_make_request feature
diff --git a/tests/btrfs/272 b/tests/btrfs/272
new file mode 100755 (executable)
index 0000000..a49ec07
--- /dev/null
@@ -0,0 +1,88 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 BingJing Chang.
+#
+# FS QA Test No. btrfs/272
+#
+# Regression test for btrfs incremental send issue where a link instruction
+# is sent against an existing path, causing btrfs receive to fail.
+#
+# This issue is fixed by the following linux kernel btrfs patch:
+#
+#   commit 3aa5bd367fa5a3 ("btrfs: send: fix sending link commands for
+#   existing file paths")
+#
+. ./common/preamble
+_begin_fstest auto quick send
+
+# real QA test starts here
+_supported_fs btrfs
+_fixed_by_kernel_commit 3aa5bd367fa5a3 \
+       "btrfs: send: fix sending link commands for existing file paths"
+_require_test
+_require_scratch
+_require_fssum
+
+send_files_dir=$TEST_DIR/btrfs-test-$seq
+
+rm -fr $send_files_dir
+mkdir $send_files_dir
+
+_scratch_mkfs >>$seqres.full 2>&1
+_scratch_mount
+
+# Create a file and 2000 hard links to the same inode
+_run_btrfs_util_prog subvolume create $SCRATCH_MNT/vol
+touch $SCRATCH_MNT/vol/foo
+for i in {1..2000}; do
+       link $SCRATCH_MNT/vol/foo $SCRATCH_MNT/vol/$i
+done
+
+# Create a snapshot for a full send operation
+_run_btrfs_util_prog subvolume snapshot -r $SCRATCH_MNT/vol $SCRATCH_MNT/snap1
+_run_btrfs_util_prog send -f $send_files_dir/1.snap $SCRATCH_MNT/snap1
+
+# Remove 2000 hard links and re-create the last 1000 links
+for i in {1..2000}; do
+       rm $SCRATCH_MNT/vol/$i
+done
+for i in {1001..2000}; do
+       link $SCRATCH_MNT/vol/foo $SCRATCH_MNT/vol/$i
+done
+
+# Create another snapshot for an incremental send operation
+_run_btrfs_util_prog subvolume snapshot -r $SCRATCH_MNT/vol $SCRATCH_MNT/snap2
+_run_btrfs_util_prog send -p $SCRATCH_MNT/snap1 -f $send_files_dir/2.snap \
+                    $SCRATCH_MNT/snap2
+
+$FSSUM_PROG -A -f -w $send_files_dir/1.fssum $SCRATCH_MNT/snap1
+$FSSUM_PROG -A -f -w $send_files_dir/2.fssum \
+       -x $SCRATCH_MNT/snap2/snap1 $SCRATCH_MNT/snap2
+
+# Recreate the filesystem by receiving both send streams and verify we get
+# the same content that the original filesystem had.
+_scratch_unmount
+_scratch_mkfs >>$seqres.full 2>&1
+_scratch_mount
+
+# Add the first snapshot to the new filesystem by applying the first send
+# stream.
+_run_btrfs_util_prog receive -f $send_files_dir/1.snap $SCRATCH_MNT
+
+# The incremental receive operation below used to fail with the following
+# error:
+#
+#    ERROR: link 1238 -> foo failed: File exists
+#
+# This is because the path "1238" was stored as an extended ref item in the
+# original snapshot but as a normal ref item in the next snapshot. The send
+# operation cannot handle the duplicated paths, which are stored in
+# different ways, well, so it decides to issue a link operation for the
+# existing path. This results in the receiver to fail with the above error.
+_run_btrfs_util_prog receive -f $send_files_dir/2.snap $SCRATCH_MNT
+
+$FSSUM_PROG -r $send_files_dir/1.fssum $SCRATCH_MNT/snap1
+$FSSUM_PROG -r $send_files_dir/2.fssum $SCRATCH_MNT/snap2
+
+status=0
+exit
diff --git a/tests/btrfs/272.out b/tests/btrfs/272.out
new file mode 100644 (file)
index 0000000..b009b87
--- /dev/null
@@ -0,0 +1,3 @@
+QA output created by 272
+OK
+OK
diff --git a/tests/btrfs/273 b/tests/btrfs/273
new file mode 100755 (executable)
index 0000000..dc9c213
--- /dev/null
@@ -0,0 +1,143 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Western Digital Corporation.  All Rights Reserved.
+#
+# FS QA Test No. 273
+#
+# Test that an active zone is properly reclaimed to allow the further
+# allocations, even if the active zones are mostly filled.
+#
+. ./common/preamble
+_begin_fstest auto quick snapshot zone
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+
+       # kill commands in stress_data_bgs_2
+       [ -n "$pid1" ] && kill $pid1
+       [ -n "$pid2" ] && kill $pid2
+       wait
+}
+
+# Import common functions.
+. ./common/zoned
+
+# real QA test starts here
+
+_supported_fs btrfs
+_fixed_by_kernel_commit 2ce543f47843 \
+       "btrfs: zoned: wait until zone is finished when allocation didn't progress"
+# which is further fixed by
+_fixed_by_kernel_commit d5b81ced74af \
+       "btrfs: zoned: fix API misuse of zone finish waiting"
+_require_zoned_device "$SCRATCH_DEV"
+_require_limited_active_zones "$SCRATCH_DEV"
+
+_require_command "$BLKZONE_PROG" blkzone
+_require_btrfs_command inspect-internal dump-tree
+
+# This test requires specific data space usage, skip if we have compression
+# enabled.
+_require_no_compress
+
+max_active=$(cat $(_sysfs_dev ${SCRATCH_DEV})/queue/max_active_zones)
+
+# Fill the zones leaving the last 1MB
+fill_active_zones() {
+       # Asuumes we have the same capacity between zones.
+       local capacity=$(_zone_capacity 0)
+       local fill_size=$((capacity - 1024 * 1024))
+
+       for x in $(seq ${max_active}); do
+               dd if=/dev/zero of=${SCRATCH_MNT}/fill$(printf "%02d" $x) \
+                       bs=${fill_size} count=1 oflag=direct 2>/dev/null
+               $BTRFS_UTIL_PROG filesystem sync ${SCRATCH_MNT}
+
+               local nactive=$($BLKZONE_PROG report ${SCRATCH_DEV} | grep oi | wc -l)
+               if [[ ${nactive} == ${max_active} ]]; then
+                       break
+               fi
+       done
+
+       echo "max active zones: ${max_active}" >> $seqres.full
+       $BLKZONE_PROG report ${SCRATCH_DEV} | grep oi | cat -n >> $seqres.full
+}
+
+workout() {
+       local func="$1"
+
+       _scratch_mkfs >/dev/null 2>&1
+       _scratch_mount
+
+       fill_active_zones
+       eval "$func" || _fail "${func} failed"
+
+       _scratch_unmount
+       _check_btrfs_filesystem ${SCRATCH_DEV}
+}
+
+stress_data_bgs() {
+       # This dd fails with ENOSPC, which should not :(
+       dd if=/dev/zero of=${SCRATCH_MNT}/large bs=64M count=1 oflag=sync \
+               >>$seqres.full 2>&1
+}
+
+stress_data_bgs_2() {
+       # This dd fails with ENOSPC, which should not :(
+       dd if=/dev/zero of=${SCRATCH_MNT}/large bs=64M count=10 conv=fsync \
+               >>$seqres.full 2>&1 &
+       pid1=$!
+
+       dd if=/dev/zero of=${SCRATCH_MNT}/large2 bs=64M count=10 conv=fsync \
+               >>$seqres.full 2>&1 &
+       pid2=$!
+
+       wait $pid1; local ret1=$?; unset pid1
+       wait $pid2; local ret2=$?; unset pid2
+
+       if [ $ret1 -ne 0 -o $ret2 -ne 0 ]; then
+               return 1
+       fi
+       return 0
+}
+
+get_meta_bgs() {
+       $BTRFS_UTIL_PROG inspect-internal dump-tree -t EXTENT ${SCRATCH_DEV} |
+               grep BLOCK_GROUP -A 1 |grep -B1 'METADATA|' |
+               grep -oP '\(\d+ BLOCK_GROUP_ITEM \d+\)'
+}
+
+# This test case does not return the result because
+# _run_btrfs_util_prog will call _fail() in the error case anyway.
+stress_metadata_bgs() {
+       local metabgs=$(get_meta_bgs)
+       local count=0
+
+       while : ; do
+               _run_btrfs_util_prog subvolume snapshot ${SCRATCH_MNT} ${SCRATCH_MNT}/snap$i
+               _run_btrfs_util_prog filesystem sync ${SCRATCH_MNT}
+               cur_metabgs=$(get_meta_bgs)
+               if [[ "${cur_metabgs}" != "${metabgs}" ]]; then
+                       break
+               fi
+               i=$((i + 1))
+       done
+}
+
+WORKS=(
+       stress_data_bgs
+       stress_data_bgs_2
+       stress_metadata_bgs
+)
+
+for work in "${WORKS[@]}"; do
+       workout "${work}"
+done
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/273.out b/tests/btrfs/273.out
new file mode 100644 (file)
index 0000000..7bb654d
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 273
+Silence is golden
diff --git a/tests/btrfs/274 b/tests/btrfs/274
new file mode 100755 (executable)
index 0000000..ec7d662
--- /dev/null
@@ -0,0 +1,58 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 274
+#
+# Test that we can not delete a subvolume that has an active swap file.
+#
+. ./common/preamble
+_begin_fstest auto quick swap subvol
+
+_cleanup()
+{
+       cd /
+       rm -f $tmp.*
+       test -n "$swap_file" && swapoff $swap_file &> /dev/null
+}
+
+. ./common/filter
+
+_supported_fs btrfs
+_fixed_by_kernel_commit 60021bd754c6ca \
+    "btrfs: prevent subvol with swapfile from being deleted"
+_require_scratch_swapfile
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+swap_file="$SCRATCH_MNT/subvol/swap"
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/subvol | _filter_scratch
+
+echo "Creating and activating swap file..."
+_format_swapfile $swap_file $(($(_get_page_size) * 32)) >> $seqres.full
+_swapon_file $swap_file
+
+echo "Attempting to delete subvolume with swap file enabled..."
+# Output differs with different btrfs-progs versions and some display multiple
+# lines on failure like this for example:
+#
+#   ERROR: Could not destroy subvolume/snapshot: Operation not permitted
+#   WARNING: deletion failed with EPERM, send may be in progress
+#   Delete subvolume (no-commit): '/home/fdmanana/btrfs-tests/scratch_1/subvol'
+#
+# So just redirect all output to the .full file and check the command's exit
+# status instead.
+$BTRFS_UTIL_PROG subvolume delete $SCRATCH_MNT/subvol >> $seqres.full 2>&1 && \
+    echo "subvolume deletion successful, expected failure!"
+
+echo "Disabling swap file..."
+swapoff $swap_file
+
+echo "Attempting to delete subvolume after disabling swap file..."
+$BTRFS_UTIL_PROG subvolume delete $SCRATCH_MNT/subvol >> $seqres.full 2>&1 || \
+   echo "subvolume deletion failure, expected success!"
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/274.out b/tests/btrfs/274.out
new file mode 100644 (file)
index 0000000..66e0de2
--- /dev/null
@@ -0,0 +1,6 @@
+QA output created by 274
+Create subvolume 'SCRATCH_MNT/subvol'
+Creating and activating swap file...
+Attempting to delete subvolume with swap file enabled...
+Disabling swap file...
+Attempting to delete subvolume after disabling swap file...
diff --git a/tests/btrfs/275 b/tests/btrfs/275
new file mode 100755 (executable)
index 0000000..b34fbda
--- /dev/null
@@ -0,0 +1,88 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test No. 275
+#
+# Test that no xattr can be changed once btrfs property is set to RO.
+#
+. ./common/preamble
+_begin_fstest auto quick attr
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+_supported_fs btrfs
+_fixed_by_kernel_commit b51111271b03 \
+       "btrfs: check if root is readonly while setting security xattr"
+_require_attrs
+_require_btrfs_command "property"
+_require_scratch
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+FILENAME=$SCRATCH_MNT/foo
+
+set_xattr()
+{
+       local value=$1
+       $SETFATTR_PROG -n "user.one" -v $value $FILENAME 2>&1 | _filter_scratch
+       $SETFATTR_PROG -n "security.one" -v $value $FILENAME 2>&1 | _filter_scratch
+       $SETFATTR_PROG -n "trusted.one" -v $value $FILENAME 2>&1 | _filter_scratch
+}
+
+get_xattr()
+{
+       _getfattr --absolute-names -n "user.one" $FILENAME 2>&1 | _filter_scratch
+       _getfattr --absolute-names -n "security.one" $FILENAME 2>&1 | _filter_scratch
+       _getfattr --absolute-names -n "trusted.one" $FILENAME 2>&1 | _filter_scratch
+}
+
+del_xattr()
+{
+       $SETFATTR_PROG -x "user.one" $FILENAME 2>&1 | _filter_scratch
+       $SETFATTR_PROG -x "security.one" $FILENAME 2>&1 | _filter_scratch
+       $SETFATTR_PROG -x "trusted.one" $FILENAME 2>&1 | _filter_scratch
+}
+
+# Create a test file.
+echo "hello world" > $FILENAME
+
+set_xattr 1
+
+$BTRFS_UTIL_PROG property set $SCRATCH_MNT ro true
+$BTRFS_UTIL_PROG property get $SCRATCH_MNT ro
+
+# Attempt to change values of RO (property) filesystem.
+set_xattr 2
+
+# Check the values of RO (property) filesystem are not changed.
+get_xattr
+
+# Attempt to remove xattr from RO (property) filesystem.
+del_xattr
+
+# Check if xattr still exist.
+get_xattr
+
+# Change filesystem property RO to false
+$BTRFS_UTIL_PROG property set $SCRATCH_MNT ro false
+$BTRFS_UTIL_PROG property get $SCRATCH_MNT ro
+
+# Change the xattrs after RO is false.
+set_xattr 2
+
+# Get the changed values.
+get_xattr
+
+# Remove xattr.
+del_xattr
+
+# check if the xattrs are really deleted.
+get_xattr
+
+status=0
+exit
diff --git a/tests/btrfs/275.out b/tests/btrfs/275.out
new file mode 100644 (file)
index 0000000..fb8f02f
--- /dev/null
@@ -0,0 +1,39 @@
+QA output created by 275
+ro=true
+setfattr: SCRATCH_MNT/foo: Read-only file system
+setfattr: SCRATCH_MNT/foo: Read-only file system
+setfattr: SCRATCH_MNT/foo: Read-only file system
+# file: SCRATCH_MNT/foo
+user.one="1"
+
+# file: SCRATCH_MNT/foo
+security.one="1"
+
+# file: SCRATCH_MNT/foo
+trusted.one="1"
+
+setfattr: SCRATCH_MNT/foo: Read-only file system
+setfattr: SCRATCH_MNT/foo: Read-only file system
+setfattr: SCRATCH_MNT/foo: Read-only file system
+# file: SCRATCH_MNT/foo
+user.one="1"
+
+# file: SCRATCH_MNT/foo
+security.one="1"
+
+# file: SCRATCH_MNT/foo
+trusted.one="1"
+
+ro=false
+# file: SCRATCH_MNT/foo
+user.one="2"
+
+# file: SCRATCH_MNT/foo
+security.one="2"
+
+# file: SCRATCH_MNT/foo
+trusted.one="2"
+
+SCRATCH_MNT/foo: user.one: No such attribute
+SCRATCH_MNT/foo: security.one: No such attribute
+SCRATCH_MNT/foo: trusted.one: No such attribute
diff --git a/tests/btrfs/276 b/tests/btrfs/276
new file mode 100755 (executable)
index 0000000..f15f208
--- /dev/null
@@ -0,0 +1,149 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 276
+#
+# Verify that fiemap correctly reports the sharedness of extents for a file with
+# a very large number of extents, spanning many b+tree leaves in the fs tree,
+# and when the file's subvolume was snapshoted.
+#
+. ./common/preamble
+_begin_fstest auto snapshot fiemap remount
+
+. ./common/filter
+. ./common/filter.btrfs
+. ./common/attr
+
+_supported_fs btrfs
+_require_scratch
+_require_xfs_io_command "fiemap" "ranged"
+_require_attrs
+_require_odirect
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+fiemap_test_file()
+{
+       local offset=$1
+       local len=$2
+
+       # Skip the first two lines of xfs_io's fiemap output (file path and
+       # header describing the output columns) as well as holes.
+       $XFS_IO_PROG -c "fiemap -v $offset $len" $SCRATCH_MNT/foo | \
+               grep -v 'hole' | tail -n +3
+}
+
+# Count the number of shared extents for the whole test file or just for a given
+# range.
+count_shared_extents()
+{
+       local offset=$1
+       local len=$2
+
+       # Column 5 (from xfs_io's "fiemap -v" command) is the flags (hex field).
+       # 0x2000 is the value for the FIEMAP_EXTENT_SHARED flag.
+       fiemap_test_file $offset $len | \
+               $AWK_PROG --source 'BEGIN { cnt = 0 }' \
+                         --source '{ if (and(strtonum($5), 0x2000)) cnt++ }' \
+                         --source 'END { print cnt }'
+}
+
+# Count the number of non shared extents for the whole test file or just for a
+# given range.
+count_not_shared_extents()
+{
+       local offset=$1
+       local len=$2
+
+       # Column 5 (from xfs_io's "fiemap -v" command) is the flags (hex field).
+       # 0x2000 is the value for the FIEMAP_EXTENT_SHARED flag.
+       fiemap_test_file $offset $len | \
+               $AWK_PROG --source 'BEGIN { cnt = 0 }' \
+                         --source '{ if (!and(strtonum($5), 0x2000)) cnt++ }' \
+                         --source 'END { print cnt }'
+}
+
+# Create a file with 2000 extents, and a fs tree with a height of at least 3
+# (root node at level 2). We want to verify later that fiemap correctly reports
+# the sharedness of each extent, even when it needs to switch from one leaf to
+# the next one and from a node at level 1 to the next node at level 1.
+# To speedup creating a fs tree of height >= 3, add several large xattrs.
+ext_size=$(( 64 * 1024 ))
+file_size=$(( 2000 * 2 * $ext_size )) # about 250M
+nr_cpus=$("$here/src/feature" -o)
+workers=0
+for (( i = 0; i < $file_size; i += 2 * $ext_size )); do
+       $XFS_IO_PROG -f -d -c "pwrite -b $ext_size $i $ext_size" \
+               $SCRATCH_MNT/foo > /dev/null &
+       workers=$(( workers + 1 ))
+       if [ "$workers" -ge "$nr_cpus" ]; then
+               workers=0
+               wait
+       fi
+done
+wait
+
+workers=0
+xattr_value=$(printf '%0.sX' $(seq 1 3900))
+for (( i = 1; i <= 29000; i++ )); do
+       echo -n > $SCRATCH_MNT/filler_$i
+       $SETFATTR_PROG -n 'user.x1' -v $xattr_value $SCRATCH_MNT/filler_$i &
+       workers=$(( workers + 1 ))
+       if [ "$workers" -ge "$nr_cpus" ]; then
+               workers=0
+               wait
+       fi
+done
+wait
+
+# Make sure every ordered extent completed and therefore updated the fs tree.
+sync
+
+# All extents should be reported as non shared (2000 extents).
+echo "Number of non-shared extents in the whole file: $(count_not_shared_extents)"
+
+# Creating a snapshot.
+$BTRFS_UTIL_PROG subvolume snapshot $SCRATCH_MNT $SCRATCH_MNT/snap | _filter_scratch
+
+# We have a snapshot, so now all extents should be reported as shared.
+echo "Number of shared extents in the whole file: $(count_shared_extents)"
+
+# Now COW two file ranges, of 64K each, in the snapshot's file.
+# So 2 extents should become non-shared after this. Each file extent item is in
+# different leaf of the snapshot tree.
+#
+$XFS_IO_PROG -d -c "pwrite -b $ext_size 512K $ext_size" \
+            -d -c "pwrite -b $ext_size 249M $ext_size" \
+            $SCRATCH_MNT/snap/foo | _filter_xfs_io
+
+# Wait for ordered extents to complete and commit current transaction to make
+# sure fiemap will see all extents in the subvolume and extent trees.
+sync
+
+# Now we should have 2 non-shared extents and 1998 shared extents.
+echo "Number of non-shared extents in the whole file: $(count_not_shared_extents)"
+echo "Number of shared extents in the whole file: $(count_shared_extents)"
+
+# Check that the non-shared extents are indeed in the expected file ranges.
+echo "Number of non-shared extents in range [512K, 512K + 64K): $(count_not_shared_extents 512K 64K)"
+echo "Number of non-shared extents in range [249M, 249M + 64K): $(count_not_shared_extents 249M 64K)"
+
+# Now delete the snapshot.
+$BTRFS_UTIL_PROG subvolume delete -c $SCRATCH_MNT/snap | _filter_btrfs_subvol_delete
+
+# We deleted the snapshot and committed the transaction used to delete it (-c),
+# but all its extents (both metadata and data) are actually only deleted in the
+# background, by the cleaner kthread. So remount, which wakes up the cleaner
+# kthread, with a commit interval of 1 second and sleep for 1.1 seconds - after
+# this we are guaranteed all extents of the snapshot were deleted.
+_scratch_remount commit=1
+sleep 1.1
+
+# Now all extents should be reported as not shared (2000 extents).
+echo "Number of non-shared extents in the whole file: $(count_not_shared_extents)"
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/276.out b/tests/btrfs/276.out
new file mode 100644 (file)
index 0000000..352e06b
--- /dev/null
@@ -0,0 +1,14 @@
+QA output created by 276
+Number of non-shared extents in the whole file: 2000
+Create a snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap'
+Number of shared extents in the whole file: 2000
+wrote 65536/65536 bytes at offset 524288
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 261095424
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Number of non-shared extents in the whole file: 2
+Number of shared extents in the whole file: 1998
+Number of non-shared extents in range [512K, 512K + 64K): 1
+Number of non-shared extents in range [249M, 249M + 64K): 1
+Delete subvolume 'SCRATCH_MNT/snap'
+Number of non-shared extents in the whole file: 2000
diff --git a/tests/btrfs/277 b/tests/btrfs/277
new file mode 100755 (executable)
index 0000000..5bb7ffa
--- /dev/null
@@ -0,0 +1,116 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Meta, Inc.  All Rights Reserved.
+#
+# FS QA Test 277
+#
+# Test sendstreams involving fs-verity enabled files.
+#
+. ./common/preamble
+_begin_fstest auto quick verity send
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       _restore_fsverity_signatures
+       rm -r -f $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/verity
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_scratch_verity
+_require_fsverity_builtin_signatures
+_require_command "$SETCAP_PROG" setcap
+_require_command "$GETCAP_PROG" getcap
+_require_btrfs_send_version 3
+
+subv=$SCRATCH_MNT/subv
+fsv_file=$subv/file.fsv
+keyfile=$tmp.key.pem
+certfile=$tmp.cert.pem
+certfileder=$tmp.cert.der
+sigfile=$tmp.sig
+stream=$tmp.fsv.ss
+
+_test_send_verity() {
+       local sig=$1
+       local salt=$2
+       local extra_args=""
+
+       _scratch_mkfs >> $seqres.full
+       _scratch_mount
+       echo -e "\nverity send/recv test: sig: $sig salt: $salt"
+       _disable_fsverity_signatures
+
+       echo "create subvolume"
+       $BTRFS_UTIL_PROG subvolume create $subv >> $seqres.full
+       echo "create file"
+       $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $fsv_file
+       if $salt; then
+               extra_args+=" --salt=deadbeef"
+       fi
+       if $sig; then
+               echo "generate keys and cert"
+               _fsv_generate_cert $keyfile $certfile $certfileder
+               echo "clear keyring"
+               _fsv_clear_keyring
+               echo "load cert into keyring"
+               _fsv_load_cert $certfileder
+               echo "require signatures"
+               _enable_fsverity_signatures
+               echo "sign file digest"
+               _fsv_sign $fsv_file $sigfile --key=$keyfile --cert=$certfile \
+                       $extra_args | _filter_scratch >> $seqres.full
+               extra_args+=" --signature=$sigfile"
+       fi
+       echo "enable verity"
+       _fsv_enable $fsv_file $extra_args
+       cat $fsv_file > $tmp.file-before
+       _fsv_measure $fsv_file > $tmp.digest-before
+
+       # ensure send plays nice with other properties that are set when
+       # finishing the file during send, like chmod and capabilities.
+       echo "modify other properties"
+       chmod a+x $fsv_file
+       $SETCAP_PROG "cap_sys_ptrace+ep cap_sys_nice+ep" $fsv_file
+       $GETCAP_PROG $fsv_file > $tmp.cap-before
+
+       echo "set subvolume read only"
+       $BTRFS_UTIL_PROG property set $subv ro true
+       echo "send subvolume"
+       $BTRFS_UTIL_PROG send $subv -f $stream -q --proto=3 >> $seqres.full
+
+       echo "blow away fs"
+       _scratch_unmount
+       _scratch_mkfs >> $seqres.full
+       _scratch_mount
+
+       echo "receive sendstream"
+       $BTRFS_UTIL_PROG receive $SCRATCH_MNT -f $stream -q >> $seqres.full
+
+       echo "check received subvolume..."
+       _scratch_cycle_mount
+       _fsv_measure $fsv_file > $tmp.digest-after
+       $GETCAP_PROG $fsv_file > $tmp.cap-after
+       diff $tmp.file-before $fsv_file
+       diff $tmp.digest-before $tmp.digest-after
+       diff $tmp.cap-before $tmp.cap-after
+       _scratch_unmount
+       echo OK
+}
+
+_test_send_verity false false # no sig; no salt
+_test_send_verity false true # no sig; salt
+_test_send_verity true false # sig; no salt
+_test_send_verity true true # sig; salt
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/277.out b/tests/btrfs/277.out
new file mode 100644 (file)
index 0000000..5f778cf
--- /dev/null
@@ -0,0 +1,59 @@
+QA output created by 277
+
+verity send/recv test: sig: false salt: false
+create subvolume
+create file
+enable verity
+modify other properties
+set subvolume read only
+send subvolume
+blow away fs
+receive sendstream
+check received subvolume...
+OK
+
+verity send/recv test: sig: false salt: true
+create subvolume
+create file
+enable verity
+modify other properties
+set subvolume read only
+send subvolume
+blow away fs
+receive sendstream
+check received subvolume...
+OK
+
+verity send/recv test: sig: true salt: false
+create subvolume
+create file
+generate keys and cert
+clear keyring
+load cert into keyring
+require signatures
+sign file digest
+enable verity
+modify other properties
+set subvolume read only
+send subvolume
+blow away fs
+receive sendstream
+check received subvolume...
+OK
+
+verity send/recv test: sig: true salt: true
+create subvolume
+create file
+generate keys and cert
+clear keyring
+load cert into keyring
+require signatures
+sign file digest
+enable verity
+modify other properties
+set subvolume read only
+send subvolume
+blow away fs
+receive sendstream
+check received subvolume...
+OK
diff --git a/tests/btrfs/278 b/tests/btrfs/278
new file mode 100755 (executable)
index 0000000..ff59ebd
--- /dev/null
@@ -0,0 +1,218 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 BingJing Chang.
+#
+# FS QA Test No. btrfs/278
+#
+# Regression test for btrfs incremental send issue when processing inodes
+# with no links
+#
+# This issue is fixed by the following linux kernel btrfs patch:
+#
+#   commit 9ed0a72e5b355d ("btrfs: send: fix failures when processing
+#   inodes with no links")
+#
+. ./common/preamble
+_begin_fstest auto quick send
+
+# real QA test starts here
+_supported_fs btrfs
+_fixed_by_kernel_commit 9ed0a72e5b355d \
+       "btrfs: send: fix failures when processing inodes with no links"
+_require_test
+_require_scratch
+_require_btrfs_command "property"
+_require_fssum
+
+send_files_dir=$TEST_DIR/btrfs-test-$seq
+
+rm -fr $send_files_dir
+mkdir $send_files_dir
+
+_scratch_mkfs >>$seqres.full 2>&1
+_scratch_mount
+_run_btrfs_util_prog subvolume create $SCRATCH_MNT/vol
+
+# Creating the first snapshot looks like:
+#
+# .                                                                  (ino 256)
+# |--- deleted.file                                                  (ino 257)
+# |--- deleted.dir/                                                  (ino 258)
+# |--- changed_subcase1.file                                         (ino 259)
+# |--- changed_subcase2.file                                         (ino 260)
+# |--- changed_subcase1.dir/                                         (ino 261)
+# |    |---- foo                                                     (ino 262)
+# |--- changed_subcase2.dir/                                         (ino 263)
+# |    |---- foo                                                     (ino 264)
+#
+touch $SCRATCH_MNT/vol/deleted.file
+mkdir $SCRATCH_MNT/vol/deleted.dir
+touch $SCRATCH_MNT/vol/changed_subcase1.file
+touch $SCRATCH_MNT/vol/changed_subcase2.file
+mkdir $SCRATCH_MNT/vol/changed_subcase1.dir
+touch $SCRATCH_MNT/vol/changed_subcase1.dir/foo
+mkdir $SCRATCH_MNT/vol/changed_subcase2.dir
+touch $SCRATCH_MNT/vol/changed_subcase2.dir/foo
+_run_btrfs_util_prog subvolume snapshot -r $SCRATCH_MNT/vol $SCRATCH_MNT/snap1
+
+# Delete the deleted.*, create a new file and a new directory, and then
+# take the second snapshot looks like:
+#
+# .                                                                  (ino 256)
+# |--- changed_subcase1.file                                         (ino 259)
+# |--- changed_subcase2.file                                         (ino 260)
+# |--- changed_subcase1.dir/                                         (ino 261)
+# |    |---- foo                                                     (ino 262)
+# |--- changed_subcase2.dir/                                         (ino 263)
+# |    |---- foo                                                     (ino 264)
+# |--- new.file                                                      (ino 265)
+# |--- new.dir/                                                      (ino 266)
+#
+unlink $SCRATCH_MNT/vol/deleted.file
+rmdir $SCRATCH_MNT/vol/deleted.dir
+touch $SCRATCH_MNT/vol/new.file
+mkdir $SCRATCH_MNT/vol/new.dir
+_run_btrfs_util_prog subvolume snapshot -r $SCRATCH_MNT/vol $SCRATCH_MNT/snap2
+
+# Set the snapshot "snap1" to read-write mode and turn several inodes to
+# orphans, so that the snapshot will look like this:
+#
+# .                                                                  (ino 256)
+# |--- (orphan) deleted.file                                         (ino 257)
+# |--- (orphan) deleted.dir/                                         (ino 258)
+# |--- (orphan) changed_subcase1.file                                (ino 259)
+# |--- changed_subcase2.file                                         (ino 260)
+# |--- (orphan) changed_subcase1.dir/                                (ino 261)
+# |--- changed_subcase2.dir/                                         (ino 263)
+# |    |---- foo                                                     (ino 264)
+#
+# Note: To make an easy illustration, I just put a tag "(orphan)" in front of
+# their original names to indicate that they're deleted, but their inodes can
+# not be removed because of open file descriptors on them. Mention that orphan
+# inodes don't have names(paths).
+#
+$BTRFS_UTIL_PROG property set $SCRATCH_MNT/snap1 ro false
+exec 71<$SCRATCH_MNT/snap1/deleted.file
+exec 72<$SCRATCH_MNT/snap1/deleted.dir
+exec 73<$SCRATCH_MNT/snap1/changed_subcase1.file
+exec 74<$SCRATCH_MNT/snap1/changed_subcase1.dir
+unlink $SCRATCH_MNT/snap1/deleted.file
+rmdir $SCRATCH_MNT/snap1/deleted.dir
+unlink $SCRATCH_MNT/snap1/changed_subcase1.file
+unlink $SCRATCH_MNT/snap1/changed_subcase1.dir/foo
+rmdir $SCRATCH_MNT/snap1/changed_subcase1.dir
+
+# Turn the snapshot "snap1" back to read-only mode.
+$BTRFS_UTIL_PROG property set $SCRATCH_MNT/snap1 ro true
+
+# Set the snapshot "snap2" to read-write mode and turn several inodes to
+# orphans, so that the snapshot will look like this:
+#
+# .                                                                  (ino 256)
+# |--- (orphan) changed_subcase1.file                                (ino 259)
+# |--- (orphan) changed_subcase2.file                                (ino 260)
+# |--- (orphan) changed_subcase1.dir/                                (ino 261)
+# |--- (orphan) changed_subcase2.dir/                                (ino 263)
+# |--- (orphan) new.file                                             (ino 265)
+# |--- (orphan) new.dir/                                             (ino 266)
+#
+# Note: Same notice as above. Mention that orphan inodes don't have
+# names(paths).
+#
+$BTRFS_UTIL_PROG property set $SCRATCH_MNT/snap2 ro false
+exec 81<$SCRATCH_MNT/snap2/changed_subcase1.file
+exec 82<$SCRATCH_MNT/snap2/changed_subcase1.dir
+exec 83<$SCRATCH_MNT/snap2/changed_subcase2.file
+exec 84<$SCRATCH_MNT/snap2/changed_subcase2.dir
+exec 85<$SCRATCH_MNT/snap2/new.file
+exec 86<$SCRATCH_MNT/snap2/new.dir
+unlink $SCRATCH_MNT/snap2/changed_subcase1.file
+unlink $SCRATCH_MNT/snap2/changed_subcase1.dir/foo
+rmdir $SCRATCH_MNT/snap2/changed_subcase1.dir
+unlink $SCRATCH_MNT/snap2/changed_subcase2.file
+unlink $SCRATCH_MNT/snap2/changed_subcase2.dir/foo
+rmdir $SCRATCH_MNT/snap2/changed_subcase2.dir
+unlink $SCRATCH_MNT/snap2/new.file
+rmdir $SCRATCH_MNT/snap2/new.dir
+
+# Turn the snapshot "snap2" back to read-only mode.
+$BTRFS_UTIL_PROG property set $SCRATCH_MNT/snap2 ro true
+
+# Test that a full send operation can handle orphans with no paths
+_run_btrfs_util_prog send -f $send_files_dir/1.snap $SCRATCH_MNT/snap1
+
+# Test that an incremental send operation can handle orphans.
+#
+# Here're descriptions for the details:
+#
+# Case 1: new.file and new.dir (BTRFS_COMPARE_TREE_NEW)
+#        |  send snapshot  | action
+#  --------------------------------
+#  nlink |        0        | ignore
+#
+# They are new inodes in the send snapshot ("snap2"), but they don't have
+# paths because they have no links. Test that the send operation can ignore
+# them in order not to generate the creation commands for them. Or it will
+# fail, with -ENOENT, when trying to generate paths for them.
+#
+#
+# Case 2: deleted.file and deleted.dir (BTRFS_COMPARE_TREE_DELETED)
+#       | parent snapshot | action
+# ----------------------------------
+# nlink |        0        | as usual
+#
+# They're deleted in the parent snapshot ("snap1") but become orphans which
+# have no paths. Test that no deletion commands will be generated as usual.
+# This case didn't fail before.
+#
+#
+# Case 3: changed_*.file and changed_*.dir (BTRFS_COMPARE_TREE_CHANGED)
+#           |       | parent snapshot | send snapshot | action
+# -----------------------------------------------------------------------
+# subcase 1 | nlink |        0        |       0       | ignore
+# subcase 2 | nlink |       >0        |       0       | new_gen(deletion)
+#
+# In subcase 1, test that the send operation can ignore them without trying
+# to generate any commands.
+#
+# In subcase 2, test that the send operation can generate an unlink command
+# for that file and test that it can generate a rename command for the
+# non-empty directory first and a rmdir command to remove it finally. Or
+# the receive operation will fail with a wrong unlink on a non-empty
+# directory.
+#
+_run_btrfs_util_prog send -p $SCRATCH_MNT/snap1 -f $send_files_dir/2.snap \
+                    $SCRATCH_MNT/snap2
+
+$FSSUM_PROG -A -f -w $send_files_dir/1.fssum $SCRATCH_MNT/snap1
+$FSSUM_PROG -A -f -w $send_files_dir/2.fssum \
+       -x $SCRATCH_MNT/snap2/snap1 $SCRATCH_MNT/snap2
+
+# Recreate the filesystem by receiving both send streams and verify we get
+# the same content that the original filesystem had.
+exec 71>&-
+exec 72>&-
+exec 73>&-
+exec 74>&-
+exec 81>&-
+exec 82>&-
+exec 83>&-
+exec 84>&-
+exec 85>&-
+exec 86>&-
+_scratch_unmount
+_scratch_mkfs >>$seqres.full 2>&1
+_scratch_mount
+
+# Add the first snapshot to the new filesystem by applying the first send
+# stream.
+_run_btrfs_util_prog receive -f $send_files_dir/1.snap $SCRATCH_MNT
+
+# Test the incremental send stream
+_run_btrfs_util_prog receive -f $send_files_dir/2.snap $SCRATCH_MNT
+
+$FSSUM_PROG -r $send_files_dir/1.fssum $SCRATCH_MNT/snap1
+$FSSUM_PROG -r $send_files_dir/2.fssum $SCRATCH_MNT/snap2
+
+status=0
+exit
diff --git a/tests/btrfs/278.out b/tests/btrfs/278.out
new file mode 100644 (file)
index 0000000..82b93b4
--- /dev/null
@@ -0,0 +1,3 @@
+QA output created by 278
+OK
+OK
diff --git a/tests/btrfs/279 b/tests/btrfs/279
new file mode 100755 (executable)
index 0000000..5b5824f
--- /dev/null
@@ -0,0 +1,82 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 279
+#
+# Test that if we have two files with shared extents, after removing one of the
+# files, if we do a fiemap against the other file, it does not report extents as
+# shared anymore.
+#
+# This exercises the processing of delayed references for data extents in the
+# backref walking code, used by fiemap to determine if an extent is shared.
+#
+. ./common/preamble
+_begin_fstest auto quick subvol fiemap clone
+
+. ./common/filter
+. ./common/reflink
+. ./common/punch # for _filter_fiemap_flags
+
+_supported_fs btrfs
+_require_scratch_reflink
+_require_cp_reflink
+_require_xfs_io_command "fiemap"
+
+_fixed_by_kernel_commit 4fc7b5722824 \
+       "btrfs: fix processing of delayed data refs during backref walking"
+
+run_test()
+{
+       local file_path_1=$1
+       local file_path_2=$2
+       local do_sync=$3
+
+       $XFS_IO_PROG -f -c "pwrite 0 64K" $file_path_1 | _filter_xfs_io
+       _cp_reflink $file_path_1 $file_path_2
+
+       if [ $do_sync -eq 1 ]; then
+               sync
+       fi
+
+       echo "Fiemap of $file_path_1 before deleting $file_path_2:" | \
+               _filter_scratch
+       $XFS_IO_PROG -c "fiemap -v" $file_path_1 | _filter_fiemap_flags
+
+       rm -f $file_path_2
+
+       echo "Fiemap of $file_path_1 after deleting $file_path_2:" | \
+               _filter_scratch
+       $XFS_IO_PROG -c "fiemap -v" $file_path_1 | _filter_fiemap_flags
+}
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+# Create two test subvolumes, we'll reflink files between them.
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/subv1 | _filter_scratch
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/subv2 | _filter_scratch
+
+echo
+echo "Testing with same subvolume and without transaction commit"
+echo
+run_test "$SCRATCH_MNT/subv1/f1" "$SCRATCH_MNT/subv1/f2" 0
+
+echo
+echo "Testing with same subvolume and with transaction commit"
+echo
+run_test "$SCRATCH_MNT/subv1/f3" "$SCRATCH_MNT/subv1/f4" 1
+
+echo
+echo "Testing with different subvolumes and without transaction commit"
+echo
+run_test "$SCRATCH_MNT/subv1/f5" "$SCRATCH_MNT/subv2/f6" 0
+
+echo
+echo "Testing with different subvolumes and with transaction commit"
+echo
+run_test "$SCRATCH_MNT/subv1/f7" "$SCRATCH_MNT/subv2/f8" 1
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/279.out b/tests/btrfs/279.out
new file mode 100644 (file)
index 0000000..a959a86
--- /dev/null
@@ -0,0 +1,39 @@
+QA output created by 279
+Create subvolume 'SCRATCH_MNT/subv1'
+Create subvolume 'SCRATCH_MNT/subv2'
+
+Testing with same subvolume and without transaction commit
+
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Fiemap of SCRATCH_MNT/subv1/f1 before deleting SCRATCH_MNT/subv1/f2:
+0: [0..127]: shared|last
+Fiemap of SCRATCH_MNT/subv1/f1 after deleting SCRATCH_MNT/subv1/f2:
+0: [0..127]: last
+
+Testing with same subvolume and with transaction commit
+
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Fiemap of SCRATCH_MNT/subv1/f3 before deleting SCRATCH_MNT/subv1/f4:
+0: [0..127]: shared|last
+Fiemap of SCRATCH_MNT/subv1/f3 after deleting SCRATCH_MNT/subv1/f4:
+0: [0..127]: last
+
+Testing with different subvolumes and without transaction commit
+
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Fiemap of SCRATCH_MNT/subv1/f5 before deleting SCRATCH_MNT/subv2/f6:
+0: [0..127]: shared|last
+Fiemap of SCRATCH_MNT/subv1/f5 after deleting SCRATCH_MNT/subv2/f6:
+0: [0..127]: last
+
+Testing with different subvolumes and with transaction commit
+
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Fiemap of SCRATCH_MNT/subv1/f7 before deleting SCRATCH_MNT/subv2/f8:
+0: [0..127]: shared|last
+Fiemap of SCRATCH_MNT/subv1/f7 after deleting SCRATCH_MNT/subv2/f8:
+0: [0..127]: last
diff --git a/tests/btrfs/280 b/tests/btrfs/280
new file mode 100755 (executable)
index 0000000..fc049ad
--- /dev/null
@@ -0,0 +1,64 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 280
+#
+# Test that if we have a large file, with file extent items spanning several
+# leaves in the fs tree, and that is shared due to a snapshot, if we COW one of
+# the extents, doing a fiemap will report the respective file range as not
+# shared.
+#
+# This exercises the processing of delayed references for metadata extents in
+# the backref walking code, used by fiemap to determine if an extent is shared.
+#
+. ./common/preamble
+_begin_fstest auto quick compress snapshot fiemap
+
+. ./common/filter
+. ./common/punch # for _filter_fiemap_flags
+
+_supported_fs btrfs
+_require_scratch
+_require_xfs_io_command "fiemap"
+
+_fixed_by_kernel_commit 943553ef9b51 \
+       "btrfs: fix processing of delayed tree block refs during backref walking"
+
+_scratch_mkfs >> $seqres.full 2>&1
+# We use compression because it's a very quick way to create a file with a very
+# large number of extents (compression limits the maximum extent size to 128K)
+# and while using very little disk space.
+_scratch_mount -o compress
+
+# A 128M file full of compressed extents results in a fs tree with a heigth
+# of 2 (root at level 1), so we'll end up with tree block references in the
+# extent tree (if the root was a leaf, we would have only data references).
+$XFS_IO_PROG -f -c "pwrite -b 1M 0 128M" $SCRATCH_MNT/foo | _filter_xfs_io
+
+# Create a RW snapshot of the default subvolume.
+$BTRFS_UTIL_PROG subvolume snapshot $SCRATCH_MNT $SCRATCH_MNT/snap | _filter_scratch
+
+echo
+echo "File foo fiemap before COWing extent:"
+echo
+$XFS_IO_PROG -c "fiemap -v" $SCRATCH_MNT/foo | _filter_fiemap_flags 1
+
+echo
+echo "Overwriting file range [120M, 120M + 128K) in the snapshot"
+echo
+$XFS_IO_PROG -c "pwrite -b 128K 120M 128K" $SCRATCH_MNT/snap/foo | _filter_xfs_io
+# Now fsync the file to force COWing the extent and the path from the root of
+# the snapshot tree down to the leaf where the extent is at.
+$XFS_IO_PROG -c "fsync" $SCRATCH_MNT/snap/foo
+
+echo
+echo "File foo fiemap after COWing extent in the snapshot:"
+echo
+# Now we should have all extents marked as shared except the 128K extent in the
+# file range [120M, 120M + 128K).
+$XFS_IO_PROG -c "fiemap -v" $SCRATCH_MNT/foo | _filter_fiemap_flags 1
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/280.out b/tests/btrfs/280.out
new file mode 100644 (file)
index 0000000..5371f3b
--- /dev/null
@@ -0,0 +1,21 @@
+QA output created by 280
+wrote 134217728/134217728 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Create a snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap'
+
+File foo fiemap before COWing extent:
+
+0: [0..261887]: shared|encoded
+1: [261888..262143]: shared|encoded|last
+
+Overwriting file range [120M, 120M + 128K) in the snapshot
+
+wrote 131072/131072 bytes at offset 125829120
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+File foo fiemap after COWing extent in the snapshot:
+
+0: [0..245759]: shared|encoded
+1: [245760..246015]: encoded
+2: [246016..261887]: shared|encoded
+3: [261888..262143]: shared|encoded|last
diff --git a/tests/btrfs/281 b/tests/btrfs/281
new file mode 100755 (executable)
index 0000000..ddc7d9e
--- /dev/null
@@ -0,0 +1,90 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 281
+#
+# Test that if we have a snapshot with a compressed extent that is partially
+# shared between two files, one of them has a size that is not sector size
+# aligned, we create a v2 send stream for the snapshot with compressed data,
+# and then apply that stream to another filesystem, the operation succeeds and
+# no data is missing. Also check that the file that had a reference to the whole
+# extent gets two compressed extents in the new filesystem, with only one of
+# them being shared (reflinked).
+#
+. ./common/preamble
+_begin_fstest auto quick send compress clone fiemap
+
+. ./common/filter
+. ./common/reflink
+. ./common/punch # for _filter_fiemap_flags
+
+_supported_fs btrfs
+_require_test
+_require_scratch_reflink
+_require_btrfs_send_version 2
+_require_xfs_io_command "fiemap"
+_require_fssum
+_require_btrfs_no_nodatacow
+
+_fixed_by_kernel_commit a11452a3709e \
+       "btrfs: send: avoid unaligned encoded writes when attempting to clone range"
+
+send_files_dir=$TEST_DIR/btrfs-test-$seq
+send_stream=$send_files_dir/snap.stream
+snap_fssum=$send_files_dir/snap.fssum
+
+rm -fr $send_files_dir
+mkdir $send_files_dir
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount -o compress
+
+# File foo has a size of 65K, which is not sector size aligned for any
+# supported sector size on btrfs.
+$XFS_IO_PROG -f -c "pwrite -S 0xab 0 65K" $SCRATCH_MNT/foo | _filter_xfs_io
+
+# File bar has a compressed extent (and its size is sector size aligned).
+$XFS_IO_PROG -f -c "pwrite -S 0xcd 0 128K" $SCRATCH_MNT/bar | _filter_xfs_io
+
+# Now clone only half of bar's extent into foo.
+$XFS_IO_PROG -c "reflink $SCRATCH_MNT/bar 0 0 64K" $SCRATCH_MNT/foo \
+       | _filter_xfs_io
+
+echo "Creating snapshot and a send stream for it..."
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT $SCRATCH_MNT/snap \
+       | _filter_scratch
+$BTRFS_UTIL_PROG send --compressed-data -f $send_stream $SCRATCH_MNT/snap 2>&1 \
+       | _filter_scratch
+
+$FSSUM_PROG -A -f -w $snap_fssum $SCRATCH_MNT/snap
+
+echo "Creating a new filesystem to receive the send stream..."
+_scratch_unmount
+_scratch_mkfs >> $seqres.full 2>&1
+# Mount without compression, we created the stream with data compression enabled
+# so we want to verify that applying the stream preserves the compression.
+_scratch_mount
+
+$BTRFS_UTIL_PROG receive -f $send_stream $SCRATCH_MNT
+
+echo "Verifying data matches the original filesystem..."
+$FSSUM_PROG -r $snap_fssum $SCRATCH_MNT/snap
+
+# Now check that fiemap reports two extents for file bar:
+#
+# 1) The first extent should be encoded, because compression was enabled in the
+#    original filesystem, and should also be flagged as shared, since that file
+#    range was reflinked with file foo in the original filesystem;
+#
+# 2) The second extent should also be encoded (compression was enabled in the
+#    original filesystem), but not shared since that file range was not
+#    reflinked in the original filesystem. It should also have the "last" flag
+#    set, as it's the last extent in the file.
+#
+echo "File bar fiemap output in the new filesystem:"
+$XFS_IO_PROG -r -c "fiemap -v" $SCRATCH_MNT/snap/bar | _filter_fiemap_flags 1
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/281.out b/tests/btrfs/281.out
new file mode 100644 (file)
index 0000000..2585e3e
--- /dev/null
@@ -0,0 +1,17 @@
+QA output created by 281
+wrote 66560/66560 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+linked 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Creating snapshot and a send stream for it...
+Create a readonly snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap'
+At subvol SCRATCH_MNT/snap
+Creating a new filesystem to receive the send stream...
+At subvol snap
+Verifying data matches the original filesystem...
+OK
+File bar fiemap output in the new filesystem:
+0: [0..127]: shared|encoded
+1: [128..255]: encoded|last
diff --git a/tests/btrfs/282 b/tests/btrfs/282
new file mode 100755 (executable)
index 0000000..395e062
--- /dev/null
@@ -0,0 +1,90 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 282
+#
+# Make sure scrub speed limitation works as expected.
+#
+. ./common/preamble
+_begin_fstest auto scrub
+
+. ./common/filter
+
+# real QA test starts here
+_supported_fs btrfs
+_wants_kernel_commit eb3b50536642 \
+       "btrfs: scrub: per-device bandwidth control"
+
+# We want at least 5G for the scratch device.
+_require_scratch_size $(( 5 * 1024 * 1024))
+
+# Make sure we can create scrub progress data file
+if [ -e /var/lib/btrfs ]; then
+       test -w /var/lib/btrfs || _notrun '/var/lib/btrfs is not writable'
+else
+       test -w /var/lib || _notrun '/var/lib/btrfs cannot be created'
+fi
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+uuid=$(findmnt -n -o UUID $SCRATCH_MNT)
+
+devinfo_dir="/sys/fs/btrfs/${uuid}/devinfo/1"
+
+# Check if we have the sysfs interface first.
+if [ ! -f "${devinfo_dir}/scrub_speed_max" ]; then
+       _notrun "No sysfs interface for scrub speed throttle"
+fi
+
+# Create a 2G file for later scrub workload.
+# The 2G size is chosen to fit even DUP on a 5G disk.
+$XFS_IO_PROG -f -c "pwrite -i /dev/urandom 0 2G" $SCRATCH_MNT/file | _filter_xfs_io
+
+# Writeback above data, as scrub only verify the committed data.
+sync
+
+# The first scrub, mostly to grab the speed of the scrub.
+$BTRFS_UTIL_PROG scrub start -B $SCRATCH_MNT >> $seqres.full
+
+# We grab the rate from "scrub status" which supports raw bytes reporting
+#
+# The output looks like this:
+# UUID:             62eaabc5-93e8-445f-b8a7-6f027934aea7
+# Scrub started:    Thu Jan  5 14:59:12 2023
+# Status:           finished
+# Duration:         0:00:02
+# Total to scrub:   1076166656
+# Rate:             538083328/s
+# Error summary:    no errors found
+#
+# What we care is that Rate line.
+init_speed=$($BTRFS_UTIL_PROG scrub status --raw $SCRATCH_MNT | grep "Rate:" |\
+            $AWK_PROG '{print $2}' | cut -f1 -d\/)
+
+# This can happen for older progs
+if [ -z "$init_speed" ]; then
+       _notrun "btrfs-progs doesn't support scrub rate reporting"
+fi
+
+# Cycle mount to drop any possible cache.
+_scratch_cycle_mount
+
+target_speed=$(( $init_speed / 2 ))
+echo "$target_speed" > "${devinfo_dir}/scrub_speed_max"
+
+# The second scrub, to check the throttled speed.
+$BTRFS_UTIL_PROG scrub start -B $SCRATCH_MNT >> $seqres.full
+speed=$($BTRFS_UTIL_PROG scrub status --raw $SCRATCH_MNT | grep "Rate:" |\
+            $AWK_PROG '{print $2}' | cut -f1 -d\/)
+
+# We gave a +- 10% tolerance for the throttle
+if [ "$speed" -gt "$(( $target_speed * 11 / 10 ))" -o \
+     "$speed" -lt "$(( $target_speed * 9 / 10))" ]; then
+       echo "scrub speed $speed Bytes/s is not properly throttled, target is $target_speed Bytes/s"
+fi
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/282.out b/tests/btrfs/282.out
new file mode 100644 (file)
index 0000000..8d53e7e
--- /dev/null
@@ -0,0 +1,3 @@
+QA output created by 282
+wrote 2147483648/2147483648 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
diff --git a/tests/btrfs/283 b/tests/btrfs/283
new file mode 100755 (executable)
index 0000000..118df08
--- /dev/null
@@ -0,0 +1,96 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 283
+#
+# Test that send operations do the best cloning decisions when we have extents
+# that are shared but some files refer to the full extent while others refer to
+# only a section of the extent.
+#
+. ./common/preamble
+_begin_fstest auto quick send clone fiemap
+
+. ./common/filter
+. ./common/reflink
+. ./common/punch # for _filter_fiemap_flags
+
+_supported_fs btrfs
+_require_test
+_require_scratch_reflink
+_require_cp_reflink
+_require_xfs_io_command "fiemap"
+_require_fssum
+
+_wants_kernel_commit c7499a64dcf6 \
+            "btrfs: send: optimize clone detection to increase extent sharing"
+
+extent_size=$(( 128 * 1024 ))
+if _scratch_btrfs_is_zoned; then
+       zone_append_max=$(cat "/sys/block/$(_short_dev $SCRATCH_DEV)/queue/zone_append_max_bytes")
+       if [[ $zone_append_max -gt 0 && $zone_append_max -lt $extent_size ]]; then
+               _notrun "zone append max $zone_append_max is smaller than wanted extent size $extent_size"
+       fi
+fi
+
+send_files_dir=$TEST_DIR/btrfs-test-$seq
+send_stream=$send_files_dir/snap.stream
+snap_fssum=$send_files_dir/snap.fssum
+
+rm -fr $send_files_dir
+mkdir $send_files_dir
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+# When using compression, btrfs limits the extent size to 128K, so do not do
+# larger writes and then expect larger extents, as that would break the test
+# if we are run with compression enabled through $MOUNT_OPTIONS (resulting in
+# mismatch with the golden output).
+$XFS_IO_PROG -f -c "pwrite -S 0xab -b 128K 0 128K" $SCRATCH_MNT/foo | _filter_xfs_io
+
+# Now clone file foo twice, which will make the 128K extent shared 3 times.
+_cp_reflink $SCRATCH_MNT/foo $SCRATCH_MNT/bar
+_cp_reflink $SCRATCH_MNT/foo $SCRATCH_MNT/baz
+
+# Overwrite the second half of file foo.
+$XFS_IO_PROG -c "pwrite -S 0xcd -b 64K 64K 64K" $SCRATCH_MNT/foo | _filter_xfs_io
+
+echo "Creating snapshot and a send stream for it..."
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT $SCRATCH_MNT/snap \
+       | _filter_scratch
+
+$BTRFS_UTIL_PROG send -f $send_stream $SCRATCH_MNT/snap 2>&1 | _filter_scratch
+
+$FSSUM_PROG -A -f -w $snap_fssum $SCRATCH_MNT/snap
+
+echo "Creating a new filesystem to receive the send stream..."
+_scratch_unmount
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+$BTRFS_UTIL_PROG receive -f $send_stream $SCRATCH_MNT
+
+echo "Verifying data matches the original filesystem..."
+$FSSUM_PROG -r $snap_fssum $SCRATCH_MNT/snap
+
+# Now verify that all extents, for all files, are shared.
+
+# File 'foo' should have a single 128K extent, which is shared because its first
+# half is referred by files 'bar' and 'baz'.
+echo -e "\nfiemap of file foo:\n"
+$XFS_IO_PROG -r -c "fiemap -v" $SCRATCH_MNT/snap/foo | _filter_fiemap_flags
+
+# File 'bar' should have two 64K shared extents. The first one is shared with
+# files 'foo' and 'baz', while the second one is only shared with file 'baz'.
+echo -e "\nfiemap of file bar:\n"
+$XFS_IO_PROG -r -c "fiemap -v" $SCRATCH_MNT/snap/bar | _filter_fiemap_flags
+
+# File 'baz' should have two 64K shared extents. The first one is shared with
+# files 'foo' and 'bar', while the second one is only shared with file 'bar'.
+echo -e "\nfiemap of file baz:\n"
+$XFS_IO_PROG -r -c "fiemap -v" $SCRATCH_MNT/snap/baz | _filter_fiemap_flags
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/283.out b/tests/btrfs/283.out
new file mode 100644 (file)
index 0000000..286dae3
--- /dev/null
@@ -0,0 +1,26 @@
+QA output created by 283
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 65536
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Creating snapshot and a send stream for it...
+Create a readonly snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap'
+At subvol SCRATCH_MNT/snap
+Creating a new filesystem to receive the send stream...
+At subvol snap
+Verifying data matches the original filesystem...
+OK
+
+fiemap of file foo:
+
+0: [0..255]: shared|last
+
+fiemap of file bar:
+
+0: [0..127]: shared
+1: [128..255]: shared|last
+
+fiemap of file baz:
+
+0: [0..127]: shared
+1: [128..255]: shared|last
diff --git a/tests/btrfs/284 b/tests/btrfs/284
new file mode 100755 (executable)
index 0000000..0df494b
--- /dev/null
@@ -0,0 +1,138 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 284
+#
+# Test btrfs send stream v2, sending and receiving compressed data without
+# decompression at the sending side.
+#
+. ./common/preamble
+_begin_fstest auto quick send compress snapshot
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_btrfs_send_version 2
+_require_test
+# The size needed is variable as it depends on the specific randomized
+# operations from fsstress and on the value of $LOAD_FACTOR. But require at
+# least $LOAD_FACTOR * 1G, just to be on the safe side.
+_require_scratch_size $(($LOAD_FACTOR * 1 * 1024 * 1024))
+_require_fssum
+
+_fixed_by_git_commit btrfs-progs e3209f8792f4 \
+       "btrfs-progs: receive: fix a corruption when decompressing zstd extents"
+_fixed_by_git_commit btrfs-progs 6f4a51886b37 \
+       "btrfs-progs: receive: fix silent data loss after fall back from encoded write"
+
+send_files_dir=$TEST_DIR/btrfs-test-$seq
+
+rm -fr $send_files_dir
+mkdir $send_files_dir
+
+# Redirect stdout to the .full file and make it not part of the golden output.
+# This is because the number of available compression algorithms may vary across
+# kernel versions, so the number of times we are running this function is
+# variable.
+run_send_test()
+{
+       local algo=$1
+       local snapshot_cmd
+       local first_stream="$send_files_dir/snap1.stream"
+       local second_stream="$send_files_dir/snap2.stream"
+       local first_fssum="$send_files_dir/snap1.fssum"
+       local second_fssum="$send_files_dir/snap2.fssum"
+
+       _scratch_mkfs >> $seqres.full 2>&1
+       _scratch_mount -o compress=$algo
+
+       snapshot_cmd="$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT"
+       snapshot_cmd="$snapshot_cmd $SCRATCH_MNT/snap1"
+
+       # Use a single process so that in case of failure it's easier to
+       # reproduce by using the same seed (logged in $seqres.full).
+       run_check $FSSTRESS_PROG -d $SCRATCH_MNT -p 1 -n $((LOAD_FACTOR * 200)) \
+                 -w $FSSTRESS_AVOID -x "$snapshot_cmd" >> $seqres.full
+
+       $BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT $SCRATCH_MNT/snap2 \
+                        >> $seqres.full
+
+       echo "Creating full and incremental send streams..." >> $seqres.full
+
+       $BTRFS_UTIL_PROG send --compressed-data -q -f $first_stream \
+                        $SCRATCH_MNT/snap1 2>&1 | tee -a $seqres.full
+       $BTRFS_UTIL_PROG send --compressed-data -q -f $second_stream \
+                        -p $SCRATCH_MNT/snap1 $SCRATCH_MNT/snap2 2>&1 | \
+                        tee -a $seqres.full
+
+       echo "Computing the checksums for each snapshot..." >> $seqres.full
+
+       $FSSUM_PROG -A -f -w $first_fssum $SCRATCH_MNT/snap1 2>&1 | \
+               tee -a $seqres.full
+       $FSSUM_PROG -A -f -w $second_fssum -x $SCRATCH_MNT/snap2/snap1 \
+                   $SCRATCH_MNT/snap2 2>&1 | tee -a $seqres.full
+
+       echo "Creating a new fs to receive the streams..." >> $seqres.full
+
+       _scratch_unmount
+       _scratch_mkfs >> $seqres.full 2>&1
+       _scratch_mount
+
+       echo "Receiving the streams..." >> $seqres.full
+
+       $BTRFS_UTIL_PROG receive -q -f $first_stream $SCRATCH_MNT 2>&1 | \
+               tee -a $seqres.full
+       $BTRFS_UTIL_PROG receive -q -f $second_stream $SCRATCH_MNT 2>&1 | \
+               tee -a $seqres.full
+
+       echo "Verifying the checksums for each snapshot..." >> $seqres.full
+
+       # On success, fssum outputs only a single line with "OK" to stdout, and
+       # on error it outputs several lines to stdout telling about each file
+       # with data or metadata mismatches. Since the number of times we run
+       # fssum depends on the available compression algorithms for the running
+       # kernel, filter out the success case, so we don't have a mismatch with
+       # the golden output. We only want the mismatch with the golden output in
+       # case there's a checksum failure.
+       $FSSUM_PROG -r $first_fssum $SCRATCH_MNT/snap1 | grep -Ev '^OK$' | \
+               tee -a $seqres.full
+       $FSSUM_PROG -r $second_fssum $SCRATCH_MNT/snap2 | grep -Ev '^OK$' | \
+               tee -a $seqres.full
+
+       # Now receive again the streams in a new filesystem, but this time use
+       # the option --force-decompress of the receiver to verify that it works
+       # as expected.
+       echo "Creating a new fs to receive the streams with decompression..." >> $seqres.full
+
+       _scratch_unmount
+       _scratch_mkfs >> $seqres.full 2>&1
+       _scratch_mount
+
+       echo "Receiving the streams with decompression..." >> $seqres.full
+
+       $BTRFS_UTIL_PROG receive -q --force-decompress -f $first_stream $SCRATCH_MNT 2>&1 \
+               | tee -a $seqres.full
+       $BTRFS_UTIL_PROG receive -q --force-decompress -f $second_stream $SCRATCH_MNT 2>&1 \
+               | tee -a $seqres.full
+
+       echo "Verifying the checksums for each snapshot..." >> $seqres.full
+
+       $FSSUM_PROG -r $first_fssum $SCRATCH_MNT/snap1 | grep -Ev '^OK$' | \
+               tee -a $seqres.full
+       $FSSUM_PROG -r $second_fssum $SCRATCH_MNT/snap2 | grep -Ev '^OK$' | \
+               tee -a $seqres.full
+
+       _scratch_unmount
+       rm -f $send_files_dir/*
+}
+
+algo_list=($(_btrfs_compression_algos))
+for algo in ${algo_list[@]}; do
+       echo -e "\nTesting with $algo...\n" >> $seqres.full
+       run_send_test $algo
+done
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/btrfs/284.out b/tests/btrfs/284.out
new file mode 100644 (file)
index 0000000..931839f
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 284
+Silence is golden
diff --git a/tests/btrfs/285 b/tests/btrfs/285
new file mode 100755 (executable)
index 0000000..56cdad5
--- /dev/null
@@ -0,0 +1,50 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Meta Platforms, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 285
+#
+# Test that mounting a btrfs filesystem properly loads block group size classes.
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+sysfs_size_classes() {
+       local uuid="$(findmnt -n -o UUID "$SCRATCH_MNT")"
+       cat "/sys/fs/btrfs/$uuid/allocation/data/size_classes"
+}
+
+_supported_fs btrfs
+_require_scratch
+_require_btrfs_fs_sysfs
+_require_fs_sysfs allocation/data/size_classes
+
+f="$SCRATCH_MNT/f"
+small=$((16 * 1024))
+medium=$((1024 * 1024))
+large=$((16 * 1024 * 1024))
+
+_scratch_mkfs >/dev/null
+_scratch_mount
+# Write files with extents in each size class
+$XFS_IO_PROG -fc "pwrite -q 0 $small" $f.small
+$XFS_IO_PROG -fc "pwrite -q 0 $medium" $f.medium
+$XFS_IO_PROG -fc "pwrite -q 0 $large" $f.large
+# Sync to force the extent allocation
+sync
+pre=$(sysfs_size_classes)
+
+# cycle mount to drop the block group cache
+_scratch_cycle_mount
+
+# Another write causes us to actually load the block groups
+$XFS_IO_PROG -fc "pwrite -q 0 $large" $f.large.2
+sync
+
+post=$(sysfs_size_classes)
+diff <(echo $pre) <(echo $post)
+
+echo "Silence is golden"
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/285.out b/tests/btrfs/285.out
new file mode 100644 (file)
index 0000000..ab12da9
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 285
+Silence is golden
diff --git a/tests/btrfs/286 b/tests/btrfs/286
new file mode 100755 (executable)
index 0000000..71f6d4b
--- /dev/null
@@ -0,0 +1,81 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 286
+#
+# Make sure btrfs dev-replace on missing device won't cause data corruption
+# for NODATASUM data.
+#
+. ./common/preamble
+_begin_fstest auto replace
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_command "$WIPEFS_PROG" wipefs
+_btrfs_get_profile_configs replace-missing
+_require_fssum
+_require_scratch_dev_pool 5
+_scratch_dev_pool_get 4
+_spare_dev_get
+
+workload()
+{
+       local profile=$1
+       local victim="$(echo $SCRATCH_DEV_POOL | $AWK_PROG '{print $2}')"
+
+       echo "=== Profile: $profile ===" >> $seqres.full
+       rm -f $tmp.fssum
+       _scratch_pool_mkfs "$profile" >> $seqres.full 2>&1
+
+       # Use nodatasum mount option, so all data won't have checksum.
+       _scratch_mount -o nodatasum
+
+       $FSSTRESS_PROG -p 10 -n 200 -d $SCRATCH_MNT >> $seqres.full
+       sync
+
+       # Generate fssum for later verification, here we only care
+       # about the file contents, thus we don't bother metadata at all.
+       $FSSUM_PROG -n -d -f -w $tmp.fssum $SCRATCH_MNT
+       _scratch_unmount
+
+       # Wipe devid 2
+       $WIPEFS_PROG -a $victim >> $seqres.full 2>&1
+
+       # Mount the fs with the victim device missing
+       _scratch_mount -o degraded,nodatasum
+
+       # Verify no data corruption first.
+       echo "=== Verify the contents before replace ===" >> $seqres.full
+       $FSSUM_PROG -r $tmp.fssum $SCRATCH_MNT >> $seqres.full 2>&1
+
+       # Replace the missing device
+       $BTRFS_UTIL_PROG replace start -Bf 2 $SPARE_DEV $SCRATCH_MNT >> $seqres.full
+
+       # Drop all cache to make sure later read are all from the disks
+       echo 3 > /proc/sys/vm/drop_caches
+
+       # Re-check the file contents
+       echo "=== Verify the contents after replace ===" >> $seqres.full
+       $FSSUM_PROG -r $tmp.fssum $SCRATCH_MNT >> $seqres.full 2>&1
+
+       _scratch_unmount
+}
+
+for t in "${_btrfs_profile_configs[@]}"; do
+       workload "$t"
+done
+
+_spare_dev_put
+_scratch_dev_pool_put
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/286.out b/tests/btrfs/286.out
new file mode 100644 (file)
index 0000000..35c4800
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 286
+Silence is golden
diff --git a/tests/btrfs/287 b/tests/btrfs/287
new file mode 100755 (executable)
index 0000000..64e6ef3
--- /dev/null
@@ -0,0 +1,166 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 287
+#
+# Test btrfs' logical to inode ioctls (v1 and v2).
+#
+. ./common/preamble
+_begin_fstest auto quick snapshot clone punch logical_resolve
+
+. ./common/filter.btrfs
+. ./common/reflink
+
+_supported_fs btrfs
+_require_btrfs_scratch_logical_resolve_v2
+_require_scratch_reflink
+_require_xfs_io_command "fpunch"
+
+# This is a test case to test the logical to ino ioctl in general but it also
+# serves as a regression a test for an issue fixed by the following commit.
+_fixed_by_kernel_commit 0cad8f14d70c \
+       "btrfs: fix backref walking not returning all inode refs"
+
+query_logical_ino()
+{
+       $BTRFS_UTIL_PROG inspect-internal logical-resolve -P $* $SCRATCH_MNT
+}
+
+# The IDs of the snapshots (roots) we create may vary if we are using the free
+# space tree or not for example (mkfs options -R free-space-tree and
+# -R ^free-space-tree). So replace their IDs with names so that we don't get
+# golden output mismatches if we are using features that create other roots.
+filter_snapshot_ids()
+{
+       sed -e "s/root $snap1_id\b/snap1/" -e "s/root $snap2_id\b/snap2/"
+}
+
+_scratch_mkfs >> $seqres.full || _fail "mkfs failed"
+_scratch_mount
+
+# Create a file with two extents:
+#
+# 1) One 4M extent covering the file range [0, 4M)
+# 2) Another 4M extent covering the file range [4M, 8M)
+$XFS_IO_PROG -f -c "pwrite -S 0xab -b 4M 0 4M" \
+            -c "fsync" \
+            -c "pwrite -S 0xcd -b 4M 4M 8M" \
+            -c "fsync" $SCRATCH_MNT/foo | _filter_xfs_io
+
+echo "resolve first extent:"
+first_extent_bytenr=$(_btrfs_get_file_extent_item_bytenr "$SCRATCH_MNT/foo" 0)
+query_logical_ino $first_extent_bytenr
+
+echo "resolve second extent:"
+sz_4m=$((4 * 1024 * 1024))
+second_extent_bytenr=$(_btrfs_get_file_extent_item_bytenr "$SCRATCH_MNT/foo" $sz_4m)
+query_logical_ino $second_extent_bytenr
+
+# Now clone both extents twice to the end of the file.
+sz_8m=$((8 * 1024 * 1024))
+$XFS_IO_PROG -c "reflink $SCRATCH_MNT/foo 0 $sz_8m $sz_8m" $SCRATCH_MNT/foo \
+       | _filter_xfs_io
+sz_16m=$((16 * 1024 * 1024))
+$XFS_IO_PROG -c "reflink $SCRATCH_MNT/foo 0 $sz_16m $sz_8m" $SCRATCH_MNT/foo \
+       | _filter_xfs_io
+
+# Now lets resolve the extents again. They should now be listed 3 times each, at
+# the right file offsets.
+echo "resolve first extent:"
+query_logical_ino $first_extent_bytenr
+
+echo "resolve second extent:"
+query_logical_ino $second_extent_bytenr
+
+# Now lets punch a 2M hole at file offset 0. This changes the first file extent
+# item to point to the first extent with an offset of 2M and a length of 2M, so
+# doing a logical resolve with the bytenr of the first extent should not return
+# file offset 0.
+$XFS_IO_PROG -c "fpunch 0 2M" $SCRATCH_MNT/foo
+echo "resolve first extent after punching hole at file range [0, 2M):"
+query_logical_ino $first_extent_bytenr
+
+# Doing a logical resolve call with the BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET
+# flag (passing -o to logical-resolve command) should ignore file extent offsets
+# and return file offsets for all file extent items that point to any section of
+# the extent (3 of them, file offsets 2M, 8M and 16M).
+echo "resolve first extent with ignore offset option:"
+query_logical_ino -o $first_extent_bytenr
+
+# Now query for file extent items containing the first extent at offset +1M.
+# Should only return the file offsets 9M and 17M.
+bytenr=$(( $first_extent_bytenr + 1024 * 1024))
+echo "resolve first extent +1M offset:"
+query_logical_ino $bytenr
+
+# Now do the same query again but with the ignore offset ioctl argument. This
+# should returns 3 results, for file offsets 2M, 8M and 16M.
+echo "resolve first extent +1M offset with ignore offset option:"
+query_logical_ino -o $bytenr
+
+# Now query for file extent items containing the first extent at offset +3M.
+# Should return the file offsets 3M and 11M and 19M.
+bytenr=$(( $first_extent_bytenr + 3 * 1024 * 1024))
+echo "resolve first extent +3M offset:"
+query_logical_ino $bytenr
+
+# Now do the same query again but with the ignore offset ioctl argument. This
+# should returns 3 results, for file offsets 2M, 8M and 16M.
+echo "resolve first extent +3M offset with ignore offset option:"
+query_logical_ino -o $bytenr
+
+# Now create two snapshots and then do some queries.
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT $SCRATCH_MNT/snap1 \
+       | _filter_scratch
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT $SCRATCH_MNT/snap2 \
+       | _filter_scratch
+
+snap1_id=$(_btrfs_get_subvolid $SCRATCH_MNT snap1)
+snap2_id=$(_btrfs_get_subvolid $SCRATCH_MNT snap2)
+
+# Query for the first extent (at offset 0). Should give two entries for each
+# root - default subvolume and the 2 snapshots, for file offsets 8M and 16M.
+echo "resolve first extent:"
+query_logical_ino $first_extent_bytenr | filter_snapshot_ids
+
+# Query for the first extent (at offset 0) with the ignore offset option.
+# Should give 3 entries for each root - default subvolume and the 2 snapshots,
+# for file offsets 2M, 8M and 16M.
+echo "resolve first extent with ignore offset option:"
+query_logical_ino -o $first_extent_bytenr | filter_snapshot_ids
+
+# Now lets punch a 1M hole at file offset 4M. This changes the second file
+# extent item to point to the second extent with an offset of 1M and a length
+# of 3M, so doing a logical resolve with the bytenr of the second extent should
+# not return file offset 4M for root 5 (default subvolume), bit it should return
+# file offset 4M for the files in the snapshots. For all the roots, it should
+# return file offsets 12M and 20M.
+$XFS_IO_PROG -c "fpunch 4M 1M" $SCRATCH_MNT/foo
+echo "resolve second extent after punching hole at file range [4M, 5M):"
+query_logical_ino $second_extent_bytenr | filter_snapshot_ids
+
+# Repeat the query but with the ignore offset option. We should get 3 entries
+# for each root. For the snapshot roots, we should get entries for file offsets
+# 4M, 12M and 20M, while for the default subvolume (root 5) we should get for
+# file offsets 5M, 12M and 20M.
+echo "resolve second extent with ignore offset option:"
+query_logical_ino -o $second_extent_bytenr | filter_snapshot_ids
+
+# Now delete the first snapshot and repeat the last 2 queries.
+$BTRFS_UTIL_PROG subvolume delete -C $SCRATCH_MNT/snap1 | _filter_btrfs_subvol_delete
+
+# Query the second extent with an offset of 0, should return file offsets 12M
+# and 20M for the default subvolume (root 5) and file offsets 4M, 12M and 20M
+# for the second snapshot root.
+echo "resolve second extent:"
+query_logical_ino $second_extent_bytenr | filter_snapshot_ids
+
+# Query the second extent with the ignore offset option, should return file
+# offsets 5M, 12M and 20M for the default subvolume (root 5) and file offsets
+# 4M, 12M and 20M for the second snapshot root.
+echo "resolve second extent with ignore offset option:"
+query_logical_ino -o $second_extent_bytenr | filter_snapshot_ids
+
+status=0
+exit
diff --git a/tests/btrfs/287.out b/tests/btrfs/287.out
new file mode 100644 (file)
index 0000000..30eac8f
--- /dev/null
@@ -0,0 +1,95 @@
+QA output created by 287
+wrote 4194304/4194304 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8388608/8388608 bytes at offset 4194304
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+resolve first extent:
+inode 257 offset 0 root 5
+resolve second extent:
+inode 257 offset 4194304 root 5
+linked 8388608/8388608 bytes at offset 8388608
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+linked 8388608/8388608 bytes at offset 16777216
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+resolve first extent:
+inode 257 offset 16777216 root 5
+inode 257 offset 8388608 root 5
+inode 257 offset 0 root 5
+resolve second extent:
+inode 257 offset 20971520 root 5
+inode 257 offset 12582912 root 5
+inode 257 offset 4194304 root 5
+resolve first extent after punching hole at file range [0, 2M):
+inode 257 offset 16777216 root 5
+inode 257 offset 8388608 root 5
+resolve first extent with ignore offset option:
+inode 257 offset 16777216 root 5
+inode 257 offset 8388608 root 5
+inode 257 offset 2097152 root 5
+resolve first extent +1M offset:
+inode 257 offset 17825792 root 5
+inode 257 offset 9437184 root 5
+resolve first extent +1M offset with ignore offset option:
+inode 257 offset 16777216 root 5
+inode 257 offset 8388608 root 5
+inode 257 offset 2097152 root 5
+resolve first extent +3M offset:
+inode 257 offset 19922944 root 5
+inode 257 offset 11534336 root 5
+inode 257 offset 3145728 root 5
+resolve first extent +3M offset with ignore offset option:
+inode 257 offset 16777216 root 5
+inode 257 offset 8388608 root 5
+inode 257 offset 2097152 root 5
+Create a readonly snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap1'
+Create a readonly snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap2'
+resolve first extent:
+inode 257 offset 16777216 snap2
+inode 257 offset 8388608 snap2
+inode 257 offset 16777216 snap1
+inode 257 offset 8388608 snap1
+inode 257 offset 16777216 root 5
+inode 257 offset 8388608 root 5
+resolve first extent with ignore offset option:
+inode 257 offset 16777216 snap2
+inode 257 offset 8388608 snap2
+inode 257 offset 2097152 snap2
+inode 257 offset 16777216 snap1
+inode 257 offset 8388608 snap1
+inode 257 offset 2097152 snap1
+inode 257 offset 16777216 root 5
+inode 257 offset 8388608 root 5
+inode 257 offset 2097152 root 5
+resolve second extent after punching hole at file range [4M, 5M):
+inode 257 offset 20971520 snap2
+inode 257 offset 12582912 snap2
+inode 257 offset 4194304 snap2
+inode 257 offset 20971520 snap1
+inode 257 offset 12582912 snap1
+inode 257 offset 4194304 snap1
+inode 257 offset 20971520 root 5
+inode 257 offset 12582912 root 5
+resolve second extent with ignore offset option:
+inode 257 offset 20971520 snap2
+inode 257 offset 12582912 snap2
+inode 257 offset 4194304 snap2
+inode 257 offset 20971520 snap1
+inode 257 offset 12582912 snap1
+inode 257 offset 4194304 snap1
+inode 257 offset 20971520 root 5
+inode 257 offset 12582912 root 5
+inode 257 offset 5242880 root 5
+Delete subvolume 'SCRATCH_MNT/snap1'
+resolve second extent:
+inode 257 offset 20971520 snap2
+inode 257 offset 12582912 snap2
+inode 257 offset 4194304 snap2
+inode 257 offset 20971520 root 5
+inode 257 offset 12582912 root 5
+resolve second extent with ignore offset option:
+inode 257 offset 20971520 snap2
+inode 257 offset 12582912 snap2
+inode 257 offset 4194304 snap2
+inode 257 offset 20971520 root 5
+inode 257 offset 12582912 root 5
+inode 257 offset 5242880 root 5
diff --git a/tests/btrfs/288 b/tests/btrfs/288
new file mode 100755 (executable)
index 0000000..efa9a63
--- /dev/null
@@ -0,0 +1,70 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 288
+#
+# Make sure btrfs-scrub respects the read-only flag.
+#
+. ./common/preamble
+_begin_fstest auto repair quick volume scrub
+
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_scratch_dev_pool 2
+
+
+_require_odirect
+# Overwriting data is forbidden on a zoned block device
+_require_non_zoned_device "${SCRATCH_DEV}"
+
+_fixed_by_kernel_commit 1f2030ff6e49 \
+       "btrfs: scrub: respect the read-only flag during repair"
+
+_scratch_dev_pool_get 2
+
+# Step 1, create a raid btrfs with one 128K file
+echo "step 1......mkfs.btrfs"
+_scratch_pool_mkfs -d raid1 -b 1G >> $seqres.full 2>&1
+_scratch_mount
+
+$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 128K 0 128K" "$SCRATCH_MNT/foobar" |\
+       _filter_xfs_io
+
+# Step 2, corrupt one mirror so we can still repair the fs.
+echo "step 2......corrupt one mirror"
+# ensure btrfs-map-logical sees the tree updates
+sync
+
+logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+
+physical1=$(_btrfs_get_physical ${logical} 1)
+devpath1=$(_btrfs_get_device_path ${logical} 1)
+
+_scratch_unmount
+
+echo " corrupt stripe #1, devpath $devpath1 physical $physical1" \
+       >> $seqres.full
+$XFS_IO_PROG -d -c "pwrite -S 0xf1 -b 64K $physical1 64K" $devpath1 \
+       > /dev/null
+
+# Step 3, do a read-only scrub, which should not fix the corruption.
+echo "step 3......do a read-only scrub"
+_scratch_mount -o ro
+$BTRFS_UTIL_PROG scrub start -BRrd $SCRATCH_MNT >> $seqres.full 2>&1
+_scratch_unmount
+
+# Step 4, make sure the corruption is still there
+echo "step 4......verify the corruption is not repaired"
+echo "  the first 16 bytes of the extent at mirror 1:"
+$XFS_IO_PROG -c "pread -qv $physical1 16" $devpath1 |\
+       _filter_xfs_io_offset
+
+_scratch_dev_pool_put
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/288.out b/tests/btrfs/288.out
new file mode 100644 (file)
index 0000000..452bdc6
--- /dev/null
@@ -0,0 +1,9 @@
+QA output created by 288
+step 1......mkfs.btrfs
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+step 2......corrupt one mirror
+step 3......do a read-only scrub
+step 4......verify the corruption is not repaired
+  the first 16 bytes of the extent at mirror 1:
+XXXXXXXX:  f1 f1 f1 f1 f1 f1 f1 f1 f1 f1 f1 f1 f1 f1 f1 f1  ................
diff --git a/tests/btrfs/289 b/tests/btrfs/289
new file mode 100755 (executable)
index 0000000..f1aaf4c
--- /dev/null
@@ -0,0 +1,71 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 289
+#
+# Make sure btrfs-scrub reports errors correctly for repaired sectors.
+#
+. ./common/preamble
+_begin_fstest auto quick scrub repair
+
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_scratch
+# No data checksums for NOCOW case, so can't detect corruption and repair data.
+_require_btrfs_no_nodatacow
+
+_require_odirect
+# Overwriting data is forbidden on a zoned block device
+_require_non_zoned_device "${SCRATCH_DEV}"
+
+# The errors reported would be in the unit of sector, thus the number
+# is dependent on the sectorsize.
+_require_btrfs_support_sectorsize 4096
+
+_fixed_by_kernel_commit 79b8ee702c91 \
+       "btrfs: scrub: also report errors hit during the initial read"
+
+# Create a single btrfs with DUP data profile, and create one 128K file.
+_scratch_mkfs -s 4k -d dup -b 1G >> $seqres.full 2>&1
+_scratch_mount
+$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 128K 0 128K" "$SCRATCH_MNT/foobar" \
+       > /dev/null
+sync
+
+logical=$(_btrfs_get_first_logical "$SCRATCH_MNT/foobar")
+
+physical1=$(_btrfs_get_physical ${logical} 1)
+devpath1=$(_btrfs_get_device_path ${logical} 1)
+_scratch_unmount
+
+echo " corrupt stripe #1, devpath $devpath1 physical $physical1" \
+       >> $seqres.full
+$XFS_IO_PROG -d -c "pwrite -S 0xf1 -b 64K $physical1 128K" $devpath1 \
+       >> $seqres.full
+
+# Mount and do a scrub and compare the output
+_scratch_mount
+$BTRFS_UTIL_PROG scrub start -BR $SCRATCH_MNT >> $tmp.scrub_report 2>&1
+cat $tmp.scrub_report >> $seqres.full
+
+# Csum errors should be 128K/4K = 32
+csum_errors=$(grep "csum_errors" $tmp.scrub_report | $AWK_PROG '{print $2}')
+if [ $csum_errors -ne 32 ]; then
+       echo "csum_errors incorrect, expect 32 has $csum_errors"
+fi
+
+# And all errors should be repaired, thus corrected errors should also be 32.
+corrected_errors=$(grep "corrected_errors" $tmp.scrub_report | $AWK_PROG '{print $2}')
+if [ $corrected_errors -ne 32 ]; then
+       echo "corrected_errors incorrect, expect 32 has $corrected_errors"
+fi
+
+echo "Silence is golden"
+
+status=0
+exit
diff --git a/tests/btrfs/289.out b/tests/btrfs/289.out
new file mode 100644 (file)
index 0000000..7d3b7f8
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 289
+Silence is golden
diff --git a/tests/btrfs/290 b/tests/btrfs/290
new file mode 100755 (executable)
index 0000000..61e741f
--- /dev/null
@@ -0,0 +1,181 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2021 Facebook, Inc. All Rights Reserved.
+#
+# FS QA Test 290
+#
+# Test btrfs support for fsverity.
+# This test extends the generic fsverity testing by corrupting inline extents,
+# preallocated extents, holes, and the Merkle descriptor in a btrfs-aware way.
+#
+. ./common/preamble
+_begin_fstest auto quick verity prealloc
+
+# Import common functions.
+. ./common/filter
+. ./common/verity
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       _restore_fsverity_signatures
+       rm -f $tmp.*
+}
+
+# real QA test starts here
+_supported_fs btrfs
+_require_scratch_verity
+_require_scratch_nocheck
+_require_odirect
+_require_xfs_io_command "falloc"
+_require_xfs_io_command "pread"
+_require_xfs_io_command "pwrite"
+_require_btrfs_corrupt_block
+_disable_fsverity_signatures
+
+get_ino() {
+       local file=$1
+       stat -c "%i" $file
+}
+
+validate() {
+       local f=$1
+       local sz=$(_get_filesize $f)
+       # buffered io
+       echo $(basename $f)
+       $XFS_IO_PROG -rc "pread -q 0 $sz" $f 2>&1 | _filter_scratch
+       # direct io
+       $XFS_IO_PROG -rdc "pread -q 0 $sz" $f 2>&1 | _filter_scratch
+}
+
+# corrupt the data portion of an inline extent
+corrupt_inline() {
+       local f=$SCRATCH_MNT/inl
+       $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 42" $f
+       local ino=$(get_ino $f)
+       _fsv_enable $f
+       _scratch_unmount
+       # inline data starts at disk_bytenr
+       # overwrite the first u64 with random bogus junk
+       $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 0 -f disk_bytenr $SCRATCH_DEV > /dev/null 2>&1
+       _scratch_mount
+       validate $f
+}
+
+# preallocate a file, then corrupt it by changing it to a regular file
+corrupt_prealloc_to_reg() {
+       local f=$SCRATCH_MNT/prealloc
+       $XFS_IO_PROG -fc "falloc 0 12k" $f
+       local ino=$(get_ino $f)
+       _fsv_enable $f
+       _scratch_unmount
+       # ensure non-zero at the pre-allocated region on disk
+       # set extent type from prealloc (2) to reg (1)
+       $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 0 -f type -v 1 $SCRATCH_DEV >/dev/null 2>&1
+       _scratch_mount
+       # now that it's a regular file, reading actually looks at the previously
+       # preallocated region, so ensure that has non-zero contents.
+       head -c 5 /dev/zero | tr '\0' X | _fsv_scratch_corrupt_bytes $f 0
+       validate $f
+}
+
+# corrupt a regular file by changing the type to preallocated
+corrupt_reg_to_prealloc() {
+       local f=$SCRATCH_MNT/reg
+       $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
+       local ino=$(get_ino $f)
+       _fsv_enable $f
+       _scratch_unmount
+       # set type from reg (1) to prealloc (2)
+       $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 0 -f type -v 2 $SCRATCH_DEV >/dev/null 2>&1
+       _scratch_mount
+       validate $f
+}
+
+# corrupt a file by punching a hole
+corrupt_punch_hole() {
+       local f=$SCRATCH_MNT/punch
+       $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
+       local ino=$(get_ino $f)
+       # make a new extent in the middle, sync so the writes don't coalesce
+       $XFS_IO_PROG -c sync $SCRATCH_MNT
+       $XFS_IO_PROG -fc "pwrite -q -S 0x59 4096 4096" $f
+       _fsv_enable $f
+       _scratch_unmount
+       # change disk_bytenr to 0, representing a hole
+       $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 4096 -f disk_bytenr -v 0 $SCRATCH_DEV > /dev/null 2>&1
+       _scratch_mount
+       validate $f
+}
+
+# plug hole
+corrupt_plug_hole() {
+       local f=$SCRATCH_MNT/plug
+       $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
+       local ino=$(get_ino $f)
+       $XFS_IO_PROG -fc "falloc 4k 4k" $f
+       _fsv_enable $f
+       _scratch_unmount
+       # change disk_bytenr to some value, plugging the hole
+       $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 4096 -f disk_bytenr -v 13639680 $SCRATCH_DEV > /dev/null 2>&1
+       _scratch_mount
+       validate $f
+}
+
+# corrupt the fsverity descriptor item indiscriminately (causes EINVAL)
+corrupt_verity_descriptor() {
+       local f=$SCRATCH_MNT/desc
+       $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
+       local ino=$(get_ino $f)
+       _fsv_enable $f
+       _scratch_unmount
+       # key for the descriptor item is <inode, BTRFS_VERITY_DESC_ITEM_KEY, 1>,
+       # 88 is X. So we write 5 Xs to the start of the descriptor
+       $BTRFS_CORRUPT_BLOCK_PROG -r 5 -I $ino,36,1 -v 88 -o 0 -b 5 $SCRATCH_DEV > /dev/null 2>&1
+       _scratch_mount
+       validate $f
+}
+
+# specifically target the root hash in the descriptor (causes EIO)
+corrupt_root_hash() {
+       local f=$SCRATCH_MNT/roothash
+       $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
+       local ino=$(get_ino $f)
+       _fsv_enable $f
+       _scratch_unmount
+       $BTRFS_CORRUPT_BLOCK_PROG -r 5 -I $ino,36,1 -v 88 -o 16 -b 1 $SCRATCH_DEV > /dev/null 2>&1
+       _scratch_mount
+       validate $f
+}
+
+# corrupt the Merkle tree data itself
+corrupt_merkle_tree() {
+       local f=$SCRATCH_MNT/merkle
+       $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
+       local ino=$(get_ino $f)
+       _fsv_enable $f
+       _scratch_unmount
+       # key for the descriptor item is <inode, BTRFS_VERITY_MERKLE_ITEM_KEY, 0>,
+       # 88 is X. So we write 5 Xs to somewhere in the middle of the first
+       # merkle item
+       $BTRFS_CORRUPT_BLOCK_PROG -r 5 -I $ino,37,0 -v 88 -o 100 -b 5 $SCRATCH_DEV > /dev/null 2>&1
+       _scratch_mount
+       validate $f
+}
+
+# real QA test starts here
+_scratch_mkfs >/dev/null
+_scratch_mount
+
+corrupt_inline
+corrupt_prealloc_to_reg
+corrupt_reg_to_prealloc
+corrupt_punch_hole
+corrupt_plug_hole
+corrupt_verity_descriptor
+corrupt_root_hash
+corrupt_merkle_tree
+
+status=0
+exit
diff --git a/tests/btrfs/290.out b/tests/btrfs/290.out
new file mode 100644 (file)
index 0000000..056b114
--- /dev/null
@@ -0,0 +1,25 @@
+QA output created by 290
+inl
+pread: Input/output error
+pread: Input/output error
+prealloc
+pread: Input/output error
+pread: Input/output error
+reg
+pread: Input/output error
+pread: Input/output error
+punch
+pread: Input/output error
+pread: Input/output error
+plug
+pread: Input/output error
+pread: Input/output error
+desc
+SCRATCH_MNT/desc: Invalid argument
+SCRATCH_MNT/desc: Invalid argument
+roothash
+pread: Input/output error
+pread: Input/output error
+merkle
+pread: Input/output error
+pread: Input/output error
diff --git a/tests/btrfs/291 b/tests/btrfs/291
new file mode 100755 (executable)
index 0000000..bfffb84
--- /dev/null
@@ -0,0 +1,170 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2021 Facebook, Inc. All Rights Reserved.
+#
+# FS QA Test 291
+#
+# Test btrfs consistency after each FUA while enabling verity on a file
+# This test works by following the pattern in log-writes/replay-individual.sh:
+# 1. run a workload (verity + sync) while logging to the log device
+# 2. replay an entry to the replay device
+# 3. snapshot the replay device to the snapshot device
+# 4. run destructive tests on the snapshot device (e.g. mount with orphans)
+# 5. goto 2
+#
+. ./common/preamble
+_begin_fstest auto verity recoveryloop
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       _log_writes_cleanup &> /dev/null
+       $LVM_PROG vgremove -f -y $vgname >>$seqres.full 2>&1
+       losetup -d $loop_dev >>$seqres.full 2>&1
+       rm -f $img
+       _restore_fsverity_signatures
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+. ./common/dmlogwrites
+. ./common/verity
+
+# real QA test starts here
+_supported_fs btrfs
+
+_require_scratch
+_require_test
+_require_loop
+_require_log_writes
+_require_dm_target snapshot
+_require_command $LVM_PROG lvm
+_require_scratch_verity
+_require_btrfs_command inspect-internal dump-tree
+_require_test_program "log-writes/replay-log"
+_disable_fsverity_signatures
+
+sync_loop() {
+       i=$1
+       [ -z "$i" ] && _fail "sync loop needs a number of iterations"
+       while [ $i -gt 0 ]
+       do
+               $XFS_IO_PROG -c sync $SCRATCH_MNT
+               let i-=1
+       done
+}
+
+dump_tree() {
+       local dev=$1
+       $BTRFS_UTIL_PROG inspect-internal dump-tree $dev
+}
+
+count_item() {
+       local dev=$1
+       local item=$2
+       dump_tree $dev | grep -c "$item"
+}
+
+count_merkle_items() {
+       local dev=$1
+       count_item $dev 'VERITY_\(DESC\|MERKLE\)_ITEM'
+}
+
+_log_writes_init $SCRATCH_DEV
+_log_writes_mkfs
+_log_writes_mount
+
+f=$SCRATCH_MNT/fsv
+MB=$((1024 * 1024))
+img=$TEST_DIR/$$.img
+$XFS_IO_PROG -fc "pwrite -q 0 $((10 * $MB))" $f
+$XFS_IO_PROG -c sync $SCRATCH_MNT
+sync_loop 10 &
+sync_proc=$!
+_fsv_enable $f
+$XFS_IO_PROG -c sync $SCRATCH_MNT
+wait $sync_proc
+
+_log_writes_unmount
+_log_writes_remove
+
+# the snapshot and the replay will each be the size of the log writes dev
+# so we create a loop device of size 2 * logwrites and then split it into
+# replay and snapshot with lvm.
+log_writes_blocks=$(blockdev --getsz $LOGWRITES_DEV)
+replay_bytes=$((512 * $log_writes_blocks))
+img_bytes=$((2 * $replay_bytes))
+
+$XFS_IO_PROG -fc "pwrite -q -S 0 $img_bytes $MB" $img >>$seqres.full 2>&1 || \
+       _fail "failed to create image for loop device"
+loop_dev=$(losetup -f --show $img)
+vgname=vg_replay
+lvname=lv_replay
+replay_dev=/dev/mapper/vg_replay-lv_replay
+snapname=lv_snap
+snap_dev=/dev/mapper/vg_replay-$snapname
+
+$LVM_PROG vgcreate -f $vgname $loop_dev >>$seqres.full 2>&1 || _fail "failed to vgcreate $vgname"
+$LVM_PROG lvcreate -L "$replay_bytes"B -n $lvname $vgname -y >>$seqres.full 2>&1 || \
+       _fail "failed to lvcreate $lvname"
+$UDEV_SETTLE_PROG >>$seqres.full 2>&1
+
+replay_log_prog=$here/src/log-writes/replay-log
+num_entries=$($replay_log_prog --log $LOGWRITES_DEV --num-entries)
+entry=$($replay_log_prog --log $LOGWRITES_DEV --replay $replay_dev --find --end-mark mkfs | cut -d@ -f1)
+prev=$(_log_writes_mark_to_entry_number mkfs)
+[ -z "$prev" ] && _fail "failed to locate entry mark 'mkfs'"
+cur=$(_log_writes_find_next_fua $prev)
+
+# state = 0: verity hasn't started
+# state = 1: verity underway
+# state = 2: verity done
+state=0
+while [ ! -z "$cur" ];
+do
+       _log_writes_replay_log_range $cur $replay_dev >> $seqres.full
+
+       $LVM_PROG lvcreate -s -L 4M -n $snapname $vgname/$lvname >>$seqres.full 2>&1 || \
+               _fail "Failed to create snapshot"
+       $UDEV_SETTLE_PROG >>$seqres.full 2>&1
+
+       orphan=$(count_item $snap_dev ORPHAN)
+       [ $state -eq 0 ] && [ $orphan -gt 0 ] && state=1
+
+       pre_mount=$(count_merkle_items $snap_dev)
+       _mount $snap_dev $SCRATCH_MNT || _fail "mount failed at entry $cur"
+       fsverity measure $SCRATCH_MNT/fsv >>$seqres.full 2>&1
+       measured=$?
+       umount $SCRATCH_MNT
+       [ $state -eq 1 ] && [ $measured -eq 0 ] && state=2
+       [ $state -eq 2 ] && ([ $measured -eq 0 ] || _fail "verity done, but measurement failed at entry $cur")
+       post_mount=$(count_merkle_items $snap_dev)
+
+       echo "entry: $cur, state: $state, orphan: $orphan, pre_mount: $pre_mount, post_mount: $post_mount" >> $seqres.full
+
+       if [ $state -eq 1 ]; then
+               [ $post_mount -eq 0 ] || \
+                       _fail "mount failed to clear under-construction merkle items pre: $pre_mount, post: $post_mount at entry $cur";
+       fi
+       if [ $state -eq 2 ]; then
+               [ $pre_mount -gt 0 ] || \
+                       _fail "expected to have verity items before mount at entry $cur"
+               [ $pre_mount -eq $post_mount ] || \
+                       _fail "mount cleared merkle items after verity was enabled $pre_mount vs $post_mount at entry $cur";
+       fi
+
+       $LVM_PROG lvremove $vgname/$snapname -y >>$seqres.full
+
+       prev=$cur
+       cur=$(_log_writes_find_next_fua $(($cur + 1)))
+done
+
+[ $state -eq 2 ] || _fail "expected to reach verity done state"
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/291.out b/tests/btrfs/291.out
new file mode 100644 (file)
index 0000000..04605c7
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 291
+Silence is golden
diff --git a/tests/btrfs/292 b/tests/btrfs/292
new file mode 100755 (executable)
index 0000000..32a7c3c
--- /dev/null
@@ -0,0 +1,91 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 292
+#
+# Test btrfs behavior with large chunks (size beyond 4G) for basic read-write
+# and discard.
+# This test focus on RAID0.
+#
+. ./common/preamble
+_begin_fstest auto raid volume trim
+
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_scratch_dev_pool 6
+_require_fstrim
+_fixed_by_kernel_commit a7299a18a179 \
+       "btrfs: fix u32 overflows when left shifting @stripe_nr"
+
+_scratch_dev_pool_get 6
+
+
+datasize=$((5 * 1024 * 1024 * 1024))
+filesize=$((8 * 1024 * 1024))
+nr_files=$(($datasize / $filesize))
+
+# Make sure each device has at least 2G.
+# Btrfs has a limits on per-device stripe length of 1G.
+# Double that so that we can ensure a RAID0 data chunk with 6G size.
+for i in $SCRATCH_DEV_POOL; do
+       devsize=$(blockdev --getsize64 "$i")
+       if [ $devsize -lt $((2 * 1024 * 1024 * 1024)) ]; then
+               _scratch_dev_pool_put
+               _notrun "device $i is too small, need at least 2G"
+       fi
+done
+
+_scratch_pool_mkfs -m raid1 -d raid0 >> $seqres.full 2>&1
+
+# We disable async/sync auto discard, so that btrfs won't try to
+# cache the discard result which can cause unexpected skip for some trim range.
+_scratch_mount -o nodiscard
+_require_batched_discard $SCRATCH_MNT
+
+# Fill the data chunk with 5G data.
+for (( i = 0; i < $nr_files; i++ )); do
+       $XFS_IO_PROG -f -c "pwrite -i /dev/urandom 0 $filesize" \
+               $SCRATCH_MNT/file_$i > /dev/null
+done
+sync
+echo "=== With initial 5G data written ===" >> $seqres.full
+$BTRFS_UTIL_PROG filesystem df $SCRATCH_MNT >> $seqres.full
+
+_scratch_unmount
+
+# Make sure we haven't corrupted anything.
+$BTRFS_UTIL_PROG check --check-data-csum $SCRATCH_DEV >> $seqres.full 2>&1
+if [ $? -ne 0 ]; then
+       _scratch_dev_pool_put
+       _fail "data corruption detected after initial data filling"
+fi
+
+_scratch_mount -o nodiscard
+# Delete half of the data, and do discard
+rm -rf - "$SCRATCH_MNT/*[02468]"
+sync
+$FSTRIM_PROG $SCRATCH_MNT
+
+echo "=== With 2.5G data trimmed ===" >> $seqres.full
+$BTRFS_UTIL_PROG filesystem df $SCRATCH_MNT >> $seqres.full
+_scratch_unmount
+
+# Make sure fstrim doesn't corrupt anything.
+$BTRFS_UTIL_PROG check --check-data-csum $SCRATCH_DEV >> $seqres.full 2>&1
+if [ $? -ne 0 ]; then
+       _scratch_dev_pool_put
+       _fail "data corruption detected after running fstrim"
+fi
+
+_scratch_dev_pool_put
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/292.out b/tests/btrfs/292.out
new file mode 100644 (file)
index 0000000..627309d
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 292
+Silence is golden
diff --git a/tests/btrfs/293 b/tests/btrfs/293
new file mode 100755 (executable)
index 0000000..06f96dc
--- /dev/null
@@ -0,0 +1,72 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test No. 293
+#
+# Test that if we have a subvolume with a non-active swap file, we can not
+# activate it if there are any snapshots. Also test that after all the snapshots
+# are removed, we will be able to activate the swapfile.
+#
+. ./common/preamble
+_begin_fstest auto quick swap snapshot remount
+
+_cleanup()
+{
+       cd /
+       rm -f $tmp.*
+       test -n "$swap_file" && swapoff $swap_file &> /dev/null
+}
+
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_fixed_by_kernel_commit deccae40e4b3 \
+       "btrfs: can_nocow_file_extent should pass down args->strict from callers"
+_require_scratch_swapfile
+
+_scratch_mkfs >> $seqres.full 2>&1
+_scratch_mount
+
+swap_file="$SCRATCH_MNT/swapfile"
+_format_swapfile $swap_file $(($(_get_page_size) * 64)) >> $seqres.full
+
+echo "Creating first snapshot..."
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT $SCRATCH_MNT/snap1 | _filter_scratch
+echo "Creating second snapshot..."
+$BTRFS_UTIL_PROG subvolume snapshot $SCRATCH_MNT $SCRATCH_MNT/snap2 | _filter_scratch
+
+echo "Activating swap file... (should fail due to snapshots)"
+_swapon_file $swap_file 2>&1 | _filter_scratch
+
+echo "Deleting first snapshot..."
+$BTRFS_UTIL_PROG subvolume delete -c $SCRATCH_MNT/snap1 | _filter_btrfs_subvol_delete
+
+# We deleted the snapshot and committed the transaction used to delete it (-c),
+# but all its extents are actually only deleted in the background, by the cleaner
+# kthread. So remount, which wakes up the cleaner kthread, with a commit interval
+# of 1 second and sleep for 1.1 seconds - after this we are guaranteed all
+# extents of the snapshot were deleted.
+echo "Remounting and waiting for cleaner thread to remove the first snapshot..."
+_scratch_remount commit=1
+sleep 1.2
+
+echo "Activating swap file... (should fail due to snapshot)"
+_swapon_file $swap_file 2>&1 | _filter_scratch
+
+echo "Deleting second snapshot..."
+$BTRFS_UTIL_PROG subvolume delete -c $SCRATCH_MNT/snap2 | _filter_btrfs_subvol_delete
+
+echo "Remounting and waiting for cleaner thread to remove the second snapshot..."
+_scratch_remount commit=1
+sleep 1.2
+
+# No more snapshots, we should be able to activate the swap file.
+echo "Activating swap file..."
+_swapon_file $swap_file
+echo "Disabling swap file..."
+swapoff $swap_file
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/293.out b/tests/btrfs/293.out
new file mode 100644 (file)
index 0000000..fd04ac9
--- /dev/null
@@ -0,0 +1,17 @@
+QA output created by 293
+Creating first snapshot...
+Create a readonly snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap1'
+Creating second snapshot...
+Create a snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap2'
+Activating swap file... (should fail due to snapshots)
+swapon: SCRATCH_MNT/swapfile: swapon failed: Invalid argument
+Deleting first snapshot...
+Delete subvolume 'SCRATCH_MNT/snap1'
+Remounting and waiting for cleaner thread to remove the first snapshot...
+Activating swap file... (should fail due to snapshot)
+swapon: SCRATCH_MNT/swapfile: swapon failed: Invalid argument
+Deleting second snapshot...
+Delete subvolume 'SCRATCH_MNT/snap2'
+Remounting and waiting for cleaner thread to remove the second snapshot...
+Activating swap file...
+Disabling swap file...
diff --git a/tests/btrfs/294 b/tests/btrfs/294
new file mode 100755 (executable)
index 0000000..d7d1364
--- /dev/null
@@ -0,0 +1,76 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test No. 294
+#
+# Test btrfs write behavior with large RAID56 chunks (size beyond 4G).
+#
+. ./common/preamble
+_begin_fstest auto raid volume
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+
+# No zoned support for RAID56 yet.
+_require_non_zoned_device "${SCRATCH_DEV}"
+
+_require_scratch_dev_pool 8
+_fixed_by_kernel_commit a7299a18a179 \
+       "btrfs: fix u32 overflows when left shifting @stripe_nr"
+_fixed_by_kernel_commit cb091225a538 \
+       "btrfs: fix remaining u32 overflows when left shifting stripe_nr"
+
+_scratch_dev_pool_get 8
+
+datasize=$((5 * 1024 * 1024 * 1024))
+
+
+workload()
+{
+       local data_profile=$1
+
+       _scratch_pool_mkfs -m raid1 -d $data_profile >> $seqres.full 2>&1
+       _scratch_mount
+       $XFS_IO_PROG -f -c "pwrite -b 1m 0 $datasize" $SCRATCH_MNT/foobar > /dev/null
+
+       # Unpatched kernel would trigger an ASSERT() or crash at writeback.
+       sync
+
+       echo "=== With initial 5G data written ($data_profile) ===" >> $seqres.full
+       $BTRFS_UTIL_PROG filesystem df $SCRATCH_MNT >> $seqres.full
+       _scratch_unmount
+
+       # Make sure we haven't corrupted anything.
+       $BTRFS_UTIL_PROG check --check-data-csum $SCRATCH_DEV >> $seqres.full 2>&1
+       if [ $? -ne 0 ]; then
+               _scratch_dev_pool_put
+               _fail "data corruption detected after initial data filling"
+       fi
+}
+
+# Make sure each device has at least 2G.
+# Btrfs has a limits on per-device stripe length of 1G.
+# Double that so that we can ensure a RAID6 data chunk with 6G size.
+for i in $SCRATCH_DEV_POOL; do
+       devsize=$(blockdev --getsize64 "$i")
+       if [ $devsize -lt $((2 * 1024 * 1024 * 1024)) ]; then
+               _scratch_dev_pool_put
+               _notrun "device $i is too small, need at least 2G"
+       fi
+done
+
+workload raid5
+workload raid6
+
+_scratch_dev_pool_put
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/294.out b/tests/btrfs/294.out
new file mode 100644 (file)
index 0000000..c09531d
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 294
+Silence is golden
diff --git a/tests/btrfs/295 b/tests/btrfs/295
new file mode 100755 (executable)
index 0000000..00a5c56
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test No. 295
+#
+# Make sure btrfs handles critical errors gracefully during mount.
+#
+. ./common/preamble
+_begin_fstest auto quick dangerous
+
+. ./common/filter
+_supported_fs btrfs
+_require_scratch
+# Directly writing to the device, which may not work with a zoned device
+_require_non_zoned_device "$SCRATCH_DEV"
+
+# Use single metadata profile so we only need to corrupt one copy of tree block
+_scratch_mkfs -m single > $seqres.full
+
+logical_root=$($BTRFS_UTIL_PROG inspect dump-tree -t root "$SCRATCH_DEV" | \
+              grep leaf | head -n1 | cut -f2 -d\ )
+physical_root=$(_btrfs_get_physical $logical_root 1)
+
+echo "tree root logical=$logical_root" >> $seqres.full
+echo "tree root physical=$physical_root" >> $seqres.full
+
+_pwrite_byte 0x00 "$physical_root" 4 $SCRATCH_DEV >> $seqres.full
+
+# mount may lead to crash
+_try_scratch_mount >> $seqres.full 2>&1
+
+echo "Silence is golden"
+
+# Re-create the fs to avoid false alert from the corrupted fs.
+_scratch_mkfs -m single >> $seqres.full
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/295.out b/tests/btrfs/295.out
new file mode 100644 (file)
index 0000000..cbebcba
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 295
+Silence is golden
diff --git a/tests/btrfs/296 b/tests/btrfs/296
new file mode 100755 (executable)
index 0000000..27f48e7
--- /dev/null
@@ -0,0 +1,66 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test No. 296
+#
+# Make sure that per-fs features sysfs interface get properly updated
+# when a new feature is added.
+#
+. ./common/preamble
+_begin_fstest auto quick balance
+
+# real QA test starts here
+_supported_fs btrfs
+_require_scratch_dev_pool 3
+_fixed_by_kernel_commit b7625f461da6 \
+       "btrfs: sysfs: update fs features directory asynchronously"
+
+# We need the global features support
+_require_btrfs_fs_sysfs
+
+global_features="/sys/fs/btrfs/features"
+# Make sure we have support RAID1C34 first
+if [ ! -f "${global_features}/raid1c34" ]; then
+       _notrun "no RAID1C34 support"
+fi
+
+_scratch_dev_pool_get 3
+
+# Go the very basic profile first, so that even older progs can support it.
+_scratch_pool_mkfs -m dup -d single >> $seqres.full 2>&1
+
+_scratch_mount
+uuid="$(findmnt -n -o UUID "$SCRATCH_MNT")"
+per_fs_features="/sys/fs/btrfs/${uuid}/features"
+
+# First we need per-fs features directory
+if [ ! -d "${per_fs_features}" ]; then
+       _notrun "no per-fs features sysfs directory"
+fi
+
+# Make sure the per-fs features doesn't include raid1c34
+if [ -f "${per_fs_features}/raid1c34" ]; then
+       _fail "raid1c34 feature found unexpectedly"
+fi
+
+# Balance to RAID1C3
+$BTRFS_UTIL_PROG balance start -mconvert=raid1c3 "$SCRATCH_MNT" >> $seqres.full
+
+# Sync before checking for sysfs update during cleaner_kthread().
+sync
+
+# Check if the per-fs features directory contains raid1c34 now
+# Make sure the per-fs features doesn't include raid1c34
+if [ ! -f "${per_fs_features}/raid1c34" ]; then
+       _fail "raid1c34 feature not found"
+fi
+
+echo "Silence is golden"
+
+_scratch_unmount
+_scratch_dev_pool_put
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/296.out b/tests/btrfs/296.out
new file mode 100644 (file)
index 0000000..7f92a47
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 296
+Silence is golden
diff --git a/tests/btrfs/297 b/tests/btrfs/297
new file mode 100755 (executable)
index 0000000..a002386
--- /dev/null
@@ -0,0 +1,85 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 297
+#
+# Make sure btrfs scrub can fix parity stripe corruption
+#
+. ./common/preamble
+_begin_fstest auto quick raid scrub
+
+. ./common/filter
+
+_supported_fs btrfs
+_require_odirect
+_require_non_zoned_device "${SCRATCH_DEV}"
+_require_scratch_dev_pool 3
+_fixed_by_kernel_commit 486c737f7fdc \
+       "btrfs: raid56: always verify the P/Q contents for scrub"
+
+workload()
+{
+       local profile=$1
+       local nr_devs=$2
+
+       echo "=== Testing $nr_devs devices $profile ===" >> $seqres.full
+       _scratch_dev_pool_get $nr_devs
+
+       _scratch_pool_mkfs -d $profile -m single >> $seqres.full 2>&1
+       # Use v2 space cache to prevent v1 space cache affecting
+       # the result.
+       _scratch_mount -o space_cache=v2
+
+       # Create one 64K extent which would cover one data stripe.
+       $XFS_IO_PROG -f -d -c "pwrite -S 0xaa -b 64K 0 64K" \
+               "$SCRATCH_MNT/foobar" > /dev/null
+       sync
+
+       # Corrupt the P/Q stripe
+       local logical=$(_btrfs_get_first_logical $SCRATCH_MNT/foobar)
+
+       # The 2nd copy is pointed to P stripe directly.
+       physical_p=$(_btrfs_get_physical ${logical} 2)
+       devpath_p=$(_btrfs_get_device_path ${logical} 2)
+
+       _scratch_unmount
+
+       echo "Corrupt stripe P at devpath $devpath_p physical $physical_p" \
+               >> $seqres.full
+       $XFS_IO_PROG -d -c "pwrite -S 0xff -b 64K $physical_p 64K" $devpath_p \
+               > /dev/null
+
+       # Do a scrub to try repair the P stripe.
+       _scratch_mount -o space_cache=v2
+       $BTRFS_UTIL_PROG scrub start -BdR $SCRATCH_MNT >> $seqres.full 2>&1
+       _scratch_unmount
+
+       # Verify the repaired content directly
+       local output=$($XFS_IO_PROG -c "pread -qv $physical_p 16" $devpath_p | _filter_xfs_io_offset)
+       local expect="XXXXXXXX:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................"
+
+       echo "The first 16 bytes of parity stripe after scrub:" >> $seqres.full
+       echo $output >> $seqres.full
+       if [ "$output" != "$expect" ]; then
+               echo "Unexpected parity content"
+               echo "has:"
+               echo "$output"
+               echo "expect"
+               echo "$expect"
+       fi
+
+       # Last safenet, let btrfs check --check-data-csum to do an offline scrub.
+       $BTRFS_UTIL_PROG check --check-data-csum $SCRATCH_DEV >> $seqres.full 2>&1
+       if [ $? -ne 0 ]; then
+               echo "Error detected after the scrub"
+       fi
+       _scratch_dev_pool_put
+}
+
+workload raid5 2
+workload raid6 3
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/btrfs/297.out b/tests/btrfs/297.out
new file mode 100644 (file)
index 0000000..41c373c
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 297
+Silence is golden
diff --git a/tests/btrfs/298 b/tests/btrfs/298
new file mode 100755 (executable)
index 0000000..0cea81d
--- /dev/null
@@ -0,0 +1,55 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle.  All Rights Reserved.
+#
+# FS QA Test 298
+#
+#   Check if the device scan registers for a single-device seed and drops
+#  it from the kernel if it is eventually marked as non-seed.
+#
+. ./common/preamble
+_begin_fstest auto quick seed
+
+_supported_fs btrfs
+_require_command "$BTRFS_TUNE_PROG" btrfstune
+_require_command "$WIPEFS_PROG" wipefs
+_require_scratch_dev_pool 2
+_scratch_dev_pool_get 1
+_spare_dev_get
+
+$WIPEFS_PROG -a $SCRATCH_DEV
+$WIPEFS_PROG -a $SPARE_DEV
+
+echo "#setup seed sprout device" >> $seqres.full
+_scratch_mkfs "-b 300M" >> $seqres.full 2>&1 || \
+       _fail "Fail to make SCRATCH_DEV with -b 300M"
+$BTRFS_TUNE_PROG -S 1 $SCRATCH_DEV
+_scratch_mount >> $seqres.full 2>&1
+$BTRFS_UTIL_PROG device add $SPARE_DEV $SCRATCH_MNT >> $seqres.full
+_scratch_unmount
+$BTRFS_UTIL_PROG device scan --forget
+
+echo "#Scan seed device and check using mount" >> $seqres.full
+$BTRFS_UTIL_PROG device scan $SCRATCH_DEV >> $seqres.full
+_mount $SPARE_DEV $SCRATCH_MNT
+umount $SCRATCH_MNT
+
+echo "#check again, ensures seed device still in kernel" >> $seqres.full
+_mount $SPARE_DEV $SCRATCH_MNT
+umount $SCRATCH_MNT
+
+echo "#Now scan of non-seed device makes kernel forget" >> $seqres.full
+$BTRFS_TUNE_PROG -f -S 0 $SCRATCH_DEV >> $seqres.full 2>&1
+$BTRFS_UTIL_PROG device scan $SCRATCH_DEV >> $seqres.full
+
+echo "#Sprout mount must fail for missing seed device" >> $seqres.full
+_mount $SPARE_DEV $SCRATCH_MNT > /dev/null 2>&1
+[[ $? == 32 ]] || _fail "mount failed to fail"
+
+_spare_dev_put
+_scratch_dev_pool_put
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/btrfs/298.out b/tests/btrfs/298.out
new file mode 100644 (file)
index 0000000..6343426
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 298
+Silence is golden
diff --git a/tests/btrfs/299 b/tests/btrfs/299
new file mode 100755 (executable)
index 0000000..d38bf2a
--- /dev/null
@@ -0,0 +1,91 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Meta Platforms, Inc.  All Rights Reserved.
+#
+# FS QA Test 299
+#
+# Given a file with extents:
+# [0 : 4096) (inline)
+# [4096 : N] (prealloc)
+# if a user uses the ioctl BTRFS_IOC_LOGICAL_INO[_V2] asking for the file of the
+# non-inline extent, it results in reading the offset field of the inline
+# extent, which is meaningless (it is full of user data..). If we are
+# particularly lucky, it can be past the end of the extent buffer, resulting in
+# a crash. This test creates that circumstance and asserts that logical inode
+# resolution is still successful.
+#
+. ./common/preamble
+_begin_fstest auto quick preallocrw logical_resolve
+
+# real QA test starts here
+# Modify as appropriate.
+_supported_fs btrfs
+_require_scratch
+_require_xfs_io_command "falloc" "-k"
+_require_btrfs_command inspect-internal logical-resolve
+# Can't run with nodatacow because we need to be able to create an inline extent
+# in a range with a prealloc extent, which can only happen with COW enabled.
+_require_btrfs_no_nodatacow
+_fixed_by_kernel_commit 560840afc3e6 \
+       "btrfs: fix resolving backrefs for inline extent followed by prealloc"
+
+# This test needs to create conditions s.t. the special inode's inline extent
+# is the first item in a leaf. Therefore, fix a leaf size and add
+# items that are otherwise not necessary to reproduce the inline-prealloc
+# condition to get to such a state.
+#
+# Roughly, the idea for getting the right item fill is to:
+# 1. create extra inline items to cause leaf splitting.
+# 2. put the target item in the middle so it is likely to catch the split
+# 3. add an extra variable inline item to tweak any final adjustments
+#
+# It took a bit of trial and error to hit working counts of inline items, since
+# it also had to account for dir and index items all going to the front.
+
+# use a 64k nodesize so that an fs with 64k pages and no subpage sector size
+# support will correctly reproduce the problem.
+_scratch_mkfs "--nodesize 64k" >> $seqres.full || _fail "mkfs failed"
+_scratch_mount
+
+f=$SCRATCH_MNT/f
+# write extra files before the evil file to use up the leaf and
+# help trick leaf balancing
+for i in {1..41}; do
+       $XFS_IO_PROG -fc "pwrite -q 0 1024" $f.inl.$i
+done
+
+# write a variable inline file to help pad the preceeding leaf
+$XFS_IO_PROG -fc "pwrite -q 0 1" $f.inl-var.$i
+
+# falloc the evil file whose inline extent will start a leaf
+$XFS_IO_PROG -fc "falloc -k 0 1m" $f.evil
+$XFS_IO_PROG -fc fsync $f.evil
+
+# write extra files after the evil file to use up the leaf and
+# help trick leaf balancing
+for i in {1..42}; do
+       $XFS_IO_PROG -fc "pwrite -q 0 1024" $f.inl.2.$i
+done
+
+# grab the prealloc offset from dump tree while it's still the only
+# extent data item for the inode
+logical=$(_btrfs_get_file_extent_item_bytenr $f.evil 0)
+
+# do the "small write; fsync; small write" pattern which reproduces the desired
+# item pattern of an inline extent followed by a preallocated extent. The 23
+# size is somewhat arbitrary, but ensures that the offset field is past the eb
+# when we are item 0 (borrowed from the actual crash this reproduces).
+$XFS_IO_PROG -fc "pwrite -q 0 23" $f.evil
+$XFS_IO_PROG -fc fsync $f.evil
+$XFS_IO_PROG -fc "pwrite -q 0 23" $f.evil
+
+# ensure we have all the extent_data items for when we do logical to inode
+# resolution
+sync
+
+# trigger the backref walk which accesses the bad inline extent
+btrfs inspect-internal logical-resolve $logical $SCRATCH_MNT
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/btrfs/299.out b/tests/btrfs/299.out
new file mode 100644 (file)
index 0000000..0fcc030
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 299
+Silence is golden
diff --git a/tests/btrfs/300 b/tests/btrfs/300
new file mode 100755 (executable)
index 0000000..8a0eaec
--- /dev/null
@@ -0,0 +1,52 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Meta Platforms, Inc.  All Rights Reserved.
+#
+# FS QA Test 300
+#
+# Validate that snapshots taken while in a remapped namespace preserve
+# the permissions of the user.
+#
+. ./common/preamble
+
+_begin_fstest auto quick subvol snapshot
+_register_cleanup "cleanup"
+
+_supported_fs btrfs
+_fixed_by_kernel_commit 94628ad94408 \
+       "btrfs: copy dir permission and time when creating a stub subvolume"
+
+_require_test
+_require_user
+_require_group
+_require_unix_perm_checking
+_require_unshare --keep-caps --map-auto --map-root-user
+
+test_dir="${TEST_DIR}/${seq}"
+cleanup() {
+       rm -rf $test_dir
+       cd /
+       rm -rf $tmp.*
+}
+
+rm -rf $test_dir
+mkdir $test_dir
+chown $qa_user:$qa_group $test_dir
+
+# _user_do executes each command as $qa_user in its own subshell. unshare
+# sets the namespace for the running shell. The test must run in one user
+# subshell to preserve the namespace over multiple commands.
+_user_do "
+cd ${test_dir};
+unshare --user --keep-caps --map-auto --map-root-user;
+$BTRFS_UTIL_PROG subvolume create subvol;
+touch subvol/{1,2,3};
+$BTRFS_UTIL_PROG subvolume create subvol/subsubvol;
+touch subvol/subsubvol/{4,5,6};
+$BTRFS_UTIL_PROG subvolume snapshot subvol snapshot;
+"
+
+find $test_dir/. -printf "%M %u %g ./%P\n"
+
+status=0
+exit
diff --git a/tests/btrfs/300.out b/tests/btrfs/300.out
new file mode 100644 (file)
index 0000000..6e94447
--- /dev/null
@@ -0,0 +1,18 @@
+QA output created by 300
+Create subvolume './subvol'
+Create subvolume 'subvol/subsubvol'
+Create a snapshot of 'subvol' in './snapshot'
+drwxr-xr-x fsgqa fsgqa ./
+drwxr-xr-x fsgqa fsgqa ./subvol
+-rw-r--r-- fsgqa fsgqa ./subvol/1
+-rw-r--r-- fsgqa fsgqa ./subvol/2
+-rw-r--r-- fsgqa fsgqa ./subvol/3
+drwxr-xr-x fsgqa fsgqa ./subvol/subsubvol
+-rw-r--r-- fsgqa fsgqa ./subvol/subsubvol/4
+-rw-r--r-- fsgqa fsgqa ./subvol/subsubvol/5
+-rw-r--r-- fsgqa fsgqa ./subvol/subsubvol/6
+drwxr-xr-x fsgqa fsgqa ./snapshot
+-rw-r--r-- fsgqa fsgqa ./snapshot/1
+-rw-r--r-- fsgqa fsgqa ./snapshot/2
+-rw-r--r-- fsgqa fsgqa ./snapshot/3
+drwxr-xr-x fsgqa fsgqa ./snapshot/subsubvol
diff --git a/tests/btrfs/301 b/tests/btrfs/301
new file mode 100755 (executable)
index 0000000..db46972
--- /dev/null
@@ -0,0 +1,480 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Meta Platforms, Inc.  All Rights Reserved.
+#
+# FS QA Test 301
+#
+# Test common btrfs simple quotas scenarios involving sharing extents and
+# removing them in various orders.
+#
+. ./common/preamble
+_begin_fstest auto quick qgroup clone subvol prealloc snapshot remount
+
+# Import common functions.
+. ./common/reflink
+
+# Real QA test starts here.
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_scratch_reflink
+_require_cp_reflink
+_require_btrfs_command inspect-internal dump-tree
+_require_xfs_io_command "falloc"
+_require_scratch_enable_simple_quota
+_require_no_compress
+
+subv=$SCRATCH_MNT/subv
+nested=$SCRATCH_MNT/subv/nested
+snap=$SCRATCH_MNT/snap
+nr_fill=512
+fill_sz=$((8 * 1024))
+total_fill=$(($nr_fill * $fill_sz))
+nodesize=$($BTRFS_UTIL_PROG inspect-internal dump-super $SCRATCH_DEV | \
+                                       grep nodesize | $AWK_PROG '{print $2}')
+ext_sz=$((128 * 1024 * 1024))
+limit_nr=8
+limit=$(($ext_sz * $limit_nr))
+
+prep_fio_config=$tmp.fio
+fio_out=$tmp.fio.out
+cat >$prep_fio_config <<EOF
+[randwrite]
+name=filler
+directory=${subv}
+rw=randwrite
+nrfiles=${nr_fill}
+filesize=${fill_sz}
+EOF
+_require_fio $fio_config
+
+get_qgroup_usage()
+{
+       local qgroupid=$1
+
+       $BTRFS_UTIL_PROG qgroup show --sync --raw $SCRATCH_MNT | \
+                               grep "$qgroupid" | $AWK_PROG '{print $3}'
+}
+
+get_subvol_usage()
+{
+       local subvolid=$1
+
+       get_qgroup_usage "0/$subvolid"
+}
+
+count_subvol_owned_metadata()
+{
+       local subvolid=$1
+
+       # We need to sync so that the metadata extents are on disk and visible
+       # to dump-tree.
+       sync
+       # Find nodes and leaves owned by the subvol, then get unique offsets
+       # to account for snapshots sharing metadata.
+       count=$($BTRFS_UTIL_PROG inspect-internal dump-tree $SCRATCH_DEV | \
+               grep "owner $subvolid" | $AWK_PROG '{print $2}' | sort | \
+                                                               uniq | wc -l)
+       # Output bytes rather than number of metadata blocks.
+       echo $(($count * $nodesize))
+}
+
+check_qgroup_usage()
+{
+       local qgroupid=$1
+       local expected=$2
+       local actual=$(get_qgroup_usage $qgroupid)
+
+       [ $expected -eq $actual ] || \
+               echo "qgroup $qgroupid mismatched usage $actual vs $expected"
+}
+
+check_subvol_usage()
+{
+       local subvolid=$1
+       local expected_data=$2
+       local expected_meta=$(count_subvol_owned_metadata $subvolid)
+       local actual=$(get_subvol_usage $subvolid)
+       local expected=$(($expected_data + $expected_meta))
+
+       [ $expected -eq $actual ] || \
+               echo "subvol $subvolid mismatched usage $actual vs $expected (expected data $expected_data expected meta $expected_meta diff $(($actual - $expected)))"
+}
+
+set_subvol_limit()
+{
+       local subvolid=$1
+       local limit=$2
+
+       $BTRFS_UTIL_PROG qgroup limit $2 0/$1 $SCRATCH_MNT
+}
+
+# We need the cleaner thread to run to actually delete extents for test
+# cases that care about that. The remount wakes up the cleaner thread and sets
+# the commit interval to 1s, so the 1.5s sleep is enough to wait for the cleaner
+# thread to run.
+trigger_cleaner()
+{
+       _scratch_remount commit=1
+       sleep 1.5
+}
+
+cycle_mount_check_subvol_usage()
+{
+       _scratch_cycle_mount
+       check_subvol_usage $@
+}
+
+do_write()
+{
+       local file=$1
+       local sz=$2
+
+       $XFS_IO_PROG -fc "pwrite -q 0 $sz" $file
+}
+
+do_enospc_write()
+{
+       local file=$1
+       local sz=$2
+
+       do_write $file $sz
+}
+
+do_falloc()
+{
+       local file=$1
+       local sz=$2
+
+       $XFS_IO_PROG -fc "falloc 0 $sz" $file
+}
+
+do_enospc_falloc()
+{
+       local file=$1
+       local sz=$2
+
+       do_falloc $file $sz
+}
+
+enable_quota()
+{
+       local mode=$1
+
+       [ $mode == "n" ] && return
+       arg=$([ $mode == "s" ] && echo "--simple")
+
+       $BTRFS_UTIL_PROG quota enable $arg $SCRATCH_MNT
+}
+
+get_subvid()
+{
+       _btrfs_get_subvolid $SCRATCH_MNT subv
+}
+
+get_snapid()
+{
+       _btrfs_get_subvolid $SCRATCH_MNT snap
+}
+
+get_nestedid()
+{
+       _btrfs_get_subvolid $SCRATCH_MNT subv/nested
+}
+
+prepare()
+{
+       _scratch_mkfs >> $seqres.full
+       _scratch_mount
+       enable_quota "s"
+       $BTRFS_UTIL_PROG subvolume create $subv >> $seqres.full
+       local subvid=$(get_subvid)
+       set_subvol_limit $subvid $limit
+       check_subvol_usage $subvid 0
+
+       # Create a bunch of little filler files to generate several levels in
+       # the btree, to make snapshotting sharing scenarios complex enough.
+       $FIO_PROG $prep_fio_config --output=$fio_out
+       check_subvol_usage $subvid $total_fill
+
+       # Create a single file whose extents we will explicitly share/unshare.
+       do_write $subv/f $ext_sz
+       check_subvol_usage $subvid $(($total_fill + $ext_sz))
+}
+
+prepare_snapshotted()
+{
+       prepare
+       $BTRFS_UTIL_PROG subvolume snapshot $subv $snap >> $seqres.full
+       check_subvol_usage $(get_subvid) $(($total_fill + $ext_sz))
+       check_subvol_usage $(get_snapid) 0
+}
+
+prepare_nested()
+{
+       prepare
+       local subvid=$(get_subvid)
+       $BTRFS_UTIL_PROG qgroup create 1/100 $SCRATCH_MNT
+       $BTRFS_UTIL_PROG qgroup limit $limit 1/100 $SCRATCH_MNT
+       $BTRFS_UTIL_PROG qgroup assign 0/$subvid 1/100 $SCRATCH_MNT >> $seqres.full
+       $BTRFS_UTIL_PROG subvolume create $nested >> $seqres.full
+       local nestedid=$(get_nestedid)
+       do_write $nested/f $ext_sz
+       check_subvol_usage $nestedid $ext_sz
+       check_subvol_usage $subvid $(($total_fill + $ext_sz))
+       local subv_usage=$(get_subvol_usage $subvid)
+       local nested_usage=$(get_subvol_usage $nestedid)
+       check_qgroup_usage 1/100 $(($subv_usage + $nested_usage))
+}
+
+# Write in a single subvolume, including going over the limit.
+basic_accounting()
+{
+       echo "basic accounting"
+       prepare
+       local subvid=$(get_subvid)
+       rm $subv/f
+       check_subvol_usage $subvid $total_fill
+       cycle_mount_check_subvol_usage $subvid $total_fill
+       do_write $subv/tmp 512M
+       rm $subv/tmp
+       do_write $subv/tmp 512M
+       rm $subv/tmp
+       do_enospc_falloc $subv/large_falloc 2G
+       do_enospc_write $subv/large 2G
+       _scratch_unmount
+}
+
+# Write to the same range of a file a bunch of times in a row
+# to test extent aware reservations.
+reservation_accounting()
+{
+       echo "reservation accounting"
+       prepare
+       for i in $(seq 10); do
+               do_write $subv/tmp 512M
+               rm $subv/tmp
+       done
+       do_enospc_write $subv/large 2G
+       _scratch_unmount
+}
+
+# Write in a snapshot.
+snapshot_accounting()
+{
+       echo "snapshot accounting"
+       prepare_snapshotted
+       local subvid=$(get_subvid)
+       local snapid=$(get_snapid)
+       touch $snap/f
+       check_subvol_usage $subvid $(($total_fill + $ext_sz))
+       check_subvol_usage $snapid 0
+       do_write $snap/f $ext_sz
+       check_subvol_usage $subvid $(($total_fill + $ext_sz))
+       check_subvol_usage $snapid $ext_sz
+       rm $snap/f
+       check_subvol_usage $subvid $(($total_fill + $ext_sz))
+       check_subvol_usage $snapid 0
+       rm $subv/f
+       check_subvol_usage $subvid $total_fill
+       check_subvol_usage $snapid 0
+       cycle_mount_check_subvol_usage $subvid $total_fill
+       check_subvol_usage $snapid 0
+       _scratch_unmount
+}
+
+# Delete the original ref first after a snapshot.
+delete_snapshot_src_ref()
+{
+       echo "delete src ref first"
+       prepare_snapshotted
+       local subvid=$(get_subvid)
+       local snapid=$(get_snapid)
+       rm $subv/f
+       check_subvol_usage $subvid $(($total_fill + $ext_sz))
+       check_subvol_usage $snapid 0
+       rm $snap/f
+       trigger_cleaner
+       check_subvol_usage $subvid $total_fill
+       check_subvol_usage $snapid 0
+       cycle_mount_check_subvol_usage $subvid $total_fill
+       check_subvol_usage $snapid 0
+       _scratch_unmount
+}
+
+# Delete the snapshot ref first after a snapshot.
+delete_snapshot_ref()
+{
+       echo "delete snapshot ref first"
+       prepare_snapshotted
+       local subvid=$(get_subvid)
+       local snapid=$(get_snapid)
+       rm $snap/f
+       check_subvol_usage $subvid $(($total_fill + $ext_sz))
+       check_subvol_usage $snapid 0
+       rm $subv/f
+       check_subvol_usage $subvid $total_fill
+       check_subvol_usage $snapid 0
+       cycle_mount_check_subvol_usage $subvid $total_fill
+       check_subvol_usage $snapid 0
+       _scratch_unmount
+}
+
+# Delete the snapshotted subvolume after a snapshot.
+delete_snapshot_src()
+{
+       echo "delete snapshot src first"
+       prepare_snapshotted
+       local subvid=$(get_subvid)
+       local snapid=$(get_snapid)
+       $BTRFS_UTIL_PROG subvolume delete $subv >> $seqres.full
+       check_subvol_usage $subvid $(($total_fill + $ext_sz))
+       check_subvol_usage $snapid 0
+       rm $snap/f
+       trigger_cleaner
+       check_subvol_usage $subvid $total_fill
+       check_subvol_usage $snapid 0
+       $BTRFS_UTIL_PROG subvolume delete $snap >> $seqres.full
+       trigger_cleaner
+       check_subvol_usage $subvid 0
+       check_subvol_usage $snapid 0
+       cycle_mount_check_subvol_usage $subvid 0
+       check_subvol_usage $snapid 0
+       _scratch_unmount
+}
+
+# Delete the snapshot subvolume after a snapshot.
+delete_snapshot()
+{
+       echo "delete snapshot first"
+       prepare_snapshotted
+       local subvid=$(get_subvid)
+       local snapid=$(get_snapid)
+       $BTRFS_UTIL_PROG subvolume delete $snap >> $seqres.full
+       check_subvol_usage $subvid $(($total_fill + $ext_sz))
+       check_subvol_usage $snapid 0
+       $BTRFS_UTIL_PROG subvolume delete $subv >> $seqres.full
+       trigger_cleaner
+       check_subvol_usage $subvid 0
+       check_subvol_usage $snapid 0
+       _scratch_unmount
+}
+
+# Write to a subvolume nested in another subvolume.
+# Exercises the auto-inheritance feature of simple quotas.
+nested_accounting()
+{
+       echo "nested accounting"
+       prepare_nested
+       local subvid=$(get_subvid)
+       local nestedid=$(get_nestedid)
+       rm $subv/f
+       check_subvol_usage $subvid $total_fill
+       check_subvol_usage $nestedid $ext_sz
+       local subv_usage=$(get_subvol_usage $subvid)
+       local nested_usage=$(get_subvol_usage $nestedid)
+       check_qgroup_usage 1/100 $(($subv_usage + $nested_usage))
+       rm $nested/f
+       check_subvol_usage $subvid $total_fill
+       check_subvol_usage $nestedid 0
+       subv_usage=$(get_subvol_usage $subvid)
+       nested_usage=$(get_subvol_usage $nestedid)
+       check_qgroup_usage 1/100 $(($subv_usage + $nested_usage))
+       do_enospc_falloc $nested/large_falloc 2G
+       do_enospc_write $nested/large 2G
+       _scratch_unmount
+}
+
+# Enable simple quotas on a filesystem with existing extents.
+enable_mature()
+{
+       echo "enable mature"
+       _scratch_mkfs >> $seqres.full
+       _scratch_mount
+       $BTRFS_UTIL_PROG subvolume create $subv >> $seqres.full
+       local subvid=$(get_subvid)
+       do_write $subv/f $ext_sz
+       # Sync before enabling squotas to reliably *not* count the writes
+       # we did before enabling.
+       sync
+       enable_quota "s"
+       set_subvol_limit $subvid $limit
+       _scratch_cycle_mount
+       usage=$(get_subvol_usage $subvid)
+       [ $usage -lt $ext_sz ] || \
+               echo "captured usage from before enable $usage >= $ext_sz"
+       do_write $subv/g $ext_sz
+       usage=$(get_subvol_usage $subvid)
+       [ $usage -lt $ext_sz ] && \
+               echo "failed to capture usage after enable $usage < $ext_sz"
+       check_subvol_usage $subvid $ext_sz
+       rm $subv/f
+       check_subvol_usage $subvid $ext_sz
+       _scratch_cycle_mount
+       rm $subv/g
+       check_subvol_usage $subvid 0
+       _scratch_unmount
+}
+
+# Reflink a file within the subvolume.
+reflink_accounting()
+{
+       echo "reflink"
+       prepare
+       local subvid=$(get_subvid)
+       # Do enough reflinks to prove that they're free. If they counted, then
+       # this wouldn't fit in the limit.
+       for i in $(seq $(($limit_nr * 2))); do
+               _cp_reflink $subv/f $subv/f.i
+       done
+       # Confirm that there is no additional data usage from the reflinks.
+       check_subvol_usage $subvid $(($total_fill + $ext_sz))
+       _scratch_unmount
+}
+
+# Delete the src ref of a reflink first.
+delete_reflink_src_ref()
+{
+       echo "delete reflink src ref"
+       prepare
+       local subvid=$(get_subvid)
+       _cp_reflink $subv/f $subv/f.link
+       check_subvol_usage $subvid $(($total_fill + $ext_sz))
+       rm $subv/f
+       check_subvol_usage $subvid $(($total_fill + $ext_sz))
+       rm $subv/f.link
+       check_subvol_usage $subvid $(($total_fill))
+       _scratch_unmount
+}
+
+# Delete the link ref of a reflink first.
+delete_reflink_ref()
+{
+       echo "delete reflink ref"
+       prepare
+       local subvid=$(get_subvid)
+       _cp_reflink $subv/f $subv/f.link
+       check_subvol_usage $subvid $(($total_fill + $ext_sz))
+       rm $subv/f.link
+       check_subvol_usage $subvid $(($total_fill + $ext_sz))
+       rm $subv/f
+       check_subvol_usage $subvid $(($total_fill))
+       _scratch_unmount
+}
+
+basic_accounting
+reservation_accounting
+snapshot_accounting
+delete_snapshot_src_ref
+delete_snapshot_ref
+delete_snapshot_src
+delete_snapshot
+nested_accounting
+enable_mature
+reflink_accounting
+delete_reflink_src_ref
+delete_reflink_ref
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/301.out b/tests/btrfs/301.out
new file mode 100644 (file)
index 0000000..1c50268
--- /dev/null
@@ -0,0 +1,18 @@
+QA output created by 301
+basic accounting
+fallocate: Disk quota exceeded
+pwrite: Disk quota exceeded
+reservation accounting
+pwrite: Disk quota exceeded
+snapshot accounting
+delete src ref first
+delete snapshot ref first
+delete snapshot src first
+delete snapshot first
+nested accounting
+fallocate: Disk quota exceeded
+pwrite: Disk quota exceeded
+enable mature
+reflink
+delete reflink src ref
+delete reflink ref
diff --git a/tests/btrfs/302 b/tests/btrfs/302
new file mode 100755 (executable)
index 0000000..f3e6044
--- /dev/null
@@ -0,0 +1,61 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 302
+#
+# Test that snapshotting a new subvolume (created in the current transaction)
+# that has a btree with a height > 1, works and does not result in a filesystem
+# corruption.
+#
+# This exercises a regression introduced in kernel 6.5 by the kernel commit:
+#
+#    1b53e51a4a8f ("btrfs: don't commit transaction for every subvol create")
+#
+. ./common/preamble
+_begin_fstest auto quick snapshot subvol
+
+. ./common/filter
+
+_supported_fs btrfs
+_require_scratch
+_require_fssum
+
+_fixed_by_kernel_commit eb96e221937a \
+       "btrfs: fix unwritten extent buffer after snapshotting a new subvolume"
+
+# Use a filesystem with a 64K node size so that we have the same node size on
+# every machine regardless of its page size (on x86_64 default node size is 16K
+# due to the 4K page size, while on PPC it's 64K by default). This way we can
+# make sure we are able to create a btree for the subvolume with a height of 2.
+_scratch_mkfs -n 64K >> $seqres.full 2>&1 || _fail "mkfs failed"
+_scratch_mount
+
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/subvol | _filter_scratch
+
+# Create a few empty files on the subvolume, this bumps its btree height to 2
+# (root node at level 1 and 2 leaves).
+for ((i = 1; i <= 300; i++)); do
+       echo -n > $SCRATCH_MNT/subvol/file_$i
+done
+
+# Create a checksum of the subvolume's content.
+fssum_file="$SCRATCH_MNT/checksum.fssum"
+$FSSUM_PROG -A -f -w $fssum_file $SCRATCH_MNT/subvol
+
+# Now create a snapshot of the subvolume and make it accessible from within the
+# subvolume.
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT/subvol \
+                $SCRATCH_MNT/subvol/snap | _filter_scratch
+
+# Now unmount and mount again the fs. We want to verify we are able to read all
+# metadata for the snapshot from disk (no IO failures, etc).
+_scratch_cycle_mount
+
+# The snapshot's content should match the subvolume's content before we created
+# the snapshot.
+$FSSUM_PROG -r $fssum_file $SCRATCH_MNT/subvol/snap
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/302.out b/tests/btrfs/302.out
new file mode 100644 (file)
index 0000000..8770aef
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 302
+Create subvolume 'SCRATCH_MNT/subvol'
+Create a readonly snapshot of 'SCRATCH_MNT/subvol' in 'SCRATCH_MNT/subvol/snap'
+OK
diff --git a/tests/btrfs/303 b/tests/btrfs/303
new file mode 100755 (executable)
index 0000000..ed3abcc
--- /dev/null
@@ -0,0 +1,92 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2024 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 303
+#
+# Test that an incremental send does not issue unnecessary writes for a sparse
+# file that got one new extent between its previous extent and the file's size.
+#
+. ./common/preamble
+_begin_fstest auto quick snapshot send fiemap
+
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.*
+       rm -fr $send_files_dir
+}
+
+. ./common/filter
+. ./common/punch  # for _filter_fiemap
+
+_supported_fs btrfs
+_require_test
+_require_scratch
+_require_xfs_io_command "fiemap"
+
+_fixed_by_kernel_commit 5897710b28ca \
+       "btrfs: send: don't issue unnecessary zero writes for trailing hole"
+
+send_files_dir=$TEST_DIR/btrfs-test-$seq
+
+rm -fr $send_files_dir
+mkdir $send_files_dir
+
+_scratch_mkfs >> $seqres.full 2>&1 || _fail "mkfs failed"
+_scratch_mount
+
+$XFS_IO_PROG -f -c "truncate 1G" $SCRATCH_MNT/foobar
+
+# Now create the base snapshot, which is going to be the parent snapshot for
+# a later incremental send.
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT \
+                $SCRATCH_MNT/mysnap1 > /dev/null
+
+# Create send stream (full send) for the first snapshot.
+$BTRFS_UTIL_PROG send -f $send_files_dir/1.snap \
+                $SCRATCH_MNT/mysnap1 2>&1 1>/dev/null | _filter_scratch
+
+# Now write one extent at the beginning of the file and one somewhere in the
+# middle, leaving a gap between the end of this second extent and the file's
+# size.
+$XFS_IO_PROG -c "pwrite -S 0xab -b 64K 0 64K" \
+            -c "pwrite -S 0xcd -b 64K 512M 64K" \
+            $SCRATCH_MNT/foobar | _filter_xfs_io
+
+# Now create a second snapshot which is going to be used for an incremental
+# send operation.
+$BTRFS_UTIL_PROG subvolume snapshot -r $SCRATCH_MNT \
+                $SCRATCH_MNT/mysnap2 > /dev/null
+
+# Create send stream (incremental send) for the second snapshot.
+$BTRFS_UTIL_PROG send -p $SCRATCH_MNT/mysnap1 -f $send_files_dir/2.snap \
+                $SCRATCH_MNT/mysnap2 2>&1 1>/dev/null | _filter_scratch
+
+# Now recreate the filesystem by receiving both send streams and verify we get
+# the same content that the original filesystem had and file foobar has only two
+# extents with a size of 64K each.
+_scratch_unmount
+_scratch_mkfs >> $seqres.full 2>&1 || _fail "mkfs failed"
+_scratch_mount
+
+$BTRFS_UTIL_PROG receive -f $send_files_dir/1.snap $SCRATCH_MNT > /dev/null
+$BTRFS_UTIL_PROG receive -f $send_files_dir/2.snap $SCRATCH_MNT > /dev/null
+
+echo "File content in the new filesystem:"
+_hexdump $SCRATCH_MNT/mysnap2/foobar
+
+echo "File fiemap in the new filesystem:"
+# Should have:
+#
+# 64K extent at file range [0, 64K[
+# hole at file range [64K, 512M[
+# 64K extent at file range [512M, 512M + 64K[
+# hole at file range [512M + 64K, 1G[
+$XFS_IO_PROG -r -c "fiemap -v" $SCRATCH_MNT/mysnap2/foobar | _filter_fiemap
+
+# File should be using only 128K of data (two 64K extents).
+echo "Space used by the file: $(du -h $SCRATCH_MNT/mysnap2/foobar | cut -f 1)"
+
+status=0
+exit
diff --git a/tests/btrfs/303.out b/tests/btrfs/303.out
new file mode 100644 (file)
index 0000000..7659a79
--- /dev/null
@@ -0,0 +1,24 @@
+QA output created by 303
+At subvol SCRATCH_MNT/mysnap1
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 65536/65536 bytes at offset 536870912
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+At subvol SCRATCH_MNT/mysnap2
+At subvol mysnap1
+File content in the new filesystem:
+000000 ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab  >................<
+*
+010000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  >................<
+*
+20000000 cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd  >................<
+*
+20010000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  >................<
+*
+40000000
+File fiemap in the new filesystem:
+0: [0..127]: data
+1: [128..1048575]: hole
+2: [1048576..1048703]: data
+3: [1048704..2097151]: hole
+Space used by the file: 128K
diff --git a/tests/btrfs/304 b/tests/btrfs/304
new file mode 100755 (executable)
index 0000000..1ecc528
--- /dev/null
@@ -0,0 +1,58 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Western Digital Cooperation.  All Rights Reserved.
+#
+# FS QA Test 304
+#
+# Test on-disk layout of RAID Stripe Tree Metadata writing 4k to a new file on
+# a pristine file system.
+#
+. ./common/preamble
+_begin_fstest auto quick raid remount volume raid-stripe-tree
+
+. ./common/filter
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_require_btrfs_command inspect-internal dump-tree
+_require_btrfs_mkfs_feature "raid-stripe-tree"
+_require_scratch_dev_pool 4
+_require_btrfs_fs_feature "raid_stripe_tree"
+_require_btrfs_fs_feature "free_space_tree"
+_require_btrfs_free_space_tree
+_require_btrfs_no_compress
+
+test $(_get_page_size) -eq 4096 || _notrun "this tests requires 4k pagesize"
+
+test_4k_write()
+{
+       local profile=$1
+       local ndevs=$2
+
+       _scratch_dev_pool_get $ndevs
+
+       echo "==== Testing $profile ===="
+       _scratch_pool_mkfs -d $profile -m $profile -O raid-stripe-tree
+       _scratch_mount
+
+       $XFS_IO_PROG -fc "pwrite 0 4k" "$SCRATCH_MNT/foo" | _filter_xfs_io
+
+       _scratch_cycle_mount
+       md5sum "$SCRATCH_MNT/foo" | _filter_scratch
+
+       _scratch_unmount
+
+       $BTRFS_UTIL_PROG inspect-internal dump-tree -t raid_stripe $SCRATCH_DEV_POOL |\
+               _filter_stripe_tree
+
+       _scratch_dev_pool_put
+}
+
+echo "= Test basic 4k write ="
+test_4k_write raid0 2
+test_4k_write raid1 2
+test_4k_write raid10 4
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/304.out b/tests/btrfs/304.out
new file mode 100644 (file)
index 0000000..39f56f3
--- /dev/null
@@ -0,0 +1,58 @@
+QA output created by 304
+= Test basic 4k write =
+==== Testing raid0 ====
+wrote 4096/4096 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+5fed275e7617a806f94c173746a2a723  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 24
+                       encoding: RAID0
+                       stripe 0 devid 1 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid1 ====
+wrote 4096/4096 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+5fed275e7617a806f94c173746a2a723  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 40
+                       encoding: RAID1
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid10 ====
+wrote 4096/4096 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+5fed275e7617a806f94c173746a2a723  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 40
+                       encoding: RAID10
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
diff --git a/tests/btrfs/305 b/tests/btrfs/305
new file mode 100755 (executable)
index 0000000..1c09248
--- /dev/null
@@ -0,0 +1,63 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Western Digital Cooperation.  All Rights Reserved.
+#
+# FS QA Test 305
+#
+# Test on-disk layout of RAID Stripe Tree Metadata by writing 8k to a new file
+# with a filesystem prepropulated, so that 4k of the write are written to the
+# 1st stripe and 4k start a new stripe.
+#
+. ./common/preamble
+_begin_fstest auto quick raid remount volume raid-stripe-tree
+
+. ./common/filter
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_require_btrfs_command inspect-internal dump-tree
+_require_btrfs_mkfs_feature "raid-stripe-tree"
+_require_scratch_dev_pool 4
+_require_btrfs_fs_feature "raid_stripe_tree"
+_require_btrfs_fs_feature "free_space_tree"
+_require_btrfs_free_space_tree
+_require_btrfs_no_compress
+
+test $(_get_page_size) -eq 4096 || _notrun "this tests requires 4k pagesize"
+
+test_8k_new_stripe()
+{
+       local profile=$1
+       local ndevs=$2
+
+       _scratch_dev_pool_get $ndevs
+
+       echo "==== Testing $profile ===="
+       _scratch_pool_mkfs -d $profile -m $profile -O raid-stripe-tree
+       _scratch_mount
+
+       # Fill the first stripe up to 64k - 4k
+       $XFS_IO_PROG -fc "pwrite 0 60k" -c fsync "$SCRATCH_MNT/bar" | _filter_xfs_io
+
+       # The actual 8k write
+       $XFS_IO_PROG -fc "pwrite 0 8k" "$SCRATCH_MNT/foo" | _filter_xfs_io
+
+       _scratch_cycle_mount
+       md5sum "$SCRATCH_MNT/foo" | _filter_scratch
+
+       _scratch_unmount
+
+       $BTRFS_UTIL_PROG inspect-internal dump-tree -t raid_stripe $SCRATCH_DEV_POOL |\
+               _filter_stripe_tree
+
+       _scratch_dev_pool_put
+}
+
+echo "= Test 8k write to a new file so that 4k start a new stripe ="
+test_8k_new_stripe raid0 2
+test_8k_new_stripe raid1 2
+test_8k_new_stripe raid10 4
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/305.out b/tests/btrfs/305.out
new file mode 100644 (file)
index 0000000..7090626
--- /dev/null
@@ -0,0 +1,82 @@
+QA output created by 305
+= Test 8k write to a new file so that 4k start a new stripe =
+==== Testing raid0 ====
+wrote 61440/61440 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8192/8192 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+9d3940adb41dd525e008a847e01b15f4  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 61440) itemoff XXXXX itemsize 24
+                       encoding: RAID0
+                       stripe 0 devid 1 physical XXXXXXXXX
+       item 1 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 24
+                       encoding: RAID0
+                       stripe 0 devid 1 physical XXXXXXXXX
+       item 2 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 24
+                       encoding: RAID0
+                       stripe 0 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid1 ====
+wrote 61440/61440 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8192/8192 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+9d3940adb41dd525e008a847e01b15f4  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 61440) itemoff XXXXX itemsize 40
+                       encoding: RAID1
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+       item 1 key (XXXXXX RAID_STRIPE 8192) itemoff XXXXX itemsize 40
+                       encoding: RAID1
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid10 ====
+wrote 61440/61440 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8192/8192 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+9d3940adb41dd525e008a847e01b15f4  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 61440) itemoff XXXXX itemsize 40
+                       encoding: RAID10
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+       item 1 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 40
+                       encoding: RAID10
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+       item 2 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 40
+                       encoding: RAID10
+                       stripe 0 devid 3 physical XXXXXXXXX
+                       stripe 1 devid 4 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
diff --git a/tests/btrfs/306 b/tests/btrfs/306
new file mode 100755 (executable)
index 0000000..6e38431
--- /dev/null
@@ -0,0 +1,61 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Western Digital Cooperation.  All Rights Reserved.
+#
+# FS QA Test 306
+#
+# Test on-disk layout of RAID Stripe Tree Metadata by writing 4k to an emppty
+# file at offset 64k with one stripe pre-filled on an otherwise pristine
+# filesystem.
+#
+. ./common/preamble
+_begin_fstest auto quick raid remount volume raid-stripe-tree
+
+. ./common/filter
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_require_btrfs_command inspect-internal dump-tree
+_require_btrfs_mkfs_feature "raid-stripe-tree"
+_require_scratch_dev_pool 4
+_require_btrfs_fs_feature "raid_stripe_tree"
+_require_btrfs_fs_feature "free_space_tree"
+_require_btrfs_free_space_tree
+_require_btrfs_no_compress
+
+test $(_get_page_size) -eq 4096 || _notrun "this tests requires 4k pagesize"
+
+test_4k_write_64koff()
+{
+       local profile=$1
+       local ndevs=$2
+
+       _scratch_dev_pool_get $ndevs
+
+       echo "==== Testing $profile ===="
+       _scratch_pool_mkfs -d $profile -m $profile -O raid-stripe-tree
+       _scratch_mount
+
+       # precondition one stripe
+       $XFS_IO_PROG -fc "pwrite 0 64k" "$SCRATCH_MNT/bar" | _filter_xfs_io
+
+       $XFS_IO_PROG -fc "pwrite 64k 4k" "$SCRATCH_MNT/foo" | _filter_xfs_io
+
+       _scratch_cycle_mount
+       md5sum "$SCRATCH_MNT/foo" | _filter_scratch
+
+       _scratch_unmount
+
+       $BTRFS_UTIL_PROG inspect-internal dump-tree -t raid_stripe $SCRATCH_DEV_POOL |\
+               _filter_stripe_tree
+
+       _scratch_dev_pool_put
+}
+echo "= Test 4k write to an empty file at offset 64k with one stripe prefilled ="
+test_4k_write_64koff raid0 2
+test_4k_write_64koff raid1 2
+test_4k_write_64koff raid10 4
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/306.out b/tests/btrfs/306.out
new file mode 100644 (file)
index 0000000..2506567
--- /dev/null
@@ -0,0 +1,75 @@
+QA output created by 306
+= Test 4k write to an empty file at offset 64k with one stripe prefilled =
+==== Testing raid0 ====
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 4096/4096 bytes at offset 65536
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+381b0e7d72cb4f75286fe2b445e8d92a  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 24
+                       encoding: RAID0
+                       stripe 0 devid 1 physical XXXXXXXXX
+       item 1 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 24
+                       encoding: RAID0
+                       stripe 0 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid1 ====
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 4096/4096 bytes at offset 65536
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+381b0e7d72cb4f75286fe2b445e8d92a  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 40
+                       encoding: RAID1
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+       item 1 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 40
+                       encoding: RAID1
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid10 ====
+wrote 65536/65536 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 4096/4096 bytes at offset 65536
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+381b0e7d72cb4f75286fe2b445e8d92a  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 40
+                       encoding: RAID10
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+       item 1 key (XXXXXX RAID_STRIPE 4096) itemoff XXXXX itemsize 40
+                       encoding: RAID10
+                       stripe 0 devid 3 physical XXXXXXXXX
+                       stripe 1 devid 4 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
diff --git a/tests/btrfs/307 b/tests/btrfs/307
new file mode 100755 (executable)
index 0000000..d9c39b9
--- /dev/null
@@ -0,0 +1,58 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Western Digital Cooperation.  All Rights Reserved.
+#
+# FS QA Test 307
+#
+# Test on-disk layout of RAID Stripe Tree Metadata by writing 128k to a new
+# file on a pristine filesystem
+#
+. ./common/preamble
+_begin_fstest auto quick raid remount volume raid-stripe-tree
+
+. ./common/filter
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_require_btrfs_command inspect-internal dump-tree
+_require_btrfs_mkfs_feature "raid-stripe-tree"
+_require_scratch_dev_pool 4
+_require_btrfs_fs_feature "raid_stripe_tree"
+_require_btrfs_fs_feature "free_space_tree"
+_require_btrfs_free_space_tree
+_require_btrfs_no_compress
+
+test $(_get_page_size) -eq 4096 || _notrun "this tests requires 4k pagesize"
+
+test_128k_write()
+{
+       local profile=$1
+       local ndevs=$2
+
+       _scratch_dev_pool_get $ndevs
+
+       echo "==== Testing $profile ===="
+       _scratch_pool_mkfs -d $profile -m $profile -O raid-stripe-tree
+       _scratch_mount
+
+       $XFS_IO_PROG -fc "pwrite 0 128k" "$SCRATCH_MNT/foo" | _filter_xfs_io
+
+       _scratch_cycle_mount
+       md5sum "$SCRATCH_MNT/foo" | _filter_scratch
+
+       _scratch_unmount
+
+       $BTRFS_UTIL_PROG inspect-internal dump-tree -t raid_stripe $SCRATCH_DEV_POOL |\
+               _filter_stripe_tree
+
+       _scratch_dev_pool_put
+}
+
+echo "= Test 128k write to empty file  ="
+test_128k_write raid0 2
+test_128k_write raid1 2
+test_128k_write raid10 4
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/307.out b/tests/btrfs/307.out
new file mode 100644 (file)
index 0000000..2815d17
--- /dev/null
@@ -0,0 +1,65 @@
+QA output created by 307
+= Test 128k write to empty file  =
+==== Testing raid0 ====
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+d48858312a922db7eb86377f638dbc9f  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 24
+                       encoding: RAID0
+                       stripe 0 devid 1 physical XXXXXXXXX
+       item 1 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 24
+                       encoding: RAID0
+                       stripe 0 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid1 ====
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+d48858312a922db7eb86377f638dbc9f  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 131072) itemoff XXXXX itemsize 40
+                       encoding: RAID1
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid10 ====
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+d48858312a922db7eb86377f638dbc9f  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 40
+                       encoding: RAID10
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+       item 1 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 40
+                       encoding: RAID10
+                       stripe 0 devid 3 physical XXXXXXXXX
+                       stripe 1 devid 4 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
diff --git a/tests/btrfs/308 b/tests/btrfs/308
new file mode 100755 (executable)
index 0000000..ee9f15f
--- /dev/null
@@ -0,0 +1,62 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Western Digital Cooperation.  All Rights Reserved.
+#
+# FS QA Test 308
+#
+# Test on-disk layout of RAID Stripe Tree Metadata by writing 128k to an empty
+# file on a filesystem that has one stripe already pre-filled. Afterwards
+# overwrite a portion of the file.
+#
+. ./common/preamble
+_begin_fstest auto quick raid remount volume raid-stripe-tree
+
+. ./common/filter
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_require_btrfs_command inspect-internal dump-tree
+_require_btrfs_mkfs_feature "raid-stripe-tree"
+_require_scratch_dev_pool 4
+_require_btrfs_fs_feature "raid_stripe_tree"
+_require_btrfs_fs_feature "free_space_tree"
+_require_btrfs_free_space_tree
+_require_btrfs_no_compress
+_require_btrfs_no_nodatacow
+
+test $(_get_page_size) -eq 4096 || _notrun "this tests requires 4k pagesize"
+
+test_128k_write_overwrite()
+{
+       local profile=$1
+       local ndevs=$2
+
+       _scratch_dev_pool_get $ndevs
+
+       echo "==== Testing $profile ===="
+       _scratch_pool_mkfs -d $profile -m $profile -O raid-stripe-tree
+       _scratch_mount
+
+       $XFS_IO_PROG -fc "pwrite -W 0 32k" "$SCRATCH_MNT/bar" | _filter_xfs_io
+       $XFS_IO_PROG -fc "pwrite -W 0 128k" "$SCRATCH_MNT/foo" | _filter_xfs_io
+       $XFS_IO_PROG -fc "pwrite -W 64k 8k" "$SCRATCH_MNT/foo" | _filter_xfs_io
+
+       _scratch_cycle_mount
+       md5sum "$SCRATCH_MNT/foo" | _filter_scratch
+
+       _scratch_unmount
+
+       $BTRFS_UTIL_PROG inspect-internal dump-tree -t raid_stripe $SCRATCH_DEV_POOL |\
+               _filter_stripe_tree
+
+       _scratch_dev_pool_put
+}
+
+echo "= Test 128k write to empty file with 1st stripe partially prefilled then overwrite ="
+test_128k_write_overwrite raid0 2
+test_128k_write_overwrite raid1 2
+test_128k_write_overwrite raid10 4
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/308.out b/tests/btrfs/308.out
new file mode 100644 (file)
index 0000000..23b31dd
--- /dev/null
@@ -0,0 +1,106 @@
+QA output created by 308
+= Test 128k write to empty file with 1st stripe partially prefilled then overwrite =
+==== Testing raid0 ====
+wrote 32768/32768 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8192/8192 bytes at offset 65536
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+d48858312a922db7eb86377f638dbc9f  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 32768) itemoff XXXXX itemsize 24
+                       encoding: RAID0
+                       stripe 0 devid 1 physical XXXXXXXXX
+       item 1 key (XXXXXX RAID_STRIPE 32768) itemoff XXXXX itemsize 24
+                       encoding: RAID0
+                       stripe 0 devid 1 physical XXXXXXXXX
+       item 2 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 24
+                       encoding: RAID0
+                       stripe 0 devid 2 physical XXXXXXXXX
+       item 3 key (XXXXXX RAID_STRIPE 32768) itemoff XXXXX itemsize 24
+                       encoding: RAID0
+                       stripe 0 devid 1 physical XXXXXXXXX
+       item 4 key (XXXXXX RAID_STRIPE 8192) itemoff XXXXX itemsize 24
+                       encoding: RAID0
+                       stripe 0 devid 1 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid1 ====
+wrote 32768/32768 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8192/8192 bytes at offset 65536
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+d48858312a922db7eb86377f638dbc9f  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 32768) itemoff XXXXX itemsize 40
+                       encoding: RAID1
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+       item 1 key (XXXXXX RAID_STRIPE 131072) itemoff XXXXX itemsize 40
+                       encoding: RAID1
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+       item 2 key (XXXXXX RAID_STRIPE 8192) itemoff XXXXX itemsize 40
+                       encoding: RAID1
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
+==== Testing raid10 ====
+wrote 32768/32768 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 8192/8192 bytes at offset 65536
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+d48858312a922db7eb86377f638dbc9f  SCRATCH_MNT/foo
+
+raid stripe tree key (RAID_STRIPE_TREE ROOT_ITEM 0)
+leaf XXXXXXXXX items X free space XXXXX generation X owner RAID_STRIPE_TREE
+leaf XXXXXXXXX flags 0x1(WRITTEN) backref revision 1
+checksum stored <CHECKSUM>
+checksum calced <CHECKSUM>
+fs uuid <UUID>
+chunk uuid <UUID>
+       item 0 key (XXXXXX RAID_STRIPE 32768) itemoff XXXXX itemsize 40
+                       encoding: RAID10
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+       item 1 key (XXXXXX RAID_STRIPE 32768) itemoff XXXXX itemsize 40
+                       encoding: RAID10
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+       item 2 key (XXXXXX RAID_STRIPE 65536) itemoff XXXXX itemsize 40
+                       encoding: RAID10
+                       stripe 0 devid 3 physical XXXXXXXXX
+                       stripe 1 devid 4 physical XXXXXXXXX
+       item 3 key (XXXXXX RAID_STRIPE 32768) itemoff XXXXX itemsize 40
+                       encoding: RAID10
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+       item 4 key (XXXXXX RAID_STRIPE 8192) itemoff XXXXX itemsize 40
+                       encoding: RAID10
+                       stripe 0 devid 1 physical XXXXXXXXX
+                       stripe 1 devid 2 physical XXXXXXXXX
+total bytes XXXXXXXX
+bytes used XXXXXX
+uuid <UUID>
diff --git a/tests/btrfs/309 b/tests/btrfs/309
new file mode 100755 (executable)
index 0000000..d1eb953
--- /dev/null
@@ -0,0 +1,27 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+#
+# FS QA Test 309
+#
+# Try to snapshot a deleted subvolume.
+#
+. ./common/preamble
+_begin_fstest auto quick snapshot subvol
+
+_supported_fs btrfs
+_require_scratch
+_require_test_program t_snapshot_deleted_subvolume
+_fixed_by_kernel_commit 7081929ab257 \
+       "btrfs: don't abort filesystem when attempting to snapshot deleted subvolume"
+
+_scratch_mkfs >> $seqres.full 2>&1 || _fail "mkfs failed"
+_scratch_mount
+
+"$here/src/t_snapshot_deleted_subvolume" "$SCRATCH_MNT"
+# Make sure the filesystem didn't go read-only.
+touch "$SCRATCH_MNT/foo"
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/btrfs/309.out b/tests/btrfs/309.out
new file mode 100644 (file)
index 0000000..56330d6
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 309
+Silence is golden
diff --git a/tests/btrfs/310 b/tests/btrfs/310
new file mode 100755 (executable)
index 0000000..507485a
--- /dev/null
@@ -0,0 +1,83 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2024 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 310
+#
+# Make sure reading on an compressed inline extent is behaving correctly
+#
+. ./common/preamble
+_begin_fstest auto quick compress
+
+# Import common functions.
+# . ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_scratch
+
+# This test require inlined compressed extents creation, and all the writes
+# are designed for 4K sector size.
+_require_btrfs_inline_extents_creation
+_require_btrfs_support_sectorsize 4096
+
+_fixed_by_kernel_commit e01a83e12604 \
+       "Revert \"btrfs: zstd: fix and simplify the inline extent decompression\""
+
+# The correct md5 for the correct 4K file filled with "0xcd"
+md5sum_correct="5fed275e7617a806f94c173746a2a723"
+
+workload()
+{
+       local algo="$1"
+
+       echo "=== Testing compression algorithm ${algo} ===" >> $seqres.full
+       _scratch_mkfs >> $seqres.full
+       _scratch_mount -o compress=${algo}
+
+       _pwrite_byte 0xcd 0 4k "$SCRATCH_MNT/inline_file" > /dev/null
+       result=$(_md5_checksum "$SCRATCH_MNT/inline_file")
+       echo "after initial write, md5sum=${result}" >> $seqres.full
+       if [ "$result" != "$md5sum_correct" ]; then
+               _fail "initial write results incorrect content for \"$algo\""
+       fi
+       # Writeback data to get correct fiemap result, or we got FIEMAP_DEALLOC
+       # without compression/inline flags.
+       sync
+
+       $XFS_IO_PROG -c "fiemap -v" $SCRATCH_MNT/inline_file | tail -n 1 > $tmp.fiemap
+       cat $tmp.fiemap >> $seqres.full
+       # Make sure we got an inlined compressed file extent.
+       # 0x200 means inlined, 0x100 means not block aligned, 0x8 means encoded
+       # (compressed in this case), and 0x1 means the last extent.
+       if ! grep -q "0x309" $tmp.fiemap; then
+               rm -f -- $tmp.fiemap
+               _notrun "No compressed inline extent created, maybe subpage?"
+       fi
+       rm -f -- $tmp.fiemap
+
+       # Unmount to clear the page cache.
+       _scratch_cycle_mount
+
+       # For v6.8-rc1 without the revert or the newer fix, this would
+       # lead to VM_BUG_ON() thus crash
+       result=$(_md5_checksum "$SCRATCH_MNT/inline_file")
+       echo "after cycle mount, md5sum=${result}" >> $seqres.full
+       if [ "$result" != "$md5sum_correct" ]; then
+               _fail "read for compressed inline extent failed for \"$algo\""
+       fi
+       _scratch_unmount
+       _check_scratch_fs
+}
+
+algo_list=($(_btrfs_compression_algos))
+for algo in ${algo_list[@]}; do
+       workload $algo
+done
+
+echo "Silence is golden"
+
+status=0
+exit
diff --git a/tests/btrfs/310.out b/tests/btrfs/310.out
new file mode 100644 (file)
index 0000000..7b9eaf7
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 310
+Silence is golden
diff --git a/tests/btrfs/311 b/tests/btrfs/311
new file mode 100755 (executable)
index 0000000..7de8f05
--- /dev/null
@@ -0,0 +1,87 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle. All Rights Reserved.
+#
+# FS QA Test 311
+#
+# Mount the device twice check if the reflink works, this helps to
+# ensure device is mounted as the same device.
+#
+. ./common/preamble
+_begin_fstest auto quick subvol tempfsid
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       $UMOUNT_PROG $mnt1 > /dev/null 2>&1
+       rm -r -f $tmp.*
+       rm -r -f $mnt1
+}
+
+. ./common/filter.btrfs
+. ./common/reflink
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_cp_reflink
+_require_scratch
+_require_btrfs_fs_feature temp_fsid
+
+mnt1=$TEST_DIR/$seq/mnt1
+mkdir -p $mnt1
+
+same_dev_mount()
+{
+       echo ---- $FUNCNAME ----
+
+       _scratch_mkfs >> $seqres.full 2>&1
+
+       _scratch_mount
+       $XFS_IO_PROG -fc 'pwrite -S 0x61 0 9000' $SCRATCH_MNT/foo | \
+                                                               _filter_xfs_io
+
+       echo Mount the device again to a different mount point
+       _mount $SCRATCH_DEV $mnt1
+
+       _cp_reflink $SCRATCH_MNT/foo $mnt1/bar
+       echo Checksum of reflinked files
+       md5sum $SCRATCH_MNT/foo | _filter_scratch
+       md5sum $mnt1/bar | _filter_test_dir
+
+       check_fsid $SCRATCH_DEV
+}
+
+same_dev_subvol_mount()
+{
+       echo ---- $FUNCNAME ----
+       _scratch_mkfs >> $seqres.full 2>&1
+
+       _scratch_mount
+       $BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/subvol | _filter_scratch
+
+       $XFS_IO_PROG -fc 'pwrite -S 0x61 0 9000' $SCRATCH_MNT/subvol/foo | \
+                                                               _filter_xfs_io
+
+       echo Mounting a subvol
+       _mount -o subvol=subvol $SCRATCH_DEV $mnt1
+
+       _cp_reflink $SCRATCH_MNT/subvol/foo $mnt1/bar
+       echo Checksum of reflinked files
+       md5sum $SCRATCH_MNT/subvol/foo | _filter_scratch
+       md5sum $mnt1/bar | _filter_test_dir
+
+       check_fsid $SCRATCH_DEV
+}
+
+same_dev_mount
+
+_scratch_unmount
+_cleanup
+mkdir -p $mnt1
+
+same_dev_subvol_mount
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/311.out b/tests/btrfs/311.out
new file mode 100644 (file)
index 0000000..4ea46ea
--- /dev/null
@@ -0,0 +1,24 @@
+QA output created by 311
+---- same_dev_mount ----
+wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Mount the device again to a different mount point
+Checksum of reflinked files
+42d69d1a6d333a7ebdf64792a555e392  SCRATCH_MNT/foo
+42d69d1a6d333a7ebdf64792a555e392  TEST_DIR/311/mnt1/bar
+On disk fsid:          FSID
+Metadata uuid:         FSID
+Temp fsid:             FSID
+Tempfsid status:       0
+---- same_dev_subvol_mount ----
+Create subvolume 'SCRATCH_MNT/subvol'
+wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Mounting a subvol
+Checksum of reflinked files
+42d69d1a6d333a7ebdf64792a555e392  SCRATCH_MNT/subvol/foo
+42d69d1a6d333a7ebdf64792a555e392  TEST_DIR/311/mnt1/bar
+On disk fsid:          FSID
+Metadata uuid:         FSID
+Temp fsid:             FSID
+Tempfsid status:       0
diff --git a/tests/btrfs/312 b/tests/btrfs/312
new file mode 100755 (executable)
index 0000000..eedcf11
--- /dev/null
@@ -0,0 +1,78 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle.  All Rights Reserved.
+#
+# FS QA Test 312
+#
+# On a clone a device check to see if tempfsid is activated.
+#
+. ./common/preamble
+_begin_fstest auto quick clone tempfsid
+
+_cleanup()
+{
+       cd /
+       $UMOUNT_PROG $mnt1 > /dev/null 2>&1
+       rm -r -f $tmp.*
+       rm -r -f $mnt1
+}
+
+. ./common/filter.btrfs
+. ./common/reflink
+
+_supported_fs btrfs
+_require_scratch_dev_pool 2
+_scratch_dev_pool_get 2
+_require_btrfs_fs_feature temp_fsid
+
+mnt1=$TEST_DIR/$seq/mnt1
+mkdir -p $mnt1
+
+create_cloned_devices()
+{
+       local dev1=$1
+       local dev2=$2
+
+       echo -n Creating cloned device...
+       _mkfs_dev -fq -b $((1024 * 1024 * 300)) $dev1
+
+       _mount $dev1 $SCRATCH_MNT
+
+       $XFS_IO_PROG -fc 'pwrite -S 0x61 0 9000' $SCRATCH_MNT/foo | \
+                                                               _filter_xfs_io
+       $UMOUNT_PROG $SCRATCH_MNT
+       # device dump of $dev1 to $dev2
+       dd if=$dev1 of=$dev2 bs=300M count=1 conv=fsync status=none || \
+                                                       _fail "dd failed: $?"
+       echo done
+}
+
+mount_cloned_device()
+{
+       echo ---- $FUNCNAME ----
+       create_cloned_devices ${SCRATCH_DEV_NAME[0]} ${SCRATCH_DEV_NAME[1]}
+
+       echo Mounting original device
+       _mount ${SCRATCH_DEV_NAME[0]} $SCRATCH_MNT
+       $XFS_IO_PROG -fc 'pwrite -S 0x61 0 9000' $SCRATCH_MNT/foo | \
+                                                               _filter_xfs_io
+       check_fsid ${SCRATCH_DEV_NAME[0]}
+
+       echo Mounting cloned device
+       _mount ${SCRATCH_DEV_NAME[1]} $mnt1 || \
+                               _fail "mount failed, tempfsid didn't work"
+
+       echo cp reflink must fail
+       _cp_reflink $SCRATCH_MNT/foo $mnt1/bar 2>&1 | \
+                                               _filter_testdir_and_scratch
+
+       check_fsid ${SCRATCH_DEV_NAME[1]}
+}
+
+mount_cloned_device
+
+_scratch_dev_pool_put
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/312.out b/tests/btrfs/312.out
new file mode 100644 (file)
index 0000000..b7de6ce
--- /dev/null
@@ -0,0 +1,19 @@
+QA output created by 312
+---- mount_cloned_device ----
+Creating cloned device...wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+done
+Mounting original device
+wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+On disk fsid:          FSID
+Metadata uuid:         FSID
+Temp fsid:             FSID
+Tempfsid status:       0
+Mounting cloned device
+cp reflink must fail
+cp: failed to clone 'TEST_DIR/312/mnt1/bar' from 'SCRATCH_MNT/foo': Invalid cross-device link
+On disk fsid:          FSID
+Metadata uuid:         FSID
+Temp fsid:             TEMPFSID
+Tempfsid status:       1
diff --git a/tests/btrfs/313 b/tests/btrfs/313
new file mode 100755 (executable)
index 0000000..5b8062f
--- /dev/null
@@ -0,0 +1,52 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle.  All Rights Reserved.
+#
+# FS QA Test 313
+#
+# Functional test for the tempfsid, clone devices created using the mkfs option.
+#
+. ./common/preamble
+_begin_fstest auto quick clone tempfsid
+
+_cleanup()
+{
+       cd /
+       $UMOUNT_PROG $mnt1 > /dev/null 2>&1
+       rm -r -f $tmp.*
+       rm -r -f $mnt1
+}
+
+. ./common/filter.btrfs
+. ./common/reflink
+
+_supported_fs btrfs
+_require_cp_reflink
+_require_scratch_dev_pool 2
+_require_btrfs_fs_feature temp_fsid
+
+_scratch_dev_pool_get 2
+
+mnt1=$TEST_DIR/$seq/mnt1
+mkdir -p $mnt1
+
+echo ---- clone_uuids_verify_tempfsid ----
+mkfs_clone ${SCRATCH_DEV_NAME[0]} ${SCRATCH_DEV_NAME[1]}
+
+echo Mounting original device
+_mount ${SCRATCH_DEV_NAME[0]} $SCRATCH_MNT
+check_fsid ${SCRATCH_DEV_NAME[0]}
+
+echo Mounting cloned device
+_mount ${SCRATCH_DEV_NAME[1]} $mnt1
+check_fsid ${SCRATCH_DEV_NAME[1]}
+
+$XFS_IO_PROG -fc 'pwrite -S 0x61 0 9000' $SCRATCH_MNT/foo | _filter_xfs_io
+echo cp reflink must fail
+_cp_reflink $SCRATCH_MNT/foo $mnt1/bar 2>&1 | _filter_testdir_and_scratch
+
+_scratch_dev_pool_put
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/313.out b/tests/btrfs/313.out
new file mode 100644 (file)
index 0000000..7a089d2
--- /dev/null
@@ -0,0 +1,16 @@
+QA output created by 313
+---- clone_uuids_verify_tempfsid ----
+Mounting original device
+On disk fsid:          FSID
+Metadata uuid:         FSID
+Temp fsid:             FSID
+Tempfsid status:       0
+Mounting cloned device
+On disk fsid:          FSID
+Metadata uuid:         FSID
+Temp fsid:             TEMPFSID
+Tempfsid status:       1
+wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+cp reflink must fail
+cp: failed to clone 'TEST_DIR/313/mnt1/bar' from 'SCRATCH_MNT/foo': Invalid cross-device link
diff --git a/tests/btrfs/314 b/tests/btrfs/314
new file mode 100755 (executable)
index 0000000..887cb69
--- /dev/null
@@ -0,0 +1,78 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle.  All Rights Reserved.
+#
+# FS QA Test 314
+#
+# Send and receive functionality test between a normal and
+# tempfsid filesystem.
+#
+. ./common/preamble
+_begin_fstest auto quick snapshot send tempfsid
+
+_cleanup()
+{
+       cd /
+       $UMOUNT_PROG $tempfsid_mnt 2>/dev/null
+       rm -r -f $tmp.*
+       rm -r -f $sendfile
+       rm -r -f $tempfsid_mnt
+}
+
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_require_scratch_dev_pool 2
+_require_btrfs_fs_feature temp_fsid
+
+_scratch_dev_pool_get 2
+
+# mount point for the tempfsid device
+tempfsid_mnt=$TEST_DIR/$seq/tempfsid_mnt
+sendfile=$TEST_DIR/$seq/replicate.send
+
+send_receive_tempfsid()
+{
+       local src=$1
+       local dst=$2
+
+       # Use first 2 devices from the SCRATCH_DEV_POOL
+       mkfs_clone ${SCRATCH_DEV} ${SCRATCH_DEV_NAME[1]}
+       _scratch_mount
+       _mount ${SCRATCH_DEV_NAME[1]} ${tempfsid_mnt}
+
+       $XFS_IO_PROG -fc 'pwrite -S 0x61 0 9000' ${src}/foo | _filter_xfs_io
+       $BTRFS_UTIL_PROG subvolume snapshot -r ${src} ${src}/snap1 | \
+                                               _filter_testdir_and_scratch
+
+       echo Send ${src} | _filter_testdir_and_scratch
+       $BTRFS_UTIL_PROG send -f ${sendfile} ${src}/snap1 2>&1 | \
+                                               _filter_testdir_and_scratch
+       echo Receive ${dst} | _filter_testdir_and_scratch
+       $BTRFS_UTIL_PROG receive -f ${sendfile} ${dst} | \
+                                               _filter_testdir_and_scratch
+       echo -e -n "Send:\t"
+       md5sum  ${src}/foo | _filter_testdir_and_scratch
+       echo -e -n "Recv:\t"
+       md5sum ${dst}/snap1/foo | _filter_testdir_and_scratch
+}
+
+mkdir -p $tempfsid_mnt
+
+echo -e \\nFrom non-tempfsid ${SCRATCH_MNT} to tempfsid ${tempfsid_mnt} | \
+                                               _filter_testdir_and_scratch
+send_receive_tempfsid $SCRATCH_MNT $tempfsid_mnt
+
+_scratch_unmount
+_cleanup
+mkdir -p $tempfsid_mnt
+
+echo -e \\nFrom tempfsid ${tempfsid_mnt} to non-tempfsid ${SCRATCH_MNT} | \
+                                               _filter_testdir_and_scratch
+send_receive_tempfsid $tempfsid_mnt $SCRATCH_MNT
+
+_scratch_dev_pool_put
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/314.out b/tests/btrfs/314.out
new file mode 100644 (file)
index 0000000..2196389
--- /dev/null
@@ -0,0 +1,23 @@
+QA output created by 314
+
+From non-tempfsid SCRATCH_MNT to tempfsid TEST_DIR/314/tempfsid_mnt
+wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Create a readonly snapshot of 'SCRATCH_MNT' in 'SCRATCH_MNT/snap1'
+Send SCRATCH_MNT
+At subvol SCRATCH_MNT/snap1
+Receive TEST_DIR/314/tempfsid_mnt
+At subvol snap1
+Send:  42d69d1a6d333a7ebdf64792a555e392  SCRATCH_MNT/foo
+Recv:  42d69d1a6d333a7ebdf64792a555e392  TEST_DIR/314/tempfsid_mnt/snap1/foo
+
+From tempfsid TEST_DIR/314/tempfsid_mnt to non-tempfsid SCRATCH_MNT
+wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Create a readonly snapshot of 'TEST_DIR/314/tempfsid_mnt' in 'TEST_DIR/314/tempfsid_mnt/snap1'
+Send TEST_DIR/314/tempfsid_mnt
+At subvol TEST_DIR/314/tempfsid_mnt/snap1
+Receive SCRATCH_MNT
+At subvol snap1
+Send:  42d69d1a6d333a7ebdf64792a555e392  TEST_DIR/314/tempfsid_mnt/foo
+Recv:  42d69d1a6d333a7ebdf64792a555e392  SCRATCH_MNT/snap1/foo
diff --git a/tests/btrfs/315 b/tests/btrfs/315
new file mode 100755 (executable)
index 0000000..7e5c74d
--- /dev/null
@@ -0,0 +1,91 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle. All Rights Reserved.
+#
+# FS QA Test 315
+#
+# Verify if the seed and device add to a tempfsid filesystem fails
+# and balance devices is successful.
+#
+. ./common/preamble
+_begin_fstest auto quick volume seed balance tempfsid
+
+_cleanup()
+{
+       cd /
+       $UMOUNT_PROG $tempfsid_mnt 2>/dev/null
+       rm -r -f $tmp.*
+       rm -r -f $tempfsid_mnt
+}
+
+. ./common/filter.btrfs
+
+_supported_fs btrfs
+_require_scratch_dev_pool 3
+_require_btrfs_fs_feature temp_fsid
+
+_scratch_dev_pool_get 3
+
+# mount point for the tempfsid device
+tempfsid_mnt=$TEST_DIR/$seq/tempfsid_mnt
+
+_filter_mount_error()
+{
+       # There are two different errors that occur at the output when
+       # mounting fails; as shown below, pick out the common part. And,
+       # remove the dmesg line.
+
+       # mount: <mnt-point>: mount(2) system call failed: File exists.
+
+       # mount: <mnt-point>: fsconfig system call failed: File exists.
+       # dmesg(1) may have more information after failed mount system call.
+
+       grep -v dmesg | _filter_test_dir | sed -e "s/mount(2)\|fsconfig//g"
+}
+
+seed_device_must_fail()
+{
+       echo ---- $FUNCNAME ----
+
+       mkfs_clone ${SCRATCH_DEV} ${SCRATCH_DEV_NAME[1]}
+
+       $BTRFS_TUNE_PROG -S 1 ${SCRATCH_DEV}
+       $BTRFS_TUNE_PROG -S 1 ${SCRATCH_DEV_NAME[1]}
+
+       _scratch_mount 2>&1 | _filter_scratch
+       _mount ${SCRATCH_DEV_NAME[1]} ${tempfsid_mnt} 2>&1 | _filter_mount_error
+}
+
+device_add_must_fail()
+{
+       echo ---- $FUNCNAME ----
+
+       mkfs_clone ${SCRATCH_DEV} ${SCRATCH_DEV_NAME[1]}
+       _scratch_mount
+       _mount ${SCRATCH_DEV_NAME[1]} ${tempfsid_mnt}
+
+       $XFS_IO_PROG -fc 'pwrite -S 0x61 0 9000' $SCRATCH_MNT/foo | \
+                                                       _filter_xfs_io
+
+$BTRFS_UTIL_PROG device add -f ${SCRATCH_DEV_NAME[2]} ${tempfsid_mnt} 2>&1 | \
+               grep -v "Performing full device TRIM" | _filter_scratch_pool
+
+       echo Balance must be successful
+       _run_btrfs_balance_start ${tempfsid_mnt}
+}
+
+mkdir -p $tempfsid_mnt
+
+seed_device_must_fail
+
+_scratch_unmount
+_cleanup
+mkdir -p $tempfsid_mnt
+
+device_add_must_fail
+
+_scratch_dev_pool_put
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/315.out b/tests/btrfs/315.out
new file mode 100644 (file)
index 0000000..3ea7a35
--- /dev/null
@@ -0,0 +1,10 @@
+QA output created by 315
+---- seed_device_must_fail ----
+mount: SCRATCH_MNT: WARNING: source write-protected, mounted read-only.
+mount: TEST_DIR/315/tempfsid_mnt:  system call failed: File exists.
+---- device_add_must_fail ----
+wrote 9000/9000 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+ERROR: error adding device 'SCRATCH_DEV': Invalid argument
+Balance must be successful
+Done, had to relocate 3 out of 3 chunks
diff --git a/tests/btrfs/316 b/tests/btrfs/316
new file mode 100755 (executable)
index 0000000..63e9a7d
--- /dev/null
@@ -0,0 +1,60 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2024 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 316
+#
+# Make sure btrfs qgroup won't leak its reserved data space if qgroup is
+# marked inconsistent.
+#
+# This exercises a regression introduced in v6.1 kernel by the following commit:
+#
+# e15e9f43c7ca ("btrfs: introduce BTRFS_QGROUP_RUNTIME_FLAG_NO_ACCOUNTING to skip qgroup accounting")
+#
+. ./common/preamble
+_begin_fstest auto quick qgroup
+
+_supported_fs btrfs
+_require_scratch
+_require_qgroup_rescan
+
+_fixed_by_kernel_commit d139ded8b9cd \
+       "btrfs: qgroup: always free reserved space for extent records"
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+$BTRFS_UTIL_PROG quota enable $SCRATCH_MNT
+_qgroup_rescan $SCRATCH_MNT >> $seqres.full
+
+$BTRFS_UTIL_PROG qgroup create 1/0 $SCRATCH_MNT >> $seqres.full
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/subv1 >> $seqres.full
+
+# This would mark qgroup inconsistent, as the snapshot belongs to a different
+# higher level qgroup, we have to do full rescan on both source and snapshot.
+# This can be very slow for large subvolumes, so btrfs only marks qgroup
+# inconsistent and let users to determine when to do a full rescan
+$BTRFS_UTIL_PROG subvolume snapshot -i 1/0 $SCRATCH_MNT/subv1 $SCRATCH_MNT/snap1 >> $seqres.full
+
+# This write would lead to a qgroup extent record holding the reserved 128K.
+# And for unpatched kernels, the reserved space would not be freed properly
+# due to qgroup is inconsistent.
+_pwrite_byte 0xcd 0 128K $SCRATCH_MNT/foobar >> $seqres.full
+
+# The qgroup leak detection is only triggered at unmount time.
+_scratch_unmount
+
+# Check the dmesg warning for data rsv leak.
+#
+# If CONFIG_BTRFS_DEBUG is enabled, we would have a kernel warning with
+# backtrace, but for release builds, it's just a warning line.
+# So here we manually check the warning message.
+if _dmesg_since_test_start | grep -q "leak"; then
+       _fail "qgroup data reserved space leaked"
+fi
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/316.out b/tests/btrfs/316.out
new file mode 100644 (file)
index 0000000..a2867a4
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 316
+Silence is golden
diff --git a/tests/btrfs/317 b/tests/btrfs/317
new file mode 100755 (executable)
index 0000000..b17ba58
--- /dev/null
@@ -0,0 +1,67 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Western Digital Corporation.  All Rights Reserved.
+#
+# FS QA Test 317
+#
+# Test that btrfs convert can ony be run to convert to supported profiles on a
+# zoned filesystem
+#
+. ./common/preamble
+_begin_fstest auto volume raid convert
+
+_fixed_by_kernel_commit 5906333cc4af \
+       "btrfs: zoned: don't skip block group profile checks on conventional zones"
+
+. common/filter.btrfs
+
+_supported_fs btrfs
+_require_scratch_dev_pool 4
+_require_zoned_device "$SCRATCH_DEV"
+
+devs=( $SCRATCH_DEV_POOL )
+
+# Create and mount single device FS
+_scratch_mkfs -msingle -dsingle 2>&1 >> $seqres.full || _fail "mkfs failed"
+_scratch_mount
+
+# Convert FS to metadata/system DUP
+_run_btrfs_balance_start -f -mconvert=dup -sconvert=dup $SCRATCH_MNT 2>&1 |\
+       _filter_balance_convert
+
+# Convert FS to data DUP, must fail
+_run_btrfs_balance_start -dconvert=dup $SCRATCH_MNT 2>&1 |\
+       _filter_balance_convert
+
+# Add device
+$BTRFS_UTIL_PROG device add ${devs[1]} $SCRATCH_MNT | _filter_device_add
+
+# Convert FS to data RAID1, must fail
+_run_btrfs_balance_start -dconvert=raid1 $SCRATCH_MNT 2>&1 |\
+       _filter_balance_convert | head -1
+
+# Convert FS to data RAID0, must fail
+_run_btrfs_balance_start -dconvert=raid0 $SCRATCH_MNT 2>&1 |\
+       _filter_balance_convert | head -1
+
+# Add device
+$BTRFS_UTIL_PROG device add ${devs[2]} $SCRATCH_MNT | _filter_device_add
+
+# Convert FS to data RAID5, must fail
+_run_btrfs_balance_start -f -dconvert=raid5 $SCRATCH_MNT 2>&1 |\
+       _filter_balance_convert | head -1
+
+# Add device
+$BTRFS_UTIL_PROG device add ${devs[3]} $SCRATCH_MNT | _filter_device_add
+
+# Convert FS to data RAID10, must fail
+_run_btrfs_balance_start -dconvert=raid10 $SCRATCH_MNT 2>&1 |\
+       _filter_balance_convert | head -1
+
+# Convert FS to data RAID6, must fail
+_run_btrfs_balance_start -f -dconvert=raid6 $SCRATCH_MNT 2>&1 |\
+       _filter_balance_convert | head -1
+
+# success, all done
+status=0
+exit
diff --git a/tests/btrfs/317.out b/tests/btrfs/317.out
new file mode 100644 (file)
index 0000000..82b00d2
--- /dev/null
@@ -0,0 +1,9 @@
+QA output created by 317
+Done, had to relocate X out of X chunks
+ERROR: error during balancing 'SCRATCH_MNT': Invalid argument
+There may be more info in syslog - try dmesg | tail
+ERROR: error during balancing 'SCRATCH_MNT': Invalid argument
+ERROR: error during balancing 'SCRATCH_MNT': Invalid argument
+ERROR: error during balancing 'SCRATCH_MNT': Invalid argument
+ERROR: error during balancing 'SCRATCH_MNT': Invalid argument
+ERROR: error during balancing 'SCRATCH_MNT': Invalid argument
diff --git a/tests/btrfs/320 b/tests/btrfs/320
new file mode 100755 (executable)
index 0000000..df7acdb
--- /dev/null
@@ -0,0 +1,109 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2021 Facebook.  All Rights Reserved.
+#
+# FS QA Test No. 320
+#
+# Test qgroups to validate the creation works, the counters are sane, rescan
+# works, and we do not get failures when we write less than the limit amount.
+#
+. ./common/preamble
+_begin_fstest auto qgroup limit
+
+# Import common functions.
+. ./common/filter
+
+_supported_fs btrfs
+_require_scratch
+_require_qgroup_rescan
+_require_btrfs_qgroup_report
+_require_scratch_qgroup
+
+# Test to make sure we can actually turn it on and it makes sense
+_basic_test()
+{
+       echo "=== basic test ===" >> $seqres.full
+       _run_btrfs_util_prog subvolume create $SCRATCH_MNT/a
+       _run_btrfs_util_prog quota enable $SCRATCH_MNT/a
+       _qgroup_rescan $SCRATCH_MNT
+       subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT a)
+       $BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep $subvolid >> \
+               $seqres.full 2>&1
+       [ $? -eq 0 ] || _fail "couldn't find our subvols quota group"
+       run_check $FSSTRESS_PROG -d $SCRATCH_MNT/a -w -p 1 -n 2000 \
+               $FSSTRESS_AVOID
+       _run_btrfs_util_prog subvolume snapshot $SCRATCH_MNT/a \
+               $SCRATCH_MNT/b
+
+       # the shared values of both the original subvol and snapshot should
+       # match
+       a_shared=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
+       a_shared=$(echo $a_shared | $AWK_PROG '{ print $2 }')
+       echo "subvol a id=$subvolid" >> $seqres.full
+       subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT b)
+       echo "subvol b id=$subvolid" >> $seqres.full
+       b_shared=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
+       b_shared=$(echo $b_shared | $AWK_PROG '{ print $2 }')
+       $BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT >> $seqres.full
+       [ $b_shared -eq $a_shared ] || _fail "shared values don't match"
+}
+
+#enable quotas, do some work, check our values and then rescan and make sure we
+#come up with the same answer
+_rescan_test()
+{
+       echo "=== rescan test ===" >> $seqres.full
+       # first with a blank subvol
+       _run_btrfs_util_prog subvolume create $SCRATCH_MNT/a
+       _run_btrfs_util_prog quota enable $SCRATCH_MNT/a
+       subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT a)
+       run_check $FSSTRESS_PROG -d $SCRATCH_MNT/a -w -p 1 -n 2000 \
+               $FSSTRESS_AVOID
+       sync
+       output=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
+       echo "qgroup values before rescan: $output" >> $seqres.full
+       refer=$(echo $output | $AWK_PROG '{ print $2 }')
+       excl=$(echo $output | $AWK_PROG '{ print $3 }')
+       _qgroup_rescan $SCRATCH_MNT
+       output=$($BTRFS_UTIL_PROG qgroup show $units $SCRATCH_MNT | grep "0/$subvolid")
+       echo "qgroup values after rescan: $output" >> $seqres.full
+       [ $refer -eq $(echo $output | $AWK_PROG '{ print $2 }') ] || \
+               _fail "reference values don't match after rescan"
+       [ $excl -eq $(echo $output | $AWK_PROG '{ print $3 }') ] || \
+               _fail "exclusive values don't match after rescan"
+}
+
+#basic noexceed limit testing
+_limit_test_noexceed()
+{
+       echo "=== limit not exceed test ===" >> $seqres.full
+       _run_btrfs_util_prog subvolume create $SCRATCH_MNT/a
+       _run_btrfs_util_prog quota enable $SCRATCH_MNT
+       subvolid=$(_btrfs_get_subvolid $SCRATCH_MNT a)
+       _run_btrfs_util_prog qgroup limit 5M 0/$subvolid $SCRATCH_MNT
+       _ddt of=$SCRATCH_MNT/a/file bs=4M count=1 >> $seqres.full 2>&1
+       [ $? -eq 0 ] || _fail "should have been allowed to write"
+}
+
+units=`_btrfs_qgroup_units`
+
+_scratch_mkfs > /dev/null 2>&1
+_scratch_mount
+_basic_test
+_scratch_unmount
+_check_scratch_fs
+
+_scratch_mkfs > /dev/null 2>&1
+_scratch_mount
+_rescan_test
+_scratch_unmount
+_check_scratch_fs
+
+_scratch_mkfs > /dev/null 2>&1
+_scratch_mount
+_limit_test_noexceed
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/btrfs/320.out b/tests/btrfs/320.out
new file mode 100644 (file)
index 0000000..1c4165d
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 320
+Silence is golden
diff --git a/tests/btrfs/330 b/tests/btrfs/330
new file mode 100755 (executable)
index 0000000..095f6b3
--- /dev/null
@@ -0,0 +1,53 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Meta Platforms, Inc.  All Rights Reserved.
+#
+# FS QA Test No. btrfs/330
+#
+# Test mounting one subvolume as ro and another as rw
+#
+. ./common/preamble
+_begin_fstest auto quick subvol
+
+_cleanup()
+{
+       rm -rf $TEST_DIR/$seq
+}
+
+# Import common functions.
+. ./common/filter.btrfs
+
+# real QA test starts here
+_supported_fs btrfs
+_require_scratch
+
+$MOUNT_PROG -V | grep -q 'fd-based-mount'
+[ "$?" -eq 0 ] && _notrun "mount uses the new mount api"
+
+_scratch_mkfs >> $seqres.full 2>&1 || _fail "mkfs failed"
+_scratch_mount
+
+# Create our subvolumes to mount
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/foo | _filter_scratch
+$BTRFS_UTIL_PROG subvolume create $SCRATCH_MNT/bar | _filter_scratch
+
+_scratch_unmount
+
+mkdir -p $TEST_DIR/$seq/foo
+mkdir -p $TEST_DIR/$seq/bar
+
+_mount -t btrfs -o subvol=foo,ro $SCRATCH_DEV $TEST_DIR/$seq/foo
+_mount -t btrfs -o subvol=bar,rw $SCRATCH_DEV $TEST_DIR/$seq/bar
+
+echo "making sure foo is read only"
+touch $TEST_DIR/$seq/foo/baz > /dev/null 2&>1
+ls $TEST_DIR/$seq/foo
+
+echo "making sure bar allows writes"
+touch $TEST_DIR/$seq/bar/qux
+ls $TEST_DIR/$seq/bar
+
+$UMOUNT_PROG $TEST_DIR/$seq/foo
+$UMOUNT_PROG $TEST_DIR/$seq/bar
+
+status=0 ; exit
diff --git a/tests/btrfs/330.out b/tests/btrfs/330.out
new file mode 100644 (file)
index 0000000..4795a2c
--- /dev/null
@@ -0,0 +1,6 @@
+QA output created by 330
+Create subvolume 'SCRATCH_MNT/foo'
+Create subvolume 'SCRATCH_MNT/bar'
+making sure foo is read only
+making sure bar allows writes
+qux
index 1b72a1a16fab8e703534a5f10078d9639b55fdc6..6d9995b4abb2755e55b14c900bca04ea16176afa 100644 (file)
@@ -14,7 +14,7 @@ default: $(DIRT)
 
 include $(BUILDRULES)
 
-install:
+install: default
        $(INSTALL) -m 755 -d $(TARGET_DIR)
        $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
        $(INSTALL) -m 644 group.list $(TARGET_DIR)
index 7970ce352babfd2e69133cec57194e576b56914f..060c4c450091f9af2f45e33b4f5d7092b84838c9 100755 (executable)
@@ -86,11 +86,15 @@ check_copyfrom_metrics()
        local copies=$4
        local c1=$(get_copyfrom_total_copies)
        local s1=$(get_copyfrom_total_size)
+       local hascopyfrom=$(_fs_options $TEST_DEV | grep "copyfrom")
        local sum
 
-       if [ ! -d $metrics_dir ]; then
+       if [ ! -d "$metrics_dir" ]; then
                return # skip metrics check if debugfs isn't mounted
        fi
+       if [ -z "$hascopyfrom" ]; then
+               return # ... or if we don't have copyfrom mount option
+       fi
 
        sum=$(($c0+$copies))
        if [ $sum -ne $c1 ]; then
index f7f1c0ba84876cc392e78e88d607956d2aecdd96..4f766c257a9cea734ba4af33ea3246314d027c04 100644 (file)
@@ -5,4 +5,4 @@ QA output created by 002
 *
 800000 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63 63  >cccccccccccccccc<
 *
-c000000
+c00000
index dbca713e5b1cc8adc56ac3cd4c35738e68bf3704..124ed1bcaf6115767861b4a298dce2394b72fc66 100755 (executable)
@@ -9,7 +9,7 @@
 #
 #    mkdir files limit
 #    truncate files/file -s 10G
-#    setfattr limit -n ceph.quota.max_bytes -v 1000000
+#    setfattr limit -n ceph.quota.max_bytes -v 1048576
 #    mv files limit/
 #
 # Because we're creating a new file and truncating it, we have Fx caps and thus
@@ -76,9 +76,9 @@ check_Fs_caps()
 }
 
 # set quota to 1m
-$SETFATTR_PROG -n ceph.quota.max_bytes -v 1000000 $dest
+$SETFATTR_PROG -n ceph.quota.max_bytes -v 1048576 $dest
 # set quota to 20g
-$SETFATTR_PROG -n ceph.quota.max_bytes -v 20000000000 $orig2
+$SETFATTR_PROG -n ceph.quota.max_bytes -v 21474836480 $orig2
 
 #
 # The following 2 testcases shall fail with either -EXDEV or -EDQUOT
diff --git a/tests/ceph/005 b/tests/ceph/005
new file mode 100755 (executable)
index 0000000..015f657
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 005
+#
+# Make sure statfs reports correct total size when:
+# 1. using a directory with 'max_byte' quota as base for a mount
+# 2. using a subdirectory of the above directory with 'max_files' quota
+#
+. ./common/preamble
+_begin_fstest auto quick quota
+
+_supported_fs ceph
+_require_scratch
+_exclude_test_mount_option "test_dummy_encryption"
+
+_scratch_mount
+mkdir -p "$SCRATCH_MNT/quota-dir/subdir"
+
+# set quota
+quota=$((2 ** 30)) # 1G 
+$SETFATTR_PROG -n ceph.quota.max_bytes -v "$quota" "$SCRATCH_MNT/quota-dir"
+$SETFATTR_PROG -n ceph.quota.max_files -v "$quota" "$SCRATCH_MNT/quota-dir/subdir"
+_scratch_unmount
+
+SCRATCH_DEV_ORIG="$SCRATCH_DEV"
+SCRATCH_DEV="$SCRATCH_DEV/quota-dir" _scratch_mount
+echo ceph quota size is $(_get_total_space "$SCRATCH_MNT") bytes
+SCRATCH_DEV="$SCRATCH_DEV_ORIG/quota-dir" _scratch_unmount
+
+SCRATCH_DEV="$SCRATCH_DEV_ORIG/quota-dir/subdir" _scratch_mount
+echo subdir ceph quota size is $(_get_total_space "$SCRATCH_MNT") bytes
+SCRATCH_DEV="$SCRATCH_DEV_ORIG/quota-dir/subdir" _scratch_unmount
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
diff --git a/tests/ceph/005.out b/tests/ceph/005.out
new file mode 100644 (file)
index 0000000..47798b1
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 005
+ceph quota size is 1073741824 bytes
+subdir ceph quota size is 1073741824 bytes
+Silence is golden
index 2761e1e9971b017e689c6cc7b0f9a3fcd899dc3a..5f24d51803ab82a6b8e07e95c01d50c14004ec43 100644 (file)
@@ -12,7 +12,7 @@ default: $(DIRT)
 
 include $(BUILDRULES)
 
-install:
+install: default
        $(INSTALL) -m 755 -d $(TARGET_DIR)
        $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
        $(INSTALL) -m 644 group.list $(TARGET_DIR)
index 62c489352d739aca357c6ba01071300b2f8465fe..0b89a01d33a67e3f0010308bcf4e7198f7f6cc38 100644 (file)
@@ -14,7 +14,7 @@ default: $(DIRT)
 
 include $(BUILDRULES)
 
-install:
+install: default
        $(INSTALL) -m 755 -d $(TARGET_DIR)
        $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
        $(INSTALL) -m 644 group.list $(TARGET_DIR)
index f1d1e829f7a104fe567de971b1c43944481af252..f865036528fe4decb906de2dfb9ba7b1e7bb1859 100755 (executable)
@@ -8,7 +8,7 @@
 #
 seqfull=$0
 . ./common/preamble
-_begin_fstest auto prealloc quick zero
+_begin_fstest auto prealloc quick zero fiemap
 
 # Import common functions.
 . ./common/filter
index 773bcb03edd9af7a4b75e3f0c0bf0d911e4c8e40..a70ad97a910b1eddd93fc61313b79e005ae4545f 100755 (executable)
@@ -26,8 +26,12 @@ _supported_fs ext4
 _require_scratch
 _require_scratch_ext4_feature "bigalloc"
 
-BLOCK_SIZE=$(get_page_size)
-$MKFS_EXT4_PROG -F -b $BLOCK_SIZE -O bigalloc -C $(($BLOCK_SIZE * 16)) -g 256 $SCRATCH_DEV 512m \
+BLOCK_SIZE=$(_get_page_size)
+features=bigalloc
+if echo "${MOUNT_OPTIONS}" | grep -q 'test_dummy_encryption' ; then
+    features+=",encrypt"
+fi
+$MKFS_EXT4_PROG -F -b $BLOCK_SIZE -O $features -C $(($BLOCK_SIZE * 16)) -g 256 $SCRATCH_DEV 512m \
        >> $seqres.full 2>&1
 _scratch_mount
 
index 48ed6bee967ebe8e5548b987bdf85d4e53c3b897..b73692aa5b8153cd9185d1fef8f470b57a68e22e 100755 (executable)
@@ -130,8 +130,8 @@ cat "$ROUND2_LOG" >> $seqres.full
 echo "++ check fs (2)" >> $seqres.full
 _check_scratch_fs >> $seqres.full 2>&1
 
-egrep -q '(did not fix|makes no progress)' $seqres.full && echo "e2fsck failed" | tee -a $seqres.full
-if [ "$(wc -l < "$ROUND2_LOG")" -ne 8 ]; then
+grep -E -q '(did not fix|makes no progress)' $seqres.full && echo "e2fsck failed" | tee -a $seqres.full
+if [ "$(wc -l < "$ROUND2_LOG")" -ne 7 ]; then
        echo "e2fsck did not fix everything" | tee -a $seqres.full
 fi
 echo "finished fuzzing" | tee -a "$seqres.full"
index 096eb03698fa30010fc84afdb332c025d10ff4a6..4258c486cb787652500819ed9a5ac823367473b4 100755 (executable)
@@ -8,7 +8,7 @@
 # see how the kernel and e2fsck deal with it.
 #
 . ./common/preamble
-_begin_fstest fuzzers
+_begin_fstest fuzzers prealloc
 
 # Override the default cleanup function.
 _cleanup()
index ec7f41596d7b6307908a7fb5a9f665ebe0fd9c13..3c07b5e5c2f1c3fc8eff0633b06fab71cbe951f8 100755 (executable)
@@ -8,7 +8,7 @@
 # see how the kernel and e2fsck deal with it.
 #
 . ./common/preamble
-_begin_fstest fuzzers punch
+_begin_fstest fuzzers punch prealloc
 
 # Override the default cleanup function.
 _cleanup()
index 321050b35cde256908ba1106c466add3e6fe747e..96929cb8aa9ef87868995a70cdc191750b4ed49e 100755 (executable)
@@ -27,7 +27,7 @@ _require_attrs
 
 # Block size
 BLOCK_SIZE=4096
-if [[ $(get_page_size) -ne $BLOCK_SIZE ]]; then
+if [[ $(_get_page_size) -ne $BLOCK_SIZE ]]; then
        _exclude_scratch_mount_option dax
 fi
 # Use large inodes to have enough space for experimentation
index 223c964fe72a9a083ccc4e62c4f7289fc27df7eb..b656e54d83620a68e77e5a41fbdc941a8d70312b 100755 (executable)
@@ -11,7 +11,7 @@
 # "ext4: make sure enough credits are reserved for dioread_nolock writes"
 #
 . ./common/preamble
-_begin_fstest auto quick quota
+_begin_fstest auto quick quota fiemap prealloc
 
 # Import common functions.
 . ./common/filter
index cad3595b782fd711f53f8fa717b5ee3b5a5f2ddb..a8278b0ee500f5915421100fbd1f670ebb4a9151 100755 (executable)
@@ -25,7 +25,14 @@ _require_scratch
 _exclude_scratch_mount_option dax
 _require_command "$RESIZE2FS_PROG" resize2fs
 
-$MKFS_EXT4_PROG -F -b 1024 -E "resize=262144" $SCRATCH_DEV 32768 >> $seqres.full 2>&1
+encrypt=
+if echo "${MOUNT_OPTIONS}" | grep -q 'test_dummy_encryption' ; then
+    encrypt="-O encrypt"
+fi
+$MKFS_EXT4_PROG -F -b 1024 -E "resize=262144" $encrypt $SCRATCH_DEV 32768 >> $seqres.full 2>&1
+if [ $? -ne 0 ]; then
+    _notrun "Can't make file system with a block size of 1024"
+fi
 _scratch_mount
 
 echo "Resizing to 262145 blocks"
index 46e44053d3e4f22d9d80f0f9e5b5d42b70ef1647..96fa70cc0d1e1f7f58ba14602fa8a0f1464cd284 100755 (executable)
@@ -16,8 +16,7 @@ _begin_fstest auto quick
 _supported_fs ext4
 _require_scratch
 _require_test_program "t_get_file_time"
-
-echo "Silence is golden"
+_require_metadata_journaling
 
 echo "Test timestamps with 256 inode size one device $SCRATCH_DEV" >$seqres.full
 _scratch_mkfs -t ext3 -I 256 >> $seqres.full 2>&1
@@ -48,15 +47,16 @@ nsec_mtime: $nsec_mtime, nsec_ctime: $nsec_ctime, cur_time[ns]: $nsec)"
 fi
 
 # Check difference between file time and current time
-_within_tolerance "sec_atime" $sec_atime $sec 1
-_within_tolerance "sec_mtime" $sec_mtime $sec 1
-_within_tolerance "sec_ctime" $sec_ctime $sec 1
+_within_tolerance "sec_atime" $sec_atime $sec 1 -v
+_within_tolerance "sec_mtime" $sec_mtime $sec 1 -v
+_within_tolerance "sec_ctime" $sec_ctime $sec 1 -v
 
 _scratch_unmount >> $seqres.full 2>&1
 
-# Test mount to ext3 then mount back to ext4 and check timestamp again
-_mount -t ext3 `_scratch_mount_options $*` || _fail "ext3 mount failed"
-_scratch_unmount >> $seqres.full 2>&1
+# Test mount to ext3 then mount back to ext4 and check timestamp again.  We
+# ignore if ext3 failed to mount. It can happen because some mount options are
+# incompatible with ext3. Still the test makes sense.
+_mount -t ext3 `_scratch_mount_options $*` >> $seqres.full 2>&1 && _scratch_unmount >> $seqres.full 2>&1
 _scratch_mount
 
 nsec_atime2=`$here/src/t_get_file_time $SCRATCH_MNT/tmp_file atime nsec`
index 12a61dc44583e1dbd1b296d032df8f817203fbce..8a3a80723b7515631fd217722ad8d269f2034919 100644 (file)
@@ -1,2 +1,4 @@
 QA output created by 044
-Silence is golden
+sec_atime is in range
+sec_mtime is in range
+sec_ctime is in range
index ee7c0de3b3066c78d0d2efd293e1d2e58ab33c52..4f0ad4aa719fe8a21d4b0b3566f55dabb790358f 100755 (executable)
@@ -22,7 +22,7 @@ _supported_fs ext4
 _require_scratch
 _require_test_program "t_create_short_dirs"
 _require_test_program "t_create_long_dirs"
-_require_dumpe2fs "$DUMPE2FS_PROG" dumpe2fs
+_require_dumpe2fs
 
 echo "Silence is golden"
 
index 79961957d9d01ae313b463b5501e627e1749e777..6f93b86d3373182b375ae13e5bfa488602ec79dc 100755 (executable)
@@ -22,55 +22,6 @@ _require_command "$DEBUGFS_PROG" debugfs
 checkpoint_journal=$here/src/checkpoint_journal
 _require_test_program "checkpoint_journal"
 
-# convert output from stat<journal_inode> to list of block numbers
-get_journal_extents() {
-       inode_info=$($DEBUGFS_PROG $SCRATCH_DEV -R "stat <8>" 2>> $seqres.full)
-       echo -e "\nJournal info:" >> $seqres.full
-       echo "$inode_info" >> $seqres.full
-
-       extents_line=$(echo "$inode_info" | awk '/EXTENTS:/{ print NR; exit }')
-       get_extents=$(echo "$inode_info" | sed -n "$(($extents_line + 1))"p)
-
-       # get just the physical block numbers
-       get_extents=$(echo "$get_extents" |  perl -pe 's|\(.*?\):||g' | sed -e 's/, /\n/g' | perl -pe 's|(\d+)-(\d+)|\1 \2|g')
-
-       echo "$get_extents"
-}
-
-# checks all extents are zero'd out except for the superblock
-# arg 1: extents (output of get_journal_extents())
-check_extents() {
-       echo -e "\nChecking extents:" >> $seqres.full
-       echo "$1" >> $seqres.full
-
-       super_block="true"
-       echo "$1" | while IFS= read line; do
-               start_block=$(echo $line | cut -f1 -d' ')
-               end_block=$(echo $line | cut -f2 -d' ' -s)
-
-               # if first block of journal, shouldn't be wiped
-               if [ "$super_block" == "true" ]; then
-                       super_block="false"
-
-                       #if super block only block in this extent, skip extent
-                       if [ -z "$end_block" ]; then
-                               continue;
-                       fi
-                       start_block=$(($start_block + 1))
-               fi
-
-               if [ ! -z "$end_block" ]; then
-                       blocks=$(($end_block - $start_block + 1))
-               else
-                       blocks=1
-               fi
-
-               check=$(od $SCRATCH_DEV --skip-bytes=$(($start_block * $blocksize)) --read-bytes=$(($blocks * $blocksize)) -An -v | sed -e 's/[0 \t\n\r]//g')
-
-               [ ! -z "$check" ] && echo "error" && break
-       done
-}
-
 testdir="${SCRATCH_MNT}/testdir"
 
 _scratch_mkfs_sized $((64 * 1024 * 1024)) >> $seqres.full 2>&1
@@ -93,11 +44,12 @@ sync --file-system $testdir/1
 # call ioctl to checkpoint and zero-fill journal blocks
 $checkpoint_journal $SCRATCH_MNT --erase=zeroout || _fail "ioctl returned error"
 
-extents=$(get_journal_extents)
-
 # check journal blocks zeroed out
-ret=$(check_extents "$extents")
-[ "$ret" = "error" ] && _fail "Journal was not zero-filled"
+$DEBUGFS_PROG $SCRATCH_DEV -R "cat <8>" 2> /dev/null | od >> $seqres.full
+check=$($DEBUGFS_PROG $SCRATCH_DEV -R "cat <8>" 2> /dev/null | \
+           od --skip-bytes="$blocksize" -An -v | sed -e '/^[0 \t]*$/d')
+
+[ ! -z "$check" ] && _fail "Journal was not zeroed"
 
 _scratch_unmount >> $seqres.full 2>&1
 
index 187a25154cd60419e81c404c022c83658ccef327..4f20d217d5fd7a6923faeb32423e664b8daf0ccf 100755 (executable)
@@ -439,7 +439,6 @@ for fstype in ext2 ext3 ext4; do
        mnt oldalloc removed
        mnt orlov removed
        mnt -t user_xattr
-       mnt nouser_xattr
 
        if _has_kernel_config CONFIG_EXT4_FS_POSIX_ACL; then
                mnt -t acl
@@ -481,7 +480,6 @@ for fstype in ext2 ext3 ext4; do
        mnt barrier=1 barrier
        mnt barrier=99 barrier
        mnt -t nobarrier
-       mnt i_version
        mnt stripe=512
        only_ext4 mnt delalloc
        only_ext4 mnt -t nodelalloc
@@ -511,20 +509,6 @@ for fstype in ext2 ext3 ext4; do
        mnt noinit_itable
        mnt max_dir_size_kb=4096
 
-       if _has_kernel_config CONFIG_FS_ENCRYPTION; then
-               mnt test_dummy_encryption
-               mnt test_dummy_encryption=v1
-               mnt test_dummy_encryption=v2
-               not_mnt test_dummy_encryption=v3
-               not_mnt test_dummy_encryption=
-       else
-               mnt test_dummy_encryption ^test_dummy_encryption
-               mnt test_dummy_encryption=v1 ^test_dummy_encryption=v1
-               mnt test_dummy_encryption=v2 ^test_dummy_encryption=v2
-               mnt test_dummy_encryption=v3 ^test_dummy_encryption=v3
-               not_mnt test_dummy_encryption=
-       fi
-
        if _has_kernel_config CONFIG_FS_ENCRYPTION_INLINE_CRYPT; then
                mnt inlinecrypt
        else
@@ -686,6 +670,30 @@ for fstype in ext2 ext3 ext4; do
        mnt_then_not_remount defaults jqfmt=vfsv1
        remount defaults grpjquota=,usrjquota= ignored
 
+       echo "== Testing the test_dummy_encryption option" >> $seqres.full
+       # Since kernel commit 5f41fdaea63d ("ext4: only allow
+       # test_dummy_encryption when supported"), the test_dummy_encryption
+       # option is only allowed when the filesystem has the encrypt feature and
+       # the kernel has CONFIG_FS_ENCRYPTION.  The encrypt feature requirement
+       # implies that this option is never allowed on ext2 or ext3 mounts.
+       if [[ $fstype == ext4 ]] && _has_kernel_config CONFIG_FS_ENCRYPTION; then
+               do_mkfs -O encrypt $SCRATCH_DEV ${SIZE}k
+               mnt test_dummy_encryption
+               mnt test_dummy_encryption=v1
+               mnt test_dummy_encryption=v2
+               not_mnt test_dummy_encryption=bad
+               not_mnt test_dummy_encryption=
+               # Can't be set or changed on remount.
+               mnt_then_not_remount defaults test_dummy_encryption
+               mnt_then_not_remount test_dummy_encryption=v1 test_dummy_encryption=v2
+               do_mkfs -O ^encrypt $SCRATCH_DEV ${SIZE}k
+       fi
+       not_mnt test_dummy_encryption
+       not_mnt test_dummy_encryption=v1
+       not_mnt test_dummy_encryption=v2
+       not_mnt test_dummy_encryption=bad
+       not_mnt test_dummy_encryption=
+
 done #for fstype in ext2 ext3 ext4; do
 
 $UMOUNT_PROG $SCRATCH_MNT > /dev/null 2>&1
index 9a11719f854bef09a33f516dc95230fa149652a8..215f564a46a894dc16abbc5d856240685617439f 100755 (executable)
 #    ext4_valid_extent_entries())
 
 . ./common/preamble
-_begin_fstest auto quick dangerous_fuzzers
+_begin_fstest auto quick dangerous_fuzzers prealloc punch
 
 # Import common functions
 . ./common/filter
 
 # real QA test starts here
 _supported_fs ext4
-_require_test
 _require_scratch_nocheck
 _require_xfs_io_command "falloc"
 _require_xfs_io_command "pwrite"
@@ -28,8 +27,8 @@ _require_xfs_io_command "fpunch"
 _require_command "$DEBUGFS_PROG" debugfs
 
 # In order to accurately construct the damaged extent in the following
-# test steps, the blocksize is set to 1024 here
-_scratch_mkfs "-b 1024" > $seqres.full 2>&1
+# test steps, the block size is set to 1024 here
+_scratch_mkfs_blocksized 1024 >> $seqres.full 2>&1
 _scratch_mount
 
 TEST_FILE="${SCRATCH_MNT}/testfile"
index 8f466f1b03b723a2e50117479fc6823cfeb2f451..aa15cfe986b99bd201019a67ce1b13155d3feb1a 100755 (executable)
@@ -26,6 +26,7 @@ _require_command "$DEBUGFS_PROG" debugfs
 echo "Silence is golden"
 
 # The 1K blocksize is designed for debugfs.
+_exclude_scratch_mount_option dax
 _scratch_mkfs "-F -O quota -b 1024" > $seqres.full 2>&1
 
 # Start from 0, fill block 1 with 6,replace the original 2.
diff --git a/tests/ext4/057 b/tests/ext4/057
new file mode 100755 (executable)
index 0000000..6babedb
--- /dev/null
@@ -0,0 +1,64 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Google, Inc. All Rights Reserved.
+#
+# Test the set/get UUID ioctl.
+#
+
+. ./common/preamble
+_begin_fstest auto ioctl
+
+# Override the default cleanup function.
+_cleanup()
+{
+        cd /
+        rm -r -f $tmp.*
+        kill -9 $fsstress_pid 2>/dev/null;
+        wait > /dev/null 2>&1
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_supported_fs ext4
+_require_scratch
+_require_test_program uuid_ioctl
+_require_command $UUIDGEN_PROG uuidgen
+
+UUID_IOCTL=$here/src/uuid_ioctl
+
+# If the ioctl is not supported by the kernel, then skip test.
+current_uuid=$($UUID_IOCTL get $SCRATCH_MNT 2>&1)
+if [[ "$current_uuid" =~ ^Inappropriate[[:space:]]ioctl ]]; then
+        _notrun "UUID ioctls are not supported by kernel."
+fi
+
+# metadata_csum_seed must be set to decouple checksums from the uuid.
+# Otherwise, checksums need to be recomputed when the uuid changes, which
+# is not supported by the ioctl.
+_scratch_mkfs_ext4 -O metadata_csum_seed >> $seqres.full 2>&1
+_scratch_mount
+
+# Begin fsstress while modifying UUID
+fsstress_args=$(_scale_fsstress_args -d $SCRATCH_MNT -p 15 -n 999999)
+$FSSTRESS_PROG $fsstress_args >> $seqres.full &
+fsstress_pid=$!
+
+for n in $(seq 1 20); do
+        new_uuid=$($UUIDGEN_PROG)
+
+        echo "Setting UUID to ${new_uuid}" >> $seqres.full 2>&1
+        $UUID_IOCTL set $SCRATCH_MNT $new_uuid
+
+        current_uuid=$($UUID_IOCTL get $SCRATCH_MNT)
+        echo "$UUID_IOCTL get $SCARTCH_MNT: $current_uuid" >> $seqres.full 2>&1
+        if [[ "$current_uuid" != "$new_uuid" ]]; then
+                echo "Current UUID ($current_uuid) does not equal what was sent with the ioctl ($new_uuid)"
+        fi
+done
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/ext4/057.out b/tests/ext4/057.out
new file mode 100644 (file)
index 0000000..185023c
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 057
+Silence is golden
diff --git a/tests/ext4/058 b/tests/ext4/058
new file mode 100755 (executable)
index 0000000..4704daa
--- /dev/null
@@ -0,0 +1,33 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 HUAWEI.  All Rights Reserved.
+#
+# FS QA Test 058
+#
+# Set 256 blocks in a block group, then inject I/O pressure,
+# it will trigger off kernel BUG in ext4_mb_mark_diskspace_used
+#
+# Regression test for commit
+# a08f789d2ab5 ext4: fix bug_on ext4_mb_use_inode_pa
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+# real QA test starts here
+
+_supported_fs ext4
+_fixed_by_kernel_commit a08f789d2ab5 \
+       "ext4: fix bug_on ext4_mb_use_inode_pa"
+_require_scratch
+
+# set 256 blocks in a block group
+_scratch_mkfs -g 256 >> $seqres.full 2>&1 || _fail "mkfs failed"
+_scratch_mount
+
+$FSSTRESS_PROG -d $SCRATCH_MNT/stress -n 1000 >> $seqres.full 2>&1
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
diff --git a/tests/ext4/058.out b/tests/ext4/058.out
new file mode 100644 (file)
index 0000000..fb5ca60
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 058
+Silence is golden
diff --git a/tests/ext4/059 b/tests/ext4/059
new file mode 100755 (executable)
index 0000000..4230bde
--- /dev/null
@@ -0,0 +1,41 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 HUAWEI.  All Rights Reserved.
+#
+# FS QA Test No. 059
+#
+# A regression test for b55c3cd102a6 ("ext4: add reserved GDT blocks check").
+# Make sure there's not kernel crash, if resize an ext4 which resize_inode
+# feature is disabled but has reserved GDT blocks.
+#
+. ./common/preamble
+_begin_fstest auto resize quick
+
+# real QA test starts here
+_supported_fs ext4
+_fixed_by_kernel_commit b55c3cd102a6 \
+       "ext4: add reserved GDT blocks check"
+
+_require_command "$RESIZE2FS_PROG" resize2fs
+_require_command "$DEBUGFS_PROG" debugfs
+_require_scratch_size_nocheck $((1024 * 1024))
+
+# Initalize a 512M ext4 fs with resize_inode feature disabled
+dev_size=$((512 * 1024 * 1024))
+MKFS_OPTIONS="-O ^resize_inode $MKFS_OPTIONS" _scratch_mkfs_sized $dev_size \
+       >>$seqres.full 2>&1 || _fail "mkfs failed"
+
+# Force some reserved GDT blocks to trigger the bug
+$DEBUGFS_PROG -w -R "set_super_value s_reserved_gdt_blocks 100" $SCRATCH_DEV \
+       >>$seqres.full 2>&1
+$DEBUGFS_PROG -R "show_super_stats -h" $SCRATCH_DEV 2>/dev/null | \
+       grep "Reserved GDT blocks"
+
+_scratch_mount
+
+# Expect no crash from this resize operation
+$RESIZE2FS_PROG $SCRATCH_DEV 1G >> $seqres.full 2>&1
+
+# success, all done
+status=0
+exit
diff --git a/tests/ext4/059.out b/tests/ext4/059.out
new file mode 100644 (file)
index 0000000..d199db3
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 059
+Reserved GDT blocks:      100
index 2ff88537e70c555e6d6bab02dd98ebe930de868a..715732a76ec8758a0017454e11cda685eeb0a570 100755 (executable)
@@ -31,10 +31,13 @@ _require_command "$RESIZE2FS_PROG" resize2fs
 # Make a small ext4 fs with extents disabled & mount it
 features="^extents"
 if grep -q 64bit /etc/mke2fs.conf ; then
-    features="^extents,^64bit"
+    features+=",^64bit"
+fi
+if echo "${MOUNT_OPTIONS}" | grep -q 'test_dummy_encryption' ; then
+    features+=",encrypt"
 fi
 
-blksz=$(get_page_size)
+blksz=$(_get_page_size)
 
 $MKFS_EXT4_PROG -F -b $blksz -O "$features" $SCRATCH_DEV 512m >> $seqres.full 2>&1
 _scratch_mount
index a249213ece0c676b1d8690e9114bb66920535f99..8b1cfc9e4c62abc393dc8e7d5d0e092c22501741 100755 (executable)
@@ -7,7 +7,7 @@
 # Check data integrity during defrag compacting
 #
 . ./common/preamble
-_begin_fstest auto ioctl rw defrag
+_begin_fstest auto ioctl rw defrag prealloc
 
 # Import common functions.
 . ./common/filter
@@ -21,7 +21,7 @@ _workout()
        out=$SCRATCH_MNT/fsstress.$$
        args=`_scale_fsstress_args -p4 -n999 -f setattr=1 $FSSTRESS_AVOID -d $out`
        echo "fsstress $args" >> $seqres.full
-       $FSSTRESS_PROG $args > /dev/null 2>&1
+       $FSSTRESS_PROG $args >> $seqres.full
        find $out -type f > $out.list
        cat $out.list | xargs  md5sum > $out.md5sum
        usage=`du -sch $out | tail -n1 | gawk '{ print $1 }'`
index b88ea056c7934bcccad3da43abf28d7605fb6999..849ebdf89133448bc35d145d49da2f3cbc23defe 100755 (executable)
@@ -9,7 +9,7 @@
 # So if ioctl was performed twice then inode's layout should not change.
 #
 . ./common/preamble
-_begin_fstest auto ioctl rw prealloc quick defrag
+_begin_fstest auto ioctl rw prealloc quick defrag fiemap
 
 PIDS=""
 
index a2a0d56116ea80990f20300104474a7a507b2bdb..296e3850b714f937098370f43c2710442fdc5092 100644 (file)
@@ -14,7 +14,7 @@ default: $(DIRT)
 
 include $(BUILDRULES)
 
-install:
+install: default
        $(INSTALL) -m 755 -d $(TARGET_DIR)
        $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
        $(INSTALL) -m 644 group.list $(TARGET_DIR)
index 1141a63e0061f45d70a24a208b03db2e7497cc0b..2bf39d8c625d6226916c164338bae13ca0bb3ff3 100755 (executable)
@@ -16,7 +16,7 @@
 # In f2fs, up to 3.4KB of data can be embedded into 4KB-sized inode block.
 #
 . ./common/preamble
-_begin_fstest auto quick rw
+_begin_fstest auto quick rw prealloc
 
 # Import common functions.
 . ./common/filter
index 59ca2a2dcd5002be89d9a9cf2bb3c20a69228eab..c0bf440b493828f2a688cc97d2e1d2bcdaec1207 100755 (executable)
@@ -40,7 +40,7 @@
 # just test LZ4.
 
 . ./common/preamble
-_begin_fstest auto quick rw encrypt compress
+_begin_fstest auto quick rw encrypt compress fiemap
 
 . ./common/filter
 . ./common/f2fs
@@ -140,16 +140,16 @@ decrypt_blocks()
 {
        $here/src/fscrypt-crypt-util "$@"                       \
                --decrypt                                       \
-               --block-size=$block_size                        \
+               --data-unit-size=$block_size                    \
                --file-nonce=$nonce                             \
                --kdf=HKDF-SHA512                               \
                AES-256-XTS                                     \
                $TEST_RAW_KEY_HEX
 }
 head -c $num_compressible_bytes $tmp.raw \
-       | decrypt_blocks --block-number=1 > $tmp.decrypted
+       | decrypt_blocks --data-unit-index=1 > $tmp.decrypted
 dd if=$tmp.raw bs=$cluster_bytes skip=$num_compressible_clusters status=none \
-       | decrypt_blocks --block-number=$num_compressible_blocks \
+       | decrypt_blocks --data-unit-index=$num_compressible_blocks \
        >> $tmp.decrypted
 
 # Decompress the compressed clusters using the lz4 command-line tool.
index 9d1ed3c639c1e2a472cbae2aa55500b8a7e4fb75..0a90b4652ea925111dfec7b86d85ef7003f08f4b 100644 (file)
@@ -15,7 +15,7 @@ default: $(DIRT)
 
 include $(BUILDRULES)
 
-install:
+install: default
        $(INSTALL) -m 755 -d $(TARGET_DIR)
        $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
        $(INSTALL) -m 644 group.list $(TARGET_DIR)
index 3b362943d27079324dd0b5cad6a64658edc4f36f..7c9b137f615db6200d2017cbdff12588e9ada00f 100755 (executable)
@@ -7,7 +7,7 @@
 # Test fallocate FALLOC_FL_ZERO_RANGE
 #
 . ./common/preamble
-_begin_fstest auto quick prealloc zero
+_begin_fstest auto quick prealloc zero fiemap
 
 # Import common functions.
 . ./common/filter
index 4b3c69f41d19e2b3df434b505d2edbd6a51ad26e..74e7a02bff8887deb7a9db7ca5a342ffc482ce69 100755 (executable)
@@ -11,7 +11,7 @@
 # For the type of tests, check the description of _test_generic_punch
 # in common/rc.
 . ./common/preamble
-_begin_fstest auto quick prealloc punch collapse
+_begin_fstest auto quick prealloc punch collapse fiemap
 
 # Import common functions.
 # we need to include common/punch to get defination fo filter functions
index 59cb8085b75122d5f60c457dd88d1d997770fe53..833e91618d941d9172eb16d48aa1c40795e79522 100755 (executable)
@@ -11,7 +11,7 @@
 # For the type of tests, check the description of _test_generic_punch
 # in common/rc.
 . ./common/preamble
-_begin_fstest auto quick prealloc punch collapse
+_begin_fstest auto quick prealloc punch collapse fiemap
 
 # Import common functions.
 # we need to include common/punch to get defination fo filter functions
index 4b6acace0e78c4066ec1ed67ae00b19cb9653c36..5f0d85139e5b0a63c836965d91f3feaa6d6276aa 100755 (executable)
@@ -10,7 +10,7 @@
 # Also check for file system consistency after completing this operation
 # for each blocksize.
 . ./common/preamble
-_begin_fstest auto prealloc collapse
+_begin_fstest auto prealloc collapse fiemap
 
 # Import common functions.
 . ./common/filter
@@ -29,7 +29,7 @@ _scratch_mount
 testfile=$SCRATCH_MNT/$seq.$$
 BLOCKS=10240
 
-BSIZE=`_get_block_size $SCRATCH_MNT`
+BSIZE=`_get_file_block_size $SCRATCH_MNT`
 
 length=$(($BLOCKS * $BSIZE))
 
index 45c91624d32ac134e17d83e2d3751209af9dbaf5..b81c1d17ba6549ea2ecea9c0b0f797eae5a4e25b 100755 (executable)
@@ -14,52 +14,23 @@ fio_config=$tmp.fio
 
 # Import common functions.
 . ./common/filter
+. ./common/fail_make_request
 _supported_fs generic
 _require_scratch
 _require_block_device $SCRATCH_DEV
 _require_fail_make_request
 
-SYSFS_BDEV=`_sysfs_dev $SCRATCH_DEV`
-
-allow_fail_make_request()
-{
-    echo "Allow global fail_make_request feature"
-    echo 100 > $DEBUGFS_MNT/fail_make_request/probability
-    echo 9999999 > $DEBUGFS_MNT/fail_make_request/times
-    echo 0 >  /sys/kernel/debug/fail_make_request/verbose
-}
-
-disallow_fail_make_request()
-{
-    echo "Disallow global fail_make_request feature"
-    echo 0 > $DEBUGFS_MNT/fail_make_request/probability
-    echo 0 > $DEBUGFS_MNT/fail_make_request/times
-}
-
-start_fail_scratch_dev()
-{
-    echo "Force SCRATCH_DEV device failure"
-    echo " echo 1 > $SYSFS_BDEV/make-it-fail" >> $seqres.full
-    echo 1 > $SYSFS_BDEV/make-it-fail
-}
-
-stop_fail_scratch_dev()
-{
-    echo "Make SCRATCH_DEV device operable again"
-    echo " echo 0 > $SYSFS_BDEV/make-it-fail" >> $seqres.full
-    echo 0 > $SYSFS_BDEV/make-it-fail
-}
-
 # Override the default cleanup function.
 _cleanup()
 {
        kill $fs_pid $fio_pid &> /dev/null
-       disallow_fail_make_request
+       _disallow_fail_make_request
        cd /
        rm -r -f $tmp.*
 }
 
 RUN_TIME=$((20+10*$TIME_FACTOR))
+test -n "$SOAK_DURATION" && RUN_TIME="$SOAK_DURATION"
 NUM_JOBS=$((4*LOAD_FACTOR))
 BLK_DEV_SIZE=`blockdev --getsz $SCRATCH_DEV`
 FILE_SIZE=$((BLK_DEV_SIZE * 512))
@@ -129,7 +100,7 @@ _workout()
 
        # Let's it work for awhile, and force device failure
        sleep $RUN_TIME
-       start_fail_scratch_dev
+       _start_fail_scratch_dev
        # After device turns in to failed state filesystem may yet not know about
        # that so buffered write(2) may succeed, but any integrity operations
        # such as (sync, fsync, fdatasync, direct-io) should fail.
@@ -147,7 +118,7 @@ _workout()
        run_check _scratch_unmount
        # Once filesystem was umounted no one is able to write to block device
        # It is now safe to bring device back to normal state
-       stop_fail_scratch_dev
+       _stop_fail_scratch_dev
 
        # In order to check that filesystem is able to recover journal on mount(2)
        # perform mount/umount, after that all errors should be fixed
@@ -159,7 +130,7 @@ _workout()
 
 _scratch_mkfs >> $seqres.full 2>&1 || _fail "mkfs failed"
 _scratch_mount
-allow_fail_make_request
+_allow_fail_make_request
 _workout
 status=$?
 exit
index d8648e96286e1b0491b05ed314b1446a5d168411..4951b9e376248c6f52bb4cd9d4b71c62bbf6ca47 100755 (executable)
@@ -51,16 +51,12 @@ _attr_list()
     fi
 }
 
-# set fs-specific max_attrs and max_attrval_size values. The parameter
-# @max_attrval_namelen is required for filesystems which take into account attr
-# name lengths (including namespace prefix) when determining limits.
+# set fs-specific max_attrs
 _attr_get_max()
 {
-       local max_attrval_namelen="$1"
-
        # set maximum total attr space based on fs type
        case "$FSTYP" in
-       xfs|udf|pvfs2|9p|ceph|nfs)
+       xfs|udf|pvfs2|9p|ceph|fuse|nfs|ceph-fuse)
                max_attrs=1000
                ;;
        ext2|ext3|ext4)
@@ -105,6 +101,12 @@ _attr_get_max()
                        let max_attrs=$((($BLOCK_SIZE - 32) / (16 + 12 + 16 )))
                fi
                ;;
+       ubifs)
+               LEB_SIZE=`_get_leb_size $TEST_DEV`
+               # On UBIFS, the number of xattrs has to be less than 50% LEB size
+               # divided by 160 (inode size)
+               let max_attrs=$((($LEB_SIZE / 2 / 160) - 1))
+               ;;
        *)
                # Assume max ~1 block of attrs
                BLOCK_SIZE=`_get_block_size $TEST_DIR`
@@ -112,6 +114,16 @@ _attr_get_max()
                # overhead
                let max_attrs=$BLOCK_SIZE/40
        esac
+}
+
+# set fs-specific max_attrval_size values. The parameter @max_attrval_namelen is
+# required for filesystems which take into account attr name lengths (including
+# namespace prefix) when determining limits; parameter @filename is required for
+# filesystems that need to take into account already existing attrs.
+_attr_get_maxval_size()
+{
+       local max_attrval_namelen="$1"
+       local filename="$2"
 
        # Set max attr value size in bytes based on fs type
        case "$FSTYP" in
@@ -128,7 +140,7 @@ _attr_get_max()
        pvfs2)
                max_attrval_size=8192
                ;;
-       xfs|udf|9p|ceph)
+       xfs|udf|9p|fuse)
                max_attrval_size=65536
                ;;
        bcachefs)
@@ -139,6 +151,23 @@ _attr_get_max()
                # the underlying filesystem, so just use the lowest value above.
                max_attrval_size=1024
                ;;
+       ceph)
+               # CephFS does not have a maximum value for attributes.  Instead,
+               # it imposes a maximum size for the full set of xattrs
+               # names+values, which by default is 64K.  Compute the maximum
+               # taking into account the already existing attributes
+               local size=$(getfattr --dump -e hex $filename 2>/dev/null | \
+                       awk -F "=0x" '/^user/ {len += length($1) + length($2) / 2} END {print len}')
+               local selinux_size=$(getfattr -n 'security.selinux' --dump -e hex $filename 2>/dev/null | \
+                       awk -F "=0x" '/^security/ {len += length($1) + length($2) / 2} END {print len}')
+               if [ -z $size ]; then
+                       size=0
+               fi
+               if [ -z $selinux_size ]; then
+                       selinux_size=0
+               fi
+               max_attrval_size=$((65536 - $size - $selinux_size - $max_attrval_namelen))
+               ;;
        *)
                # Assume max ~1 block of attrs
                BLOCK_SIZE=`_get_block_size $TEST_DIR`
@@ -181,8 +210,7 @@ echo "*** remove attribute"
 _attr -r fish $testfile
 _attr_list $testfile
 
-max_attrval_name="long_attr"   # add 5 for "user." prefix
-_attr_get_max "$(( 5 + ${#max_attrval_name} ))"
+_attr_get_max
 
 echo "*** add lots of attributes"
 v=0
@@ -226,6 +254,9 @@ done
 _attr_list $testfile
 
 echo "*** really long value"
+max_attrval_name="long_attr"   # add 5 for "user." prefix
+_attr_get_maxval_size "$(( 5 + ${#max_attrval_name} ))" "$testfile"
+
 dd if=/dev/zero bs=1 count=$max_attrval_size 2>/dev/null \
     | _attr -s "$max_attrval_name" $testfile >/dev/null
 
index 7407bf03e5b88dbdcf92cc34de548f37587f80c3..532feeeb7e5b0186279b27954955343448ddc4b2 100755 (executable)
@@ -11,7 +11,7 @@
 # For the type of tests, check the description of _test_generic_punch
 # in common/rc.
 . ./common/preamble
-_begin_fstest auto quick prealloc punch collapse
+_begin_fstest auto quick prealloc punch collapse fiemap
 
 # Import common functions.
 # we need to include common/punch to get defination fo filter functions
index b983c5d0603fec215bca9ebab242bd1020ed4107..62577b81084d0ec2f1244c563402e1bc66e864e3 100755 (executable)
@@ -11,7 +11,7 @@
 # For the type of tests, check the description of _test_generic_punch
 # in common/rc.
 . ./common/preamble
-_begin_fstest auto quick prealloc punch collapse
+_begin_fstest auto quick prealloc punch collapse fiemap
 
 # Import common functions.
 # we need to include common/punch to get defination fo filter functions
index cbb2fc341771343f6b29bf358e2fa6fe760bc911..0d2e826812787230bbffa8d30775c695857b6261 100755 (executable)
@@ -25,6 +25,7 @@ testfile=$SCRATCH_MNT/testfile
 
 _scratch_mkfs > /dev/null 2>&1
 _scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 4096
 
 $XFS_IO_PROG -f \
        -c "pwrite 185332 55756" \
index 3302c1eebe916357cac861153add2acd0c811c1e..c006a591709c8c01b8f1d3c5c2262c04bd75b269 100755 (executable)
@@ -11,7 +11,7 @@
 # are always read back as zeroes.
 #
 . ./common/preamble
-_begin_fstest auto quick rw
+_begin_fstest auto quick rw fiemap prealloc
 
 # Override the default cleanup function.
 _cleanup()
index c6cea94e345bb479a36ff9e503f4f666710a8aef..e1176292fbdcf9c1eb9118a61d6b17bc69b473f2 100755 (executable)
 # disk's image file is performed by the host).
 #
 . ./common/preamble
-_begin_fstest auto stress trim
+_begin_fstest auto stress trim prealloc
 
 # Override the default cleanup function.
 _cleanup()
 {
+       [ -n "${create_pids}" ] && kill ${create_pids[@]}
+       [ -n "${fallocate_pids}" ] && kill ${fallocate_pids[@]}
+       [ -n "${trim_pids}" ] && kill ${trim_pids[@]}
+       wait
        rm -fr $tmp
 }
 
@@ -47,6 +51,8 @@ _supported_fs generic
 _require_scratch
 _require_xfs_io_command "falloc"
 
+echo "Silence is golden"
+
 # Keep allocating and deallocating 1G of data space with the goal of creating
 # and deleting 1 block group constantly. The intention is to race with the
 # fstrim loop below.
@@ -94,6 +100,8 @@ nr_files=$((50000 * LOAD_FACTOR))
 create_files()
 {
        local prefix=$1
+       local now=$(date '+%s')
+       local end_time=$(( now + (TIME_FACTOR * 30) ))
 
        for ((n = 0; n < 4; n++)); do
                mkdir $SCRATCH_MNT/$n
@@ -107,13 +115,17 @@ create_files()
                                echo "Failed creating file $n/${prefix}_$i" >>$seqres.full
                                break
                        fi
+                       if [ "$(date '+%s')" -ge $end_time ]; then
+                               echo "runtime exceeded @ $i files" >> $seqres.full
+                               break
+                       fi
                done
                ) &
                create_pids[$n]=$!
        done
 
        wait ${create_pids[@]}
-
+       unset create_pids
 }
 
 _scratch_mkfs >>$seqres.full 2>&1
@@ -136,12 +148,8 @@ create_files "foobar"
 kill ${fallocate_pids[@]}
 kill ${trim_pids[@]}
 wait
+unset fallocate_pids
+unset trim_pids
 
-# The fstests framework will now check for fs consistency with fsck.
-# The trimming was racy and caused some btree nodes to get full of zeroes on
-# disk, which obviously caused fs metadata corruption. The race often lead
-# to missing free space entries in a block group's free space cache too.
-
-echo "Silence is golden"
 status=0
 exit
index dbc65e338f003263fb74d9c281b471e91adfc7bc..63a46d6b2b84ca29a73954f00c85d5f1d693b4a0 100755 (executable)
@@ -13,7 +13,7 @@
 # stale data exposure can occur.
 #
 . ./common/preamble
-_begin_fstest shutdown rw punch zero
+_begin_fstest shutdown rw punch zero prealloc auto quick
 
 # Import common functions.
 . ./common/filter
@@ -27,14 +27,7 @@ _crashtest()
        img=$SCRATCH_MNT/$seq.img
        mnt=$SCRATCH_MNT/$seq.mnt
        file=$mnt/file
-       size=25M
-
-       # f2fs-utils 1.9.0 needs at least 38 MB space for f2fs image. However,
-       # f2fs-utils 1.14.0 needs at least 52 MB. Not sure if it will change
-       # again. So just set it 128M.
-       if [ $FSTYP == "f2fs" ]; then
-               size=128M
-       fi
+       size=$(_small_fs_size_mb 25)M
 
        # Create an fs on a small, initialized image. The pattern is written to
        # the image to detect stale data exposure.
index b1432e258a8de4bfece4d9d216a343f9d4fe6cba..d5ca438a807afd1027e93452e62b64a57dc272eb 100755 (executable)
@@ -7,7 +7,7 @@
 # Test for NULL files problem
 #
 . ./common/preamble
-_begin_fstest shutdown metadata log auto
+_begin_fstest shutdown metadata log auto fiemap
 
 # Import common functions.
 . ./common/filter
index 56c50548c91fa63bf0eacfa8cdf81ee9831c66ab..a5d3e9edc546f1704ede5ce7665cc8f2468d5230 100755 (executable)
@@ -7,7 +7,7 @@
 # Test for NULL files problem
 #
 . ./common/preamble
-_begin_fstest shutdown metadata log auto
+_begin_fstest shutdown metadata log auto fiemap
 
 # Import common functions.
 . ./common/filter
index f5f36a7a08a03830cf1a877836713fee7667c086..dfbcaeb42cca943ee19a36f8ae7bd5e86fb586ec 100755 (executable)
@@ -7,7 +7,7 @@
 # Test for NULL files problem
 #
 . ./common/preamble
-_begin_fstest shutdown metadata log auto
+_begin_fstest shutdown metadata log auto fiemap
 
 # Import common functions.
 . ./common/filter
index 5dbb8fa5b6440565a3eb3639ba572170bbf8adcc..29165e4509d9966d4e1b2e3b0c61084d53ce8093 100755 (executable)
@@ -7,7 +7,7 @@
 # Test for NULL files problem
 #
 . ./common/preamble
-_begin_fstest shutdown metadata log auto
+_begin_fstest shutdown metadata log auto fiemap
 
 # Import common functions.
 . ./common/filter
index 770e7f1b102164d7530f3be2253dea3f25e3e8a8..61590e9b53ee3b0e9e9eca50f73261cfd39f8ae0 100755 (executable)
@@ -8,7 +8,7 @@
 # test inode size is on disk after fsync
 #
 . ./common/preamble
-_begin_fstest shutdown metadata rw auto
+_begin_fstest shutdown metadata rw auto fiemap
 
 # Import common functions.
 . ./common/filter
index 10c87f3b2a35a458c82c348acd0e05011779e227..ebe9132e50370ca95a33226aab9ded8c104463c8 100755 (executable)
@@ -8,7 +8,7 @@
 # test inode size is on disk after sync
 #
 . ./common/preamble
-_begin_fstest shutdown metadata rw auto
+_begin_fstest shutdown metadata rw auto fiemap
 
 # Import common functions.
 . ./common/filter
index 4effc4a64e56d65537542ab4ce74b39cbf26a9a3..e5c5a0e3078f1e35678e03a482af096ce14492ab 100755 (executable)
@@ -8,7 +8,7 @@
 # test inode size is on disk after sync - expose log replay bug
 #
 . ./common/preamble
-_begin_fstest shutdown metadata rw auto
+_begin_fstest shutdown metadata rw auto fiemap
 
 # Import common functions.
 . ./common/filter
index cb685ffb448082d71abc5939a173d7995d2cff81..dddadbe08703572ca46fd8d90adf8e6fe75c8403 100755 (executable)
@@ -11,7 +11,7 @@
 # For the type of tests, check the description of _test_generic_punch
 # in common/rc.
 . ./common/preamble
-_begin_fstest auto quick prealloc punch insert
+_begin_fstest auto quick prealloc punch insert fiemap
 
 # Import common functions.
 # we need to include common/punch to get defination fo filter functions
index 0fd4278519011c85eaa97b1f881e1b21fa7ab6d4..3a890ed03d15c2ff55941fa8d43d1de05ae8fbd0 100755 (executable)
@@ -11,7 +11,7 @@
 # For the type of tests, check the description of _test_generic_punch
 # in common/rc.
 . ./common/preamble
-_begin_fstest auto quick prealloc punch insert
+_begin_fstest auto quick prealloc punch insert fiemap
 
 # Import common functions.
 # we need to include common/punch to get defination fo filter functions
index c4998b938611da94f55299b393388014bd35b9ea..e370ffdfb8fd1528871ae97d8624bea99dbed144 100755 (executable)
@@ -11,7 +11,7 @@
 # For the type of tests, check the description of _test_generic_punch
 # in common/rc.
 . ./common/preamble
-_begin_fstest auto quick prealloc punch insert
+_begin_fstest auto quick prealloc punch insert fiemap
 
 # Import common functions.
 # we need to include common/punch to get defination fo filter functions
index 60a5f242dbb90ce826fa76b6b998651524e62878..3974647b9e151cd6f4f85989fddc4cf92afdd493 100755 (executable)
@@ -11,7 +11,7 @@
 # For the type of tests, check the description of _test_generic_punch
 # in common/rc.
 . ./common/preamble
-_begin_fstest auto quick prealloc punch insert
+_begin_fstest auto quick prealloc punch insert fiemap
 
 # Import common functions.
 # we need to include common/punch to get defination fo filter functions
index b7d7fa4b3aa9746b32d3abc6ef1e580ff88499e7..b50c55e7ee78a8a5165b72d6599511998f836d32 100755 (executable)
@@ -10,7 +10,7 @@
 # on the previously inserted ranges to test merge code of collapse
 # range. Also check for data integrity and file system consistency.
 . ./common/preamble
-_begin_fstest auto quick prealloc collapse insert
+_begin_fstest auto quick prealloc collapse insert fiemap
 
 # Import common functions.
 . ./common/filter
@@ -29,7 +29,7 @@ _scratch_mount
 src=$SCRATCH_MNT/testfile
 dest=$SCRATCH_MNT/testfile.dest
 BLOCKS=100
-BSIZE=`_get_block_size $SCRATCH_MNT`
+BSIZE=`_get_file_block_size $SCRATCH_MNT`
 length=$(($BLOCKS * $BSIZE))
 
 # Write file
index 56262cd7a646168632c83ce0f1e4ccd08b52ee91..af527fee2b5db0ec741cbb89bcec9157548c55c7 100755 (executable)
@@ -17,9 +17,12 @@ ITERATIONS=10
 # Override the default cleanup function.
 _cleanup()
 {
-    cd /
-
-    trap 0 1 2 3 15
+       # Make sure $SCRATCH_MNT is unfreezed
+       xfs_freeze -u $SCRATCH_MNT 2>/dev/null
+       [ -n "$pid" ] && kill -9 $pid 2>/dev/null
+       wait $pid
+       cd /
+       rm -f $tmp.*
 }
 
 # Import common functions.
@@ -54,12 +57,13 @@ touch $tmp.running
       # We do both read & write IO - not only is this more realistic,
       # but it also potentially tests atime updates
       FSSTRESS_ARGS=`_scale_fsstress_args -d $STRESS_DIR -p $procs -n $nops $FSSTRESS_AVOID`
-      $FSSTRESS_PROG $FSSTRESS_ARGS > /dev/null 2>&1
+      $FSSTRESS_PROG $FSSTRESS_ARGS >>$seqres.full
     done
 
     rm -r $STRESS_DIR/*
     rmdir $STRESS_DIR
 } &
+pid=$!
 
 # start fstest -m loop in a background block; this gets us mmap coverage
 {
@@ -75,6 +79,7 @@ touch $tmp.running
     rm -rf $FSTEST_DIR/*
     rmdir $FSTEST_DIR
 } &
+pid="$pid $!"
 
 i=0
 let ITERATIONS=$ITERATIONS-1
@@ -103,6 +108,7 @@ done
 rm $tmp.running
 
 # wait for fsstresses to finish
-wait
+wait $pid
+unset pid
 
 exit 1
index 22ac94de538304b44aebd235115e48289754181f..0996f221d3804b2b0c3330d221ad235b73d7288b 100755 (executable)
@@ -62,13 +62,16 @@ snapname=snap_$seq
 mnt=$TEST_DIR/mnt_$seq
 mkdir -p $mnt
 
+size=$(_small_fs_size_mb 300)
+lvsize=$((size * 85 / 100))     # ~256M
+
 # make sure there's enough disk space for 256M lv, test for 300M here in case
 # lvm uses some space for metadata
-_scratch_mkfs_sized $((300 * 1024 * 1024)) >>$seqres.full 2>&1
+_scratch_mkfs_sized $((size * 1024 * 1024)) >>$seqres.full 2>&1
 $LVM_PROG vgcreate -f $vgname $SCRATCH_DEV >>$seqres.full 2>&1
 # We use yes pipe instead of 'lvcreate --yes' because old version of lvm
 # (like 2.02.95 in RHEL6) don't support --yes option
-yes | $LVM_PROG lvcreate -L 256M -n $lvname $vgname >>$seqres.full 2>&1
+yes | $LVM_PROG lvcreate -L ${lvsize}M -n $lvname $vgname >>$seqres.full 2>&1
 # wait for lvcreation to fully complete
 $UDEV_SETTLE_PROG >>$seqres.full 2>&1
 
index 20cf875a27ec621b581dd5a643257ba1bc3b0700..786d8e6fab162d66cd8790ee9158fb99a5dd2ee5 100755 (executable)
@@ -25,6 +25,8 @@ cleanup_dmdev()
 {
        # in case it's still suspended and/or mounted
        $DMSETUP_PROG resume $lvdev >/dev/null 2>&1
+       [ -n "$pid" ] && kill -9 $pid 2>/dev/null
+       wait $pid
        $UMOUNT_PROG $lvdev >/dev/null 2>&1
        _dmsetup_remove $node
 }
@@ -75,6 +77,7 @@ done &
 pid="$pid $!"
 
 wait $pid
+unset pid
 
 status=0
 exit
index 505e0ec84f6f65013a362a3d9d4ef2bf8021a4ed..bdd8feddf0794f98a7b718a20d92db49ae29cc01 100755 (executable)
@@ -12,7 +12,7 @@
 # preallocated space.
 #
 . ./common/preamble
-_begin_fstest auto quick prealloc
+_begin_fstest auto quick prealloc fiemap
 
 status=0       # success is the default!
 
@@ -28,6 +28,12 @@ _require_test
 _require_xfs_io_command "falloc"
 _require_xfs_io_command "fiemap"
 
+# If the truncation sizes (5M/7M) aren't aligned with the file allocation unit
+# length, then the FIEMAP output will show blocks beyond EOF.  That will cause
+# trouble with the golden output, so skip this test if that will be the case.
+_require_congruent_file_oplen $TEST_DIR $((5 * 1048576))
+_require_congruent_file_oplen $TEST_DIR $((7 * 1048576))
+
 # First test to make sure that truncating at i_size trims the preallocated bit
 # past i_size
 $XFS_IO_PROG -f -c "falloc -k 0 10M" -c "pwrite 0 5M" -c "truncate 5M"\
index 7a078a88dc4539b4b8995e473af7e2c8589d0f67..0d9ce8b6ee766d06238e51647dbcca7fffe80d54 100755 (executable)
@@ -7,7 +7,7 @@
 # Run the fiemap (file extent mapping) tester with preallocation enabled
 #
 . ./common/preamble
-_begin_fstest auto quick prealloc
+_begin_fstest auto quick prealloc fiemap
 
 # Import common functions.
 . ./common/filter
@@ -26,11 +26,6 @@ fiemapfile=$SCRATCH_MNT/$seq.fiemap
 
 _require_test_program "fiemap-tester"
 
-# FIEMAP test doesn't like finding unwritten blocks after it punches out
-# a partial rt extent.
-test "$FSTYP" = "xfs" && \
-       _require_file_block_size_equals_fs_block_size $SCRATCH_MNT
-
 seed=`date +%s`
 
 echo "using seed $seed" >> $seqres.full
index 5cdac9abc42a8908cae06ff2b3039707be25d9f7..eab6f4723b7edd8d67d125e8a0b049abf2e197c8 100755 (executable)
@@ -8,7 +8,7 @@
 #
 #!/bin/bash
 . ./common/preamble
-_begin_fstest acl auto quick
+_begin_fstest acl auto quick perms
 
 # Import common functions.
 . ./common/filter
index 4efa1dc366f99afa3de7bab120119d4d92ac090b..fd650ec9002f1c8ff1887e391ba46857234a273f 100755 (executable)
@@ -11,7 +11,7 @@
 # ENOSPC.
 #
 . ./common/preamble
-_begin_fstest auto quick attr enospc
+_begin_fstest auto quick attr enospc prealloc
 
 _register_cleanup "_cleanup" 25
 
index b422ea349344492949c12181a7cd6fb4b7081e3d..a05219df0967a4c0567383799b277d7c0436cb7e 100755 (executable)
@@ -10,7 +10,7 @@
 #   which pulls out an earlier mod
 #
 . ./common/preamble
-_begin_fstest acl auto quick
+_begin_fstest acl auto quick perms
 
 # Override the default cleanup function.
 _cleanup()
index efe66ba57f1ce9e09c73d88b92de508285bbde43..07703fc8f1e446cdfb0dd602d33c44863d07dfee 100755 (executable)
@@ -44,9 +44,11 @@ vgname=vg_$seq
 
 physical=`blockdev --getpbsz $SCRATCH_DEV`
 logical=`blockdev --getss $SCRATCH_DEV`
+size=$(_small_fs_size_mb 300)
+lvsize=$((size * 91 / 100))
 
 # _get_scsi_debug_dev returns a scsi debug device with 128M in size by default
-SCSI_DEBUG_DEV=`_get_scsi_debug_dev ${physical:-512} ${logical:-512} 0 300`
+SCSI_DEBUG_DEV=`_get_scsi_debug_dev ${physical:-512} ${logical:-512} 0 $size`
 test -b "$SCSI_DEBUG_DEV" || _notrun "Failed to initialize scsi debug device"
 echo "SCSI debug device $SCSI_DEBUG_DEV" >>$seqres.full
 
@@ -55,7 +57,7 @@ $LVM_PROG pvcreate -f $SCSI_DEBUG_DEV $SCRATCH_DEV >>$seqres.full 2>&1
 $LVM_PROG vgcreate -f $vgname $SCSI_DEBUG_DEV $SCRATCH_DEV >>$seqres.full 2>&1
 # We use yes pipe instead of 'lvcreate --yes' because old version of lvm
 # (like 2.02.95 in RHEL6) don't support --yes option
-yes | $LVM_PROG lvcreate -i 2 -I 4m -L 275m -n $lvname $vgname \
+yes | $LVM_PROG lvcreate -i 2 -I 4m -L ${lvsize}m -n $lvname $vgname \
        >>$seqres.full 2>&1
 # wait for lv creation to fully complete
 $UDEV_SETTLE_PROG >>$seqres.full 2>&1
index 5d6d6ecb969995a879bbe0318b03913b8a66ccba..ef5ac5f6d53dabcd810e7495b2abda3ea3642c59 100755 (executable)
@@ -11,7 +11,7 @@
 #   - Modify the reflinked file
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Override the default cleanup function.
 _cleanup()
index 3e376096f1cd8118c43e2c2208e1869610b34432..5df0fa80c8bf0e9b0784be375684ab0f0444f51c 100755 (executable)
@@ -12,7 +12,7 @@
 #   - Modify one of the copies
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Override the default cleanup function.
 _cleanup()
index daaca523723a258ff0ffd0357357f38827472b33..13d4b537308fe06347e845695d681248a92a784d 100755 (executable)
@@ -10,7 +10,7 @@
 #   - Delete the original (moved) file, check that the copy still exists.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Override the default cleanup function.
 _cleanup()
index b8816e311d76f05405aeed3b4c849b1c2741c403..88b64f4c342934a1481ccdc075a8bf9bf590aead 100755 (executable)
@@ -31,6 +31,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
 _pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file1 >> $seqres.full
 _pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file2 >> $seqres.full
 _test_cycle_mount
index 4fa2e1e3e7dec13d48b1f60a90541e15b1e2b5c1..35d933ffd013091c0660736f41fb0cee0312aff8 100755 (executable)
@@ -32,6 +32,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
 _pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file1 >> $seqres.full
 _pwrite_byte 0x62 $((blksz * 2)) $((blksz * 6)) $testdir/file2 >> $seqres.full
 _test_cycle_mount
index fd4c366171368abd38e093dbf102444153a0ce4e..481d12d2b8aa85fad751df57e7870a1b32982e91 100755 (executable)
@@ -34,6 +34,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
 _pwrite_byte 0x61 0 $((blksz * 8)) $testdir/file1 >> $seqres.full
 _pwrite_byte 0x62 0 $((blksz * 8)) $testdir/file2 >> $seqres.full
 _pwrite_byte 0x63 0 $((blksz * 8)) $testdir/file3 >> $seqres.full
index 43137469438c2c2bc6b16d1edcf7661e34ea9baa..e9038240a30e9cca2679814c47dd2c7dbf6363de 100755 (executable)
@@ -31,6 +31,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
 _pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file1 >> $seqres.full
 _pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file2 >> $seqres.full
 _test_cycle_mount
index fbf3f1f2378acd23d443f489db6ce1f47059bc01..89309c22af3f14deb01d4de7fc5ecf73b1d8da94 100755 (executable)
@@ -31,6 +31,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
 _pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file1 >> $seqres.full
 _pwrite_byte 0x62 $((blksz * 2)) $((blksz * 6)) $testdir/file2 >> $seqres.full
 _test_cycle_mount
index f9b28abb8b91980414fc104f9895d29e3e5b0173..43f90b46e85ced4e5365bffdc82a11134188483e 100755 (executable)
@@ -28,6 +28,7 @@ _supported_fs generic
 
 _require_test
 _require_user
+_require_unix_perm_checking
 
 my_test_subdir=$TEST_DIR/123subdir
 
index 077895d44c11d490d1fc0e272c43cf657441f88b..0d8e61a11da1e4141c3a090c7b2da46c55583f1f 100755 (executable)
@@ -7,7 +7,7 @@
 # ftruncate test, modified from CXFSQA tests cxfs_ftrunc and cxfs_trunc
 #
 . ./common/preamble
-_begin_fstest other pnfs
+_begin_fstest other pnfs auto
 
 # Import common functions.
 . ./common/filter
index dc1d43f4413472639fb0326f2a5930fc4a54b580..924d6aa834d7d4aba4d1f967897fd0722d4ef531 100755 (executable)
@@ -18,6 +18,7 @@ _supported_fs generic
 _require_scratch
 _require_user
 _require_chmod
+_require_unix_perm_checking
 
 _scratch_mkfs >/dev/null 2>&1
 _scratch_mount "-o nosuid"
index ab76f0460f16cfc68aed5e14901f3f8743d6ac9d..58b81872bf151ed63a4e3f4f94ba4c7a63dbb0f1 100755 (executable)
@@ -35,6 +35,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
 _pwrite_byte 0x61 0 $((blksz + 37)) $testdir/file1 >> $seqres.full
 _pwrite_byte 0x61 0 $((blksz + 37)) $testdir/file2 >> $seqres.full
 _pwrite_byte 0x62 0 $((blksz + 37)) $testdir/file3 >> $seqres.full
index 98ebb0da2d82abc227fdd9a4b3db084bae556f3d..c5b80074f11fdeb77bce9268b281bfa609b221aa 100755 (executable)
@@ -35,6 +35,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
 _pwrite_byte 0x61 0 $((blksz + 37)) $testdir/file1 >> $seqres.full
 _pwrite_byte 0x61 0 $((blksz + 37)) $testdir/file2 >> $seqres.full
 _pwrite_byte 0x62 0 $((blksz + 37)) $testdir/file3 >> $seqres.full
index fb0071b10605882c4ad460b9e3537a0890430206..18644d9d783c9c4f283846ce8a21b75cb689fecd 100755 (executable)
@@ -13,7 +13,7 @@
 #     extents, and non-matches; but actually dedupe real matches.
 #
 . ./common/preamble
-_begin_fstest auto clone dedupe
+_begin_fstest auto clone dedupe prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -37,6 +37,7 @@ mkdir $testdir
 
 echo "Create the original file blocks"
 blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
 _pwrite_byte 0x61 0 $blksz $testdir/file1 >> $seqres.full
 _pwrite_byte 0x62 $blksz $((blksz * 2)) $testdir/file1 >> $seqres.full
 
index 0bbc222ca3d5b06cb3fb5bdc6f829a74617b2284..3eb1519d8718012b556c40a599af4bc2db42c0b2 100755 (executable)
@@ -26,7 +26,7 @@ _cleanup()
 # real QA test starts here
 _require_test_reflink
 _require_cp_reflink
-_require_odirect
+_require_odirect 512
 
 testdir=$TEST_DIR/test-$seq
 rm -rf $testdir
index 842d51f3ceb713b4568106e8e75e286b3af45dfc..4daaeae01b838e2716e266de5df79fb54e5f4ce4 100755 (executable)
@@ -10,7 +10,7 @@
 #   - Check that the reflinked areas are still there.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -35,6 +35,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
 _pwrite_byte 0x61 0 $((blksz * 5 + 37)) $testdir/file1 >> $seqres.full
 
 _reflink_range $testdir/file1 $blksz $testdir/file2 $blksz \
index 0d5454387375524b917bc5510c58ffcf82bda579..f213f53be840bff83e9404ca82c14514ec074e81 100755 (executable)
@@ -11,7 +11,7 @@
 #   - Check that the reflinked areas are still there.
 #
 . ./common/preamble
-_begin_fstest auto quick clone collapse
+_begin_fstest auto quick clone collapse prealloc
 
 # Override the default cleanup function.
 _cleanup()
index 5343a139211053b5356fd964efa24f0464b7cb3a..108f1368b12da6b80acccba8f3209afa9b999a8a 100755 (executable)
@@ -35,6 +35,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
 _pwrite_byte 0x61 0 $blksz $testdir/file1 >> $seqres.full
 _pwrite_byte 0x62 $blksz $blksz $testdir/file1 >> $seqres.full
 _pwrite_byte 0x63 $((blksz * 2)) $blksz $testdir/file1 >> $seqres.full
index 4087726618329a55aa95602136d25a2325f09c60..342959fd37948e1bae0e3da00f3ac40c7c57a995 100755 (executable)
@@ -37,7 +37,7 @@ rm -rf $testdir
 mkdir $testdir
 
 echo "Create the original file blocks"
-blksz="$(_get_block_size $testdir)"
+blksz="$(_get_file_block_size $testdir)"
 blks=2000
 margin='15%'
 sz=$((blksz * blks))
index 18f5208c08b045c245acb9124bb71c331d0f6b63..df0d0a74a16816acb93a4ff24548a34ce4131c24 100755 (executable)
@@ -20,7 +20,7 @@
 # "funshare" refers to fallocate copy-on-writing the shared blocks
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone unshare
 
 # Override the default cleanup function.
 _cleanup()
index 649c75dbf8129a7503ac86d2cca4c83b2463e09f..b9955265ed51f1bff434a6d93cbae5c8ff7872ff 100755 (executable)
@@ -38,7 +38,7 @@ testdir2=$SCRATCH_MNT/test-$seq
 mkdir $testdir2
 
 echo "Create the original files"
-blksz="$(_get_block_size $testdir1)"
+blksz="$(_get_file_block_size $testdir1)"
 blks=1000
 margin='7%'
 sz=$((blksz * blks))
index 0dc17f75cdf3eaf7f77e0ddd4a34b93bf09c50ac..7b625e86e6b74bebc438fef832042b486c1775ce 100755 (executable)
@@ -38,6 +38,7 @@ mkdir $testdir
 loops=512
 nr_loops=$((loops - 1))
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 
 echo "Initialize files"
 echo >> $seqres.full
index 4a6c341e0b38d06581939247b364c48a921e4df2..91da69d326e1e0eb04e7fbca8c904c1f843fe0cf 100755 (executable)
@@ -38,6 +38,7 @@ mkdir $testdir
 loops=512
 nr_loops=$((loops - 1))
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 
 echo "Initialize files"
 echo >> $seqres.full
index 8e0b630ba8a7a86305ff5f8619088760d47e0f3a..8ab71c72b0992a10df239baac46cbf41bd3daf71 100755 (executable)
@@ -40,6 +40,7 @@ mkdir $testdir
 loops=512
 nr_loops=$((loops - 1))
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 
 echo "Initialize files"
 echo >> $seqres.full
@@ -49,7 +50,7 @@ _cp_reflink $testdir/file1 $testdir/file3
 _scratch_cycle_mount
 
 fbytes() {
-       egrep -v '(61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61|62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62)'
+       grep -E -v '(61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61|62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62)'
 }
 
 reader() {
index d9e6a6e90c8e93b99be1f1197b59e7340916c51d..6deb6623582240a2cad7e28ec1e90f2025ecd7fd 100755 (executable)
@@ -41,6 +41,7 @@ mkdir $testdir
 loops=512
 nr_loops=$((loops - 1))
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 
 echo "Initialize files"
 echo >> $seqres.full
@@ -50,7 +51,7 @@ _cp_reflink $testdir/file1 $testdir/file3
 _scratch_cycle_mount
 
 fbytes() {
-       egrep -v '(61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61|62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62)'
+       grep -E -v '(61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61|62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62)'
 }
 
 reader() {
index 575ff08cb04488309baf619512d77c5b358e33fc..bdc8f7a05efb26bd2b1ea1ca751a8f6be05706bf 100755 (executable)
@@ -39,6 +39,7 @@ mkdir $testdir
 loops=1024
 nr_loops=$((loops - 1))
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 
 echo "Initialize files"
 echo >> $seqres.full
index d323ab8fd927bd0d489bfcf7db15a89b27597b4e..593cfbb73633033e248ed8df0cc6701b1d920df2 100755 (executable)
@@ -40,6 +40,7 @@ mkdir $testdir
 loops=1024
 nr_loops=$((loops - 1))
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 
 echo "Initialize files"
 echo >> $seqres.full
index 07af21992051102568d205d368b4c53607a840b5..f827beb67fe9c6720b910bb85e565ce0347623c6 100755 (executable)
@@ -33,7 +33,6 @@ _pwrite_byte 0x61 0 $blksz "$testdir/file1" >> "$seqres.full"
 
 fnr=19
 echo "Create extents"
-truncate -s $(( (2 ** i) * blksz)) "$testdir/file1"
 for i in $(seq 0 $fnr); do
        echo " ++ Reflink size $i, $((2 ** i)) blocks" >> "$seqres.full"
        n=$(( (2 ** i) * blksz))
index 8ad3f9662134a56e17ef85d6e6917fa44303aa3e..ff55a79f97263a303dbe775288b447fc6cb35bae 100755 (executable)
@@ -11,7 +11,7 @@
 # This test is motivated by a bug found in btrfs.
 #
 . ./common/preamble
-_begin_fstest auto quick prealloc metadata punch log
+_begin_fstest auto quick prealloc metadata punch log fiemap
 
 # Override the default cleanup function.
 _cleanup()
index 2b4617be8b09994ef938a87ed365845073160ca5..5e5883dfe2950a33ef583a360cad073642d84867 100755 (executable)
@@ -33,6 +33,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
 _pwrite_byte 0x61 0 $((blksz * 256)) $testdir/file1 >> $seqres.full
 _pwrite_byte 0x62 0 $((blksz * 256)) $testdir/file2 >> $seqres.full
 _pwrite_byte 0x62 0 $((blksz * 2)) $testdir/file2.chk >> $seqres.full
index 77bfcfcbd9045598261a5e7b2900c6f991e9643e..c86145141bf6068d75aeb54d417081c5decc8111 100755 (executable)
@@ -39,6 +39,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _pwrite_byte 0x61 0 $filesize $testdir/file1 >> $seqres.full
index 09469924963a76c7c58f6e9a099df2247065d752..75dbc6b84ce1a7a02d44be5ea8abcf96c77dd6d8 100755 (executable)
@@ -38,6 +38,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _pwrite_byte 0x61 0 $filesize $testdir/file1 >> $seqres.full
index 37d88440d962917929e8ca3299d2c1351ab097e7..5f6959a7e67ad6d4428c10b301bfbfe60a5650b7 100755 (executable)
@@ -15,7 +15,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto clone punch
+_begin_fstest auto clone punch prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -81,6 +81,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=1024
 filesize=$((blksz * nr))
 _pwrite_byte 0x61 0 $filesize $testdir/file1 >> $seqres.full
index 152e3cc2c2e7f6689855cbf850f34725e0a929d3..e9b373995deeb43177cb5acdc3edefddfa302b5b 100755 (executable)
@@ -15,7 +15,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto clone punch
+_begin_fstest auto clone punch prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -29,11 +29,14 @@ _cleanup()
 . ./common/reflink
 
 # real QA test starts here
+
+# btrfs can't fragment free space. This test is unreliable on NFS, as it
+# depends on the exported filesystem.
+_supported_fs ^btrfs ^nfs
 _require_scratch_reflink
 _require_cp_reflink
 _require_xfs_io_command "falloc"
 _require_xfs_io_command "fpunch"
-test $FSTYP = "btrfs" && _notrun "Can't fragment free space on btrfs."
 _require_odirect
 
 _fragment_freesp()
@@ -82,6 +85,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=1024
 filesize=$((blksz * nr))
 _pwrite_byte 0x61 0 $filesize $testdir/file1 >> $seqres.full
index eab77b39c3af038db84d2a242d93801d6e65d4e6..4a6346a757d779d60c2fd92618ffee2dca2205e7 100755 (executable)
@@ -12,7 +12,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -39,6 +39,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _weave_reflink_unwritten $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index 75cca42afb94570c6147d7650fc4caf6cd426098..262ae671f6ebfc3a9d847214b8918081fb68f20a 100755 (executable)
@@ -12,7 +12,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -38,6 +38,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _weave_reflink_unwritten $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index 9e22074082367dcd4b2055c1f030d6fafea1642d..d95f071aa581c32251cd348023bad5b265d2f6f7 100755 (executable)
@@ -12,7 +12,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -39,6 +39,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _weave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index 78b9a3f067162d530b763a622d8b36180cf089f8..49d31dbc9bb82e26cdccaf6f4e1703a94b60d8ad 100755 (executable)
@@ -12,7 +12,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -38,6 +38,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _weave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index 0d3cd03b4bf3639a05b12106bdfe94a691db59b0..28254866357bd636fa7d5ffc1f075d7013bec781 100755 (executable)
@@ -29,17 +29,27 @@ delay=5
 testfile=$TEST_DIR/testfile
 rm -f $testfile
 
+# Preload every binary used between sampling time1 and time2 so that loading
+# them has minimal overhead even if the root fs is hosted over a slow network.
+# Also don't put pipe and tee creation in that critical section.
+for i in echo stat sleep cat; do
+       $i --help &>/dev/null
+done
+
 echo test >$testfile
-time1=`_access_time $testfile | tee -a $seqres.full`
+time1=`_access_time $testfile`
+echo $time1 >> $seqres.full
 
 echo "sleep for $delay seconds"
 sleep $delay # sleep to allow time to move on for access
 cat $testfile
-time2=`_access_time $testfile | tee -a $seqres.full`
+time2=`_access_time $testfile`
+echo $time2 >> $seqres.full
 
 cd /
 _test_cycle_mount
-time3=`_access_time $testfile | tee -a $seqres.full`
+time3=`_access_time $testfile`
+echo $time3 >> $seqres.full
 
 delta1=`expr $time2 - $time1`
 delta2=`expr $time3 - $time1`
index ddaff69bfda64d3d15aa58593e9be12642e6c6a2..e2710b07d432d709e88d875fa9cb0f0c6ab6636b 100755 (executable)
@@ -7,7 +7,7 @@
 # Test permission checks in ->setattr
 #
 . ./common/preamble
-_begin_fstest metadata auto quick
+_begin_fstest metadata auto quick perms
 
 _register_cleanup "_cleanup_files"
 tag="added by qa $seq"
index ff76438d85ca5c2db3bcdbe8e2dc0053f83ac287..93dc4778bda040d387ca53cba9e942b4aba6fd86 100755 (executable)
@@ -13,7 +13,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -41,6 +41,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _weave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index e087b99cbf65aeafbd58926d9e99c34978826999..1262b185268ca893847866f2fe44ad4f6e7c6dc0 100755 (executable)
@@ -13,7 +13,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -40,6 +40,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _weave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index e2ae0410ddb15b2ee5510839a19ee71d6869f982..e02ee24abd2ed592290c2c5d1284788717688584 100755 (executable)
@@ -12,7 +12,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -39,6 +39,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _weave_reflink_regular $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index c5f802079a36343cf0aa9cfc94e0aed734d954f4..a057cac461e9c83c4233abfb9685ef9ed76ef431 100755 (executable)
@@ -12,7 +12,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -38,6 +38,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _weave_reflink_regular $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index 2a8cafcc46492cda8ca4199a7dd17e6b6f105dee..e20a2e2818a00679103aeffb8528f7aa7640b14a 100755 (executable)
@@ -16,7 +16,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -46,6 +46,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _weave_reflink_rainbow $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index a1a78ef40fd8337e5c2dcd3978e296facd9ce808..3cd90aa43fa7821e214d4b5384d4269e9139951e 100755 (executable)
@@ -16,7 +16,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -46,6 +46,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _weave_reflink_rainbow $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index 2598b44a17e864e9f535a20da3d7ee27c06c9304..faf168b2a4499183d4fa28c050541dcc3ba42250 100755 (executable)
@@ -8,7 +8,7 @@
 # unlink the file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -34,6 +34,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _pwrite_byte 0x61 0 $filesize $testdir/file1 >> $seqres.full
index aa2939b33f0a59ccc12ae6188c552f44d572b685..2e40173deab4a70d800eba41b9a8eca590e7f1f4 100755 (executable)
@@ -11,7 +11,7 @@
 # the golden output; we can only compare to a check file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
index 3c49662ba2fe29228e73236579a59176167939ed..a5a8b35f7bd5f109116251daa2ef7c972b70f319 100755 (executable)
@@ -11,7 +11,7 @@
 # the golden output; we can only compare to a check file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
index 00d6af05b6dcd2a4fb462e501fdb08f5a7729dec..3e6bd18a62445dbc4e3fea869e2a19dbf00714ea 100755 (executable)
@@ -11,7 +11,7 @@
 # the golden output; we can only compare to a check file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
index d3d257f1bec6646de68261d8fd87a2188f14b4cb..71da25e352991fe6db7cb8e08fd3d044df1787b2 100755 (executable)
@@ -20,6 +20,7 @@ _require_scratch
 _require_quota
 _require_user
 _require_group
+_require_odirect
 
 test_files()
 {
index ae979b7c4f9a7125c6b1befbe81d4d0b057485ff..f3a53565a5b21b5b110d5570952717a9c2496a03 100755 (executable)
@@ -11,7 +11,7 @@
 # the golden output; we can only compare to a check file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
index 409cfe0d0d7f7e9964a8beea50e4d9417b997387..bdb5bb1e5eb9ee39676a64d121f9ba6a5eb28229 100755 (executable)
@@ -11,7 +11,7 @@
 # the golden output; we can only compare to a check file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
index 2a5811241877fcf7c72d7611aa71a6c27f592421..0fb07e12a7ac75afb4d905e5ae411da675d18d69 100755 (executable)
@@ -7,7 +7,7 @@
 # File alignment tests
 #
 . ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick prealloc
 
 # Import common functions.
 . ./common/filter
index 26156c9b7fd8d10f7c52c338d557ecf3f54d5825..a996889ecf1fcedb438611ed7f9245d77ad3867c 100755 (executable)
@@ -7,7 +7,7 @@
 # Run the fiemap (file extent mapping) tester
 #
 . ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick fiemap
 
 # Import common functions.
 . ./common/filter
@@ -26,11 +26,6 @@ fiemaplog=$SCRATCH_MNT/$seq.log
 
 _require_test_program "fiemap-tester"
 
-# FIEMAP test doesn't like finding unwritten blocks after it punches out
-# a partial rt extent.
-test "$FSTYP" = "xfs" && \
-       _require_file_block_size_equals_fs_block_size $SCRATCH_MNT
-
 seed=`date +%s`
 
 echo "using seed $seed" >> $fiemaplog
index ce81e58c46ac8d021a2aeb0d258d343c85469019..e7708db9dbe03a1e17ab8b46a977f512d76604c0 100755 (executable)
@@ -11,7 +11,7 @@
 # the golden output; we can only compare to a check file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
index 82abf25f5454a191d08ccf55190c732dac0341ee..c5c941848c4349a29f4cd8138caa603753d5d427 100755 (executable)
@@ -11,7 +11,7 @@
 # the golden output; we can only compare to a check file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
index f17e32e47c3258e82a079a3720c74635959440fa..a12a425907c51068274681bdedbe0713abb36589 100755 (executable)
@@ -8,7 +8,7 @@
 # See also http://marc.info/?l=linux-btrfs&m=127434445620298&w=2
 #
 . ./common/preamble
-_begin_fstest auto quick acl
+_begin_fstest auto quick acl perms
 
 # Import common functions.
 . ./common/filter
index 410dbc0e176c8f8b5a785048b7dc1944475a634b..c8d12c19d7a67cd4affa077a5e1bf6a3788874c7 100755 (executable)
@@ -11,7 +11,7 @@
 # the golden output; we can only compare to a check file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Override the default cleanup function.
 _cleanup()
index e2d5c926e906e6e595da3ab2163fecfcf0f89761..81ce4d4e2450f4a950ac5d02026bbda6de73f23f 100755 (executable)
@@ -29,9 +29,12 @@ _cleanup()
 # According to the rename(2) manpage you can get either EEXIST or ENOTEMPTY as an
 # error for trying to rename a non-empty directory, so just catch the error for
 # ENOTMEMPTY and replace it with the EEXIST output so that either result passes
+# Also, mv v9.4+ modified error message when a nonempty destination directory fails
+# to be overwriteen
 _filter_directory_not_empty()
 {
-       sed -e "s,Directory not empty,File exists,g"
+       sed -e "s,Directory not empty,File exists,g" \
+           -e "s,cannot move .* to \(.*\):\(.*\),cannot overwrite \1:\2,g"
 }
  
 
index f5b5f182619dce5366ec14ab4fa5a44ab91b06e3..19b598e4a175b41500b4255d5b1f391513a81e47 100644 (file)
@@ -1,2 +1,2 @@
 QA output created by 245
-mv: cannot move 'TEST_DIR/test-mv/ab/aa/' to 'TEST_DIR/test-mv/aa': File exists
+mv: cannot overwrite 'TEST_DIR/test-mv/aa': File exists
index 192ab5cc31e706fedbc187f09a3ecb048e0cc866..b7a15f91894fad6c9c1803d3eca4c6744056d290 100755 (executable)
@@ -10,7 +10,7 @@
 # corrupts the filesystem (data/metadata).
 #
 . ./common/preamble
-_begin_fstest ioctl trim
+_begin_fstest ioctl trim auto
 
 tmp=`mktemp -d`
 trap "_cleanup; exit \$status" 0 1 3
@@ -53,14 +53,46 @@ _fail()
        kill $mypid 2> /dev/null
 }
 
-_guess_max_minlen()
+# Set FSTRIM_{MIN,MAX}_MINLEN to the lower and upper bounds of the -m(inlen)
+# parameter to fstrim on the scratch filesystem.
+set_minlen_constraints()
 {
-       mmlen=100000
-       while [ $mmlen -gt 1 ]; do
+       local mmlen
+
+       for ((mmlen = 100000; mmlen > 0; mmlen /= 2)); do
+               $FSTRIM_PROG -l $(($mmlen*2))k -m ${mmlen}k $SCRATCH_MNT &> /dev/null && break
+       done
+       test $mmlen -gt 0 || \
+               _notrun "could not determine maximum FSTRIM minlen param"
+       FSTRIM_MAX_MINLEN=$mmlen
+
+       for ((mmlen = 1; mmlen < FSTRIM_MAX_MINLEN; mmlen *= 2)); do
                $FSTRIM_PROG -l $(($mmlen*2))k -m ${mmlen}k $SCRATCH_MNT &> /dev/null && break
-               mmlen=$(($mmlen/2))
        done
-       echo $mmlen
+       test $mmlen -le $FSTRIM_MAX_MINLEN || \
+               _notrun "could not determine minimum FSTRIM minlen param"
+       FSTRIM_MIN_MINLEN=$mmlen
+}
+
+# Set FSTRIM_{MIN,MAX}_LEN to the lower and upper bounds of the -l(ength)
+# parameter to fstrim on the scratch filesystem.
+set_length_constraints()
+{
+       local mmlen
+
+       for ((mmlen = 100000; mmlen > 0; mmlen /= 2)); do
+               $FSTRIM_PROG -l ${mmlen}k $SCRATCH_MNT &> /dev/null && break
+       done
+       test $mmlen -gt 0 || \
+               _notrun "could not determine maximum FSTRIM length param"
+       FSTRIM_MAX_LEN=$mmlen
+
+       for ((mmlen = 1; mmlen < FSTRIM_MAX_LEN; mmlen *= 2)); do
+               $FSTRIM_PROG -l ${mmlen}k $SCRATCH_MNT &> /dev/null && break
+       done
+       test $mmlen -le $FSTRIM_MAX_LEN || \
+               _notrun "could not determine minimum FSTRIM length param"
+       FSTRIM_MIN_LEN=$mmlen
 }
 
 ##
@@ -70,13 +102,24 @@ _guess_max_minlen()
 ##
 fstrim_loop()
 {
+       set_minlen_constraints
+       set_length_constraints
+       echo "MINLEN max=$FSTRIM_MAX_MINLEN min=$FSTRIM_MIN_MINLEN" >> $seqres.full
+       echo "LENGTH max=$FSTRIM_MAX_LEN min=$FSTRIM_MIN_LEN" >> $seqres.full
+
        trap "_destroy_fstrim; exit \$status" 2 15
-       fsize=$($DF_PROG | grep $SCRATCH_MNT | grep $SCRATCH_DEV  | awk '{print $3}')
-       mmlen=$(_guess_max_minlen)
+       fsize=$(_discard_max_offset_kb "$SCRATCH_MNT" "$SCRATCH_DEV")
 
        while true ; do
-               step=$((RANDOM*$RANDOM+4))
-               minlen=$(((RANDOM*($RANDOM%2+1))%$mmlen))
+               while true; do
+                       step=$((RANDOM*$RANDOM+4))
+                       test "$step" -ge "$FSTRIM_MIN_LEN" && break
+               done
+               while true; do
+                       minlen=$(( (RANDOM * (RANDOM % 2 + 1)) % FSTRIM_MAX_MINLEN ))
+                       test "$minlen" -ge "$FSTRIM_MIN_MINLEN" && break
+               done
+
                start=$RANDOM
                if [ $((RANDOM%10)) -gt 7 ]; then
                        $FSTRIM_PROG $SCRATCH_MNT &
@@ -130,7 +173,13 @@ function run_process() {
 }
 
 nproc=20
-content=$here
+
+# Copy $here to the scratch fs and make coipes of the replica.  The fstests
+# output (and hence $seqres.full) could be in $here, so we need to snapshot
+# $here before computing file checksums.
+content=$SCRATCH_MNT/orig
+mkdir -p $content
+cp -axT $here/ $content/
 
 mkdir -p $tmp
 
index 307d5287d9ba6d1e98aa5f94ab88545615167194..39efb6b2ae29dc7ef639cfeb8f664b33ada74d6a 100755 (executable)
@@ -7,7 +7,7 @@
 # Test Generic fallocate hole punching
 #
 . ./common/preamble
-_begin_fstest auto quick prealloc punch
+_begin_fstest auto quick prealloc punch fiemap
 
 # Import common functions.
 . ./common/filter
index 808a730f3ad6c348373073c0bdcb5730af9c540c..ea6cc2938a5ab31c1c82aa56d0232ad901aa1375 100755 (executable)
@@ -44,6 +44,8 @@ _test_full_fs_punch()
        local file_len=$(( $(( $hole_len + $hole_interval )) * $iterations ))
        local path=`dirname $file_name`
        local hole_offset=0
+       local start_time
+       local stop_time
 
        if [ $# -ne 5 ]
        then
@@ -57,6 +59,9 @@ _test_full_fs_punch()
                -c "fsync" $file_name &> /dev/null
        chmod 666 $file_name
 
+       start_time="$(date +%s)"
+       stop_time=$(( start_time + (30 * TIME_FACTOR) ))
+
        # All files are created as a non root user to prevent reserved blocks
        # from being consumed.
        _fill_fs $(( 1024 * 1024 * 1024 )) $path/fill $block_size 1 \
@@ -64,6 +69,8 @@ _test_full_fs_punch()
 
        for (( i=0; i<$iterations; i++ ))
        do
+               test "$(date +%s)" -ge "$stop_time" && break
+
                # This part must not be done as root in order to
                # test that reserved blocks are used when needed
                _user_do "$XFS_IO_PROG -f -c \"fpunch $hole_offset $hole_len\" $file_name"
index b4d72e0fff67ccc415a2758324a16e751e46ba06..08fde46873e3e13487842813b817ee31f21db093 100755 (executable)
@@ -27,7 +27,7 @@ _scratch_mount
 
 _require_batched_discard $SCRATCH_MNT
 
-fssize=$($DF_PROG -k | grep "$SCRATCH_MNT" | grep "$SCRATCH_DEV"  | awk '{print $3}')
+fssize=$(_discard_max_offset_kb "$SCRATCH_MNT" "$SCRATCH_DEV")
 
 beyond_eofs=$(_math "$fssize*2048")
 max_64bit=$(_math "2^64 - 1")
@@ -121,6 +121,9 @@ case $FSTYP in
                start=$(_math "$base*$agsize*$bsize")
                len=$start
                export MKFS_OPTIONS="-F -b $bsize -g $agsize"
+               if echo "${MOUNT_OPTIONS}" | grep -q 'test_dummy_encryption' ; then
+                   MKFS_OPTIONS+=" -O encrypt"
+               fi
                ;;
        xfs)
                agsize=65538
index 83538319c1785a0e6bf8d525ac08cc81df6d37d3..1a7ccc84cd26b4ff61b89b649e14d349a6b42ee6 100755 (executable)
@@ -10,7 +10,7 @@
 # the golden output; we can only compare to a check file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone unshare
 
 # Import common functions.
 . ./common/filter
index 838e696da7276750b00a5a7f43d22e5642e993fd..b7cdecd94f219a33d11c8a1cfce2724fdf6b4e68 100755 (executable)
@@ -23,7 +23,7 @@ _workout()
        out=$SCRATCH_MNT/fsstress.$$
        args=`_scale_fsstress_args -p128 -n999999999 -f setattr=1 $FSSTRESS_AVOID -d $out`
        echo "fsstress $args" >> $seqres.full
-       $FSSTRESS_PROG $args > /dev/null 2>&1 &
+       $FSSTRESS_PROG $args &>> $seqres.full &
        pid=$!
        echo "Run dd writers in parallel"
        for ((i=0; i < num_iterations; i++))
index 8a6a2822b76bb309eba284bd43b1f548a23a366f..e7329c2f3280d8f4c960f9e5398291c951c4ca96 100755 (executable)
@@ -13,6 +13,7 @@ _begin_fstest auto quota rw prealloc ioctl enospc stress
 # Import common functions.
 . ./common/filter
 . ./common/quota
+. ./common/attr
 
 # Disable all sync operations to get higher load
 FSSTRESS_AVOID="$FSSTRESS_AVOID -ffsync=0 -fsync=0 -ffdatasync=0"
@@ -58,6 +59,7 @@ _require_user
 _require_scratch
 _require_command "$KILLALL_PROG" killall
 _require_command "$SETCAP_PROG" setcap
+_require_attrs security
 
 _scratch_mkfs_sized $((512 * 1024 * 1024)) >> $seqres.full 2>&1
 _scratch_mount "-o usrquota,grpquota"
index f86dae9b809509f794b4d2e80794633a8f1391f8..54c1999624212723066a63200f4edb4c2b8365c7 100755 (executable)
@@ -50,9 +50,21 @@ _file_create()
 
        cd $SCRATCH_MNT/origin
 
-       _disksize=`$DF_PROG -B 1 $SCRATCH_MNT | tail -1 | $AWK_PROG '{ print $5 }'`
+       _disksize=$(_get_available_space $SCRATCH_MNT)
+       _free_inodes=$(_get_free_inode $SCRATCH_MNT)
+       # Some filesystems do not limit number of inodes and return 0
+       if [ $_free_inodes -eq 0 ]; then
+               # Guess one block per inode
+               _free_inodes=$(($_disksize / $block_size))
+       fi
+       # Leave some slack for directories etc.
+       _free_inodes=$(($_free_inodes - $_free_inodes/8))
        _disksize=$(($_disksize / 3))
-       _num=$(($_disksize / $count / $threads / $block_size))
+       _num=$(($_disksize / $count / $block_size))
+       if [ $_num -gt $_free_inodes ]; then
+               _num=$_free_inodes
+       fi
+       _num=$(($_num/$threads))
        _count=$count
        while [ $_i -lt $_num ]
        do
index 6189edca2fd5baa36a5a387e6b4fa3bb061d97f7..f3b0540904a7e699c3c9db2d774043c315534176 100755 (executable)
@@ -37,6 +37,15 @@ _scratch_unmount 2>/dev/null
 _scratch_mkfs_sized $((2 * 1024 * 1024 * 1024)) >>$seqres.full 2>&1
 _scratch_mount
 
+# Certain filesystems such as XFS require sufficient free blocks to handle the
+# worst-case directory expansion as a result of a creat() call.  If the fs
+# block size is very large (e.g. 64k) then the number of blocks required for
+# the creat() call can represent far more free space than the 256K left at the
+# end of this test.  Therefore, create the file that the last dd will write to
+# now when we know there's enough free blocks.
+later_file=$SCRATCH_MNT/later
+touch $later_file
+
 # this file will get removed to create 256k of free space after ENOSPC
 # conditions are created.
 dd if=/dev/zero of=$SCRATCH_MNT/tmp1 bs=256K count=1 >>$seqres.full 2>&1
@@ -63,12 +72,12 @@ _freespace=`$DF_PROG -k $SCRATCH_MNT | tail -n 1 | awk '{print $5}'`
 
 # Try to write more than available space in chunks that will allow at least one
 # full write to succeed.
-dd if=/dev/zero of=$SCRATCH_MNT/tmp1 bs=128k count=8 >>$seqres.full 2>&1
+dd if=/dev/zero of=$later_file bs=128k count=8 >>$seqres.full 2>&1
 echo "Bytes written until ENOSPC:" >>$seqres.full
-du $SCRATCH_MNT/tmp1 >>$seqres.full
+du $later_file >>$seqres.full
 
 # And at least some of it should succeed.
-_filesize=`_get_filesize $SCRATCH_MNT/tmp1`
+_filesize=`_get_filesize $later_file`
 [ $_filesize -lt $((128 * 1024)) ] && \
        _fail "Partial write until enospc failed; wrote $_filesize bytes."
 
index 071445558e75d1f0e43c3e58718bc7173e3843b0..8e1ae4d298163b874cf701d899f123a5caeb3565 100755 (executable)
 . ./common/preamble
 _begin_fstest auto quota freeze
 
+# Override the default cleanup function.
+_cleanup()
+{
+       # Make sure $SCRATCH_MNT is unfreezed
+       xfs_freeze -u $SCRATCH_MNT 2>/dev/null
+       [ -n "$pid" ] && kill -9 $pid 2>/dev/null
+       wait $pid
+       cd /
+       rm -f $tmp.*
+}
+
 # Import common functions.
 . ./common/filter
 . ./common/quota
@@ -34,7 +45,7 @@ pid=$!
 sleep 1
 xfs_freeze -u $SCRATCH_MNT
 wait $pid
-_scratch_unmount
+unset pid
 
 # Failure comes in the form of a deadlock.
 
index 729da77a13c51922597bf3676ffaf3733079a23d..dc9b8a9d90ddd3cf9584ce60a98ef3283e4b91d7 100755 (executable)
@@ -13,7 +13,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -32,6 +32,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _sweave_reflink_regular $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index 6c755b5f79b802e90308194d45ecc1c219e30aae..d18500212fbf191114439d9863928f4a61cf6246 100755 (executable)
@@ -28,7 +28,7 @@ _require_test_program "seek_sanity_test"
 # Override the default cleanup function.
 _cleanup()
 {
-       eval "rm -f $BASE_TEST_FILE.*"
+       rm -f $BASE_TEST_FILE*
 }
 
 _run_seek_sanity_test $BASE_TEST_FILE > $seqres.full 2>&1 ||
index 7358124597a4f529f2212573792cbabaf417a112..629cb55b2e736c3d3c3b60624377302321b5d2b1 100755 (executable)
@@ -7,7 +7,7 @@
 # SEEK_DATA/SEEK_HOLE copy tests.
 #
 . ./common/preamble
-_begin_fstest auto quick other seek
+_begin_fstest auto quick other seek prealloc
 
 # Import common functions.
 . ./common/filter
index 76ea26bafdfa0eb7e2e7a31c863def86068752f7..14aea37ca6fc0c51d81d96280522c480dfe37c1f 100755 (executable)
@@ -13,7 +13,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -33,6 +33,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _sweave_reflink_regular $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index ed4f3268a05506f15e596dac066d5da224a38521..3ce234c3db2ca758ed9eb0d489dce3ab6d424d06 100755 (executable)
@@ -15,7 +15,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -34,6 +34,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _sweave_reflink_unwritten $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index 534fb24fbef5c570685f4e14536a138665fd6666..13e098788b66e937f2c54ca39f7bb744e6d5bfbc 100755 (executable)
@@ -15,7 +15,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -35,6 +35,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _sweave_reflink_unwritten $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index 50119c03a56e7ec1d44151c1e8a8103993822666..f61ae5a30ce7953db737a02cf32e51cb636b1d90 100755 (executable)
@@ -15,7 +15,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -34,6 +34,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _sweave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index 24cdab53957e7a121c02ba29460d0be26d65c4e4..40566ceca7487fd76160ab02775d9e0c4d5940b7 100755 (executable)
@@ -15,7 +15,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -35,6 +35,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _sweave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index 0f1d84160866fb561ee00036dcf75d593a309bf0..99500b41e7e0795723a060fc90a1abaf9b0a74a0 100755 (executable)
@@ -16,7 +16,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -36,6 +36,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _sweave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index 406b1b3954b989b21d905f62b30a2ce6f08b4a13..408cbd395c57a4b7c7c97d8b45a3ac39be2fe3d8 100755 (executable)
@@ -15,8 +15,10 @@ _begin_fstest auto quick
 
 # real QA test starts here
 
-# Modify as appropriate.
-_supported_fs generic
+# NFS will optimize away the on-the-wire lookup before attempting to
+# create a new file (since that means an extra round trip).
+_supported_fs ^nfs
+
 _require_scratch
 _require_symlinks
 _require_mknod
index f66c18054013469e49c1dc62e07b05b04aef0768..7ab9580360ce506270b9a455f5b4fe7b3c355d0e 100755 (executable)
@@ -16,7 +16,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -37,6 +37,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _sweave_reflink_holes $blksz $nr $testdir/file1 $testdir/file3 >> $seqres.full
index 6bdc3e1c7a50a7d337df4998ff23df4c6665d178..54c2ac2142b6a5760d656b7a2e11b6524a6f53b3 100755 (executable)
@@ -42,7 +42,6 @@ _pwrite_byte 0x61 0 $blksz $testdir/file1 >> $seqres.full
 fnr=26         # 2^26 reflink extents should be enough to find a slow op?
 timeout=8      # guarantee a good long run...
 echo "Find a reflink size that takes a long time"
-truncate -s $(( (2 ** i) * blksz)) $testdir/file1
 for i in $(seq 0 $fnr); do
        echo " ++ Reflink size $i, $((2 ** i)) blocks" >> $seqres.full
        n=$(( (2 ** i) * blksz))
index 95d4c02bce985cbe558b0da5ea7eaea1b0d23693..115a9bf74ef204ce89f4a82d210863b9b14f5e08 100755 (executable)
@@ -42,7 +42,6 @@ _pwrite_byte 0x61 0 $blksz $testdir/file1 >> $seqres.full
 fnr=26         # 2^26 reflink extents should be enough to find a slow op?
 timeout=8      # guarantee a good long run...
 echo "Find a reflink size that takes a long time"
-truncate -s $(( (2 ** i) * blksz)) $testdir/file1
 for i in $(seq 0 $fnr); do
        echo " ++ Reflink size $i, $((2 ** i)) blocks" >> $seqres.full
        n=$(( (2 ** i) * blksz))
index 8e1a112c378e149d86a321ce47bc80ac7951b351..0cd122029aa1a723864f916c7a48c2225e2d9d84 100755 (executable)
@@ -9,7 +9,7 @@
 # Test will operate on huge sparsed files so ENOSPC is expected.
 #
 . ./common/preamble
-_begin_fstest auto aio enospc rw stress
+_begin_fstest auto aio enospc rw stress prealloc
 
 fio_config=$tmp.fio
 fio_out=$tmp.fio.out
@@ -24,12 +24,13 @@ _require_scratch
 _require_odirect
 _require_aio
 _require_block_device $SCRATCH_DEV
+_require_xfs_io_command "falloc"
 
 NUM_JOBS=$((4*LOAD_FACTOR))
 BLK_DEV_SIZE=`blockdev --getsz $SCRATCH_DEV`
 FILE_SIZE=$((BLK_DEV_SIZE * 512))
 
-max_file_size=$(_get_max_file_size)
+max_file_size=$(_get_max_file_size $TEST_DIR)
 if [ $max_file_size -lt $FILE_SIZE ]; then
        FILE_SIZE=$max_file_size
 fi
@@ -96,7 +97,6 @@ filename=buffered-aio-verifier
 EOF
 
 _require_fio $fio_config
-_require_xfs_io_command "falloc"
 
 _scratch_mkfs >> $seqres.full 2>&1
 _scratch_mount
index faf982f5c0b1760738ed30b8644189b21b1a61bb..3f895ff8e52162eacd796c57fdfe71a6ebaf32f2 100755 (executable)
@@ -10,7 +10,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
index 011776024e6f3e417f878d4e867401652ca39942..9c305abedc4c47c98ec0148c42e47dcf956177f0 100755 (executable)
@@ -10,7 +10,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
index 3b0ff698a2f71f843910078b6d92142b8f7c6d8b..b46d512742edf0607aa6085df4b0929cb699c14d 100755 (executable)
@@ -8,7 +8,7 @@
 # charged for buffered copy on write.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
index 23f37a0d9d7f2dff833170369adb7fd09ba9bcad..d83da5a4f049ff2944487322d14a7359ad35342f 100755 (executable)
@@ -18,7 +18,7 @@
 # regression test of sorts.
 #
 . ./common/preamble
-_begin_fstest auto metadata log
+_begin_fstest auto metadata log prealloc
 
 # Override the default cleanup function.
 _cleanup()
index 68a2f71648409c82b7e6487edf0ae2ac35e4997e..dd617089af1dcfdcfa8707b1d940ecdabdab3234 100755 (executable)
@@ -7,7 +7,7 @@
 # Test SGID inheritance on subdirectories
 #
 . ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick perms
 
 # Import common functions.
 . ./common/filter
@@ -17,6 +17,7 @@ _supported_fs generic
 _require_test
 _require_user
 _require_chown
+_require_sgid_inheritance
 
 rm -rf $TEST_DIR/$seq-dir
 
index 5bc9c1d955fbccf1645a96d59717c5c070ad7ae8..5b8901260b7543aa5b786fde245eb1998fd149f2 100755 (executable)
@@ -7,7 +7,7 @@
 # Test Generic fallocate hole punching w/o unwritten extent
 #
 . ./common/preamble
-_begin_fstest auto quick punch
+_begin_fstest auto quick punch fiemap
 
 # Import common functions.
 . ./common/filter
index 134d5e3fc32666a9ca5cca5c5e739ac9788d4d3e..39cccc8bc0d05fd7e6245c5f02f242fb24c4e975 100755 (executable)
@@ -38,6 +38,7 @@ _require_user
 _require_ugid_map
 _require_userns
 _require_chown
+_require_use_local_uidgid
 qa_user_id=`id -u $qa_user`
 
 _filter_output()
index ed50818ab2b30ba1dc451e49d8ea5ad3ad360fd7..71ee76666700a2cdc8cc34212d8ba0b58294c802 100755 (executable)
@@ -13,7 +13,7 @@
 # the ACL was flushed and brought back from disk.
 #
 . ./common/preamble
-_begin_fstest acl attr auto quick
+_begin_fstest acl attr auto quick perms
 
 # Override the default cleanup function.
 _cleanup()
index 082d93bcd736b4f66aac5422e233f67abd3953fc..0efb46bed71752b095b97222144b5d2b14c03541 100755 (executable)
@@ -12,7 +12,7 @@
 #     https://patchwork.kernel.org/patch/3046931/
 #
 . ./common/preamble
-_begin_fstest acl auto quick
+_begin_fstest acl auto quick perms
 
 # Import common functions.
 . ./common/filter
index 2d185021d6d063a4259fa068e3a39add47cb5b6d..523d1f0428ffcf7946a4eae799da006aee2c01d9 100755 (executable)
@@ -7,7 +7,7 @@
 # Sanity check for defrag utility.
 #
 . ./common/preamble
-_begin_fstest auto fsr quick defrag
+_begin_fstest auto fsr quick defrag prealloc
 
 PIDS=""
 
index 3e9332ac4cc3ae961f43890edde6dc5d1c10a89c..f5c557b3a08abc41e1e27d8bc03f894e3053a114 100755 (executable)
@@ -8,7 +8,7 @@
 # charged for directio copy on write.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
index 95a2246116964d8f45006c19fd7b20888d67b7dc..92540b19dd8769bc7a9aaffc786c603f113baf66 100755 (executable)
@@ -7,7 +7,7 @@
 # Ensure that we can't go over the hard block limit when reflinking.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
index 2b589211e5f2ee96f4848c0d520f34205fa39beb..db7fd3db412d99712285a61f6af511746b6a8ff7 100755 (executable)
@@ -7,7 +7,7 @@
 # Ensure that we can't go over the hard block limit when CoWing a file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
index 676b876b4f0a38eafb192f9bab74e0cb93ce0d7b..cdffaaf3f9e31fd942b3db2b8411e1a131a8a2d2 100755 (executable)
@@ -31,6 +31,11 @@ _require_dm_target flakey
 
 _scratch_mkfs >>$seqres.full 2>&1
 _require_metadata_journaling $SCRATCH_DEV
+
+if [ $FSTYP = "f2fs" ]; then
+       export MOUNT_OPTIONS="-o fsync_mode=strict $MOUNT_OPTIONS"
+fi
+
 _init_flakey
 _mount_flakey
 
index fb16da1659dae8c2dbd58f6102947763d52c6292..c4e87675250ae2cc748810e3f8c05c962d7d79f3 100755 (executable)
@@ -11,7 +11,7 @@
 # devices that don't support write_same or discard.
 #
 . ./common/preamble
-_begin_fstest blockdev rw punch collapse insert zero
+_begin_fstest blockdev rw punch collapse insert zero prealloc
 
 _register_cleanup "_cleanup" BUS
 
index 3f504a29cca2a0bf9ccec28e513feb0bba98e854..acc17dac38d061de0ad95c13e70fb30991611c8a 100755 (executable)
@@ -11,7 +11,7 @@
 # Which btrfs will soft lock up and return wrong shared flag.
 #
 . ./common/preamble
-_begin_fstest auto clone
+_begin_fstest auto clone fiemap
 
 # Import common functions.
 . ./common/filter
@@ -28,13 +28,16 @@ _require_xfs_io_command "fiemap"
 _scratch_mkfs > /dev/null 2>&1
 _scratch_mount
 
-blocksize=$((128 * 1024))
+blocksize=$(_get_file_block_size $SCRATCH_MNT)
+_require_congruent_file_oplen $SCRATCH_MNT $blocksize
 file="$SCRATCH_MNT/tmp"
 
 # Golden output is for $LOAD_FACTOR == 1 case
+# and assumes a normal blocksize of 4K
 orig_nr=8192
-orig_last_extent=$(($orig_nr * $blocksize / 512))
-orig_end=$(($orig_last_extent + $blocksize / 512 - 1))
+orig_blocksize=4096
+orig_last_extent=$(($orig_nr * $orig_blocksize / 512))
+orig_end=$(($orig_last_extent + $orig_blocksize / 512 - 1))
 
 # Real output
 nr=$(($orig_nr * $LOAD_FACTOR))
@@ -42,7 +45,7 @@ last_extent=$(($nr * $blocksize / 512))
 end=$(($last_extent + $blocksize / 512 - 1))
 
 # write the initial block for later reflink
-_pwrite_byte 0xcdcdcdcd 0 $blocksize $file | _filter_xfs_io
+_pwrite_byte 0xcdcdcdcd 0 $blocksize $file > /dev/null
 
 # use reflink to create the rest of the file, whose all extents are all
 # pointing to the first extent
index 4ff66c21fa243bfaac477bf5468de321781a05bc..454ff52f0010f46a78954a400fede8a1b7248da6 100644 (file)
@@ -1,5 +1,3 @@
 QA output created by 352
-wrote 131072/131072 bytes at offset 0
-XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-0: [0..2097151]: shared
-1: [2097152..2097407]: shared|last
+0: [0..65535]: shared
+1: [65536..65543]: shared|last
index 1ce6ce6dd3fe1bfb119d61f4836a3b87341660ee..c563972510754782fc1806412b2e856690cceb1e 100755 (executable)
@@ -12,7 +12,7 @@
 # This caused SHARED flag only occurs after sync.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
@@ -29,31 +29,34 @@ _require_xfs_io_command "fiemap"
 _scratch_mkfs > /dev/null 2>&1
 _scratch_mount
 
-blocksize=64k
+blocksize=$(_get_file_block_size $SCRATCH_MNT)
+
 file1="$SCRATCH_MNT/file1"
 file2="$SCRATCH_MNT/file2"
+extmap1="$SCRATCH_MNT/extmap1"
+extmap2="$SCRATCH_MNT/extmap2"
 
 # write the initial file
-_pwrite_byte 0xcdcdcdcd 0 $blocksize $file1 | _filter_xfs_io
+_pwrite_byte 0xcdcdcdcd 0 $blocksize $file1 > /dev/null
 
 # reflink initial file
-_reflink_range $file1 0 $file2 0 $blocksize | _filter_xfs_io
+_reflink_range $file1 0 $file2 0 $blocksize > /dev/null
 
 # check their fiemap to make sure it's correct
-echo "before sync:"
-echo "$file1" | _filter_scratch
-$XFS_IO_PROG -c "fiemap -v" $file1 | _filter_fiemap_flags
-echo "$file2" | _filter_scratch
-$XFS_IO_PROG -c "fiemap -v" $file2 | _filter_fiemap_flags
+$XFS_IO_PROG -c "fiemap -v" $file1 | _filter_fiemap_flags > $extmap1
+$XFS_IO_PROG -c "fiemap -v" $file2 | _filter_fiemap_flags > $extmap2
+
+cmp -s $extmap1 $extmap2 || echo "mismatched extent maps before sync"
 
 # sync and recheck, to make sure the fiemap doesn't change just
 # due to sync
 sync
-echo "after sync:"
-echo "$file1" | _filter_scratch
-$XFS_IO_PROG -c "fiemap -v" $file1 | _filter_fiemap_flags
-echo "$file2" | _filter_scratch
-$XFS_IO_PROG -c "fiemap -v" $file2 | _filter_fiemap_flags
+$XFS_IO_PROG -c "fiemap -v" $file1 | _filter_fiemap_flags > $extmap1
+$XFS_IO_PROG -c "fiemap -v" $file2 | _filter_fiemap_flags > $extmap2
+
+cmp -s $extmap1 $extmap2 || echo "mismatched extent maps after sync"
+
+echo "Silence is golden"
 
 # success, all done
 status=0
index 4f6e0b9242abb2c091fcf04d819d778c6fbfd1cc..16ba4f1f4c57d77b37d8ab8205d598a5c0304856 100644 (file)
@@ -1,15 +1,2 @@
 QA output created by 353
-wrote 65536/65536 bytes at offset 0
-XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-linked 65536/65536 bytes at offset 0
-XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-before sync:
-SCRATCH_MNT/file1
-0: [0..127]: shared|last
-SCRATCH_MNT/file2
-0: [0..127]: shared|last
-after sync:
-SCRATCH_MNT/file1
-0: [0..127]: shared|last
-SCRATCH_MNT/file2
-0: [0..127]: shared|last
+Silence is golden
index 4bc69eeb3ed5efa55238cc58a08039037805ad0b..7c108d1b82b3ede26c0cabdcd9ee2afbe0143404 100755 (executable)
@@ -7,7 +7,7 @@
 # Test clear of suid/sgid on direct write.
 #
 . ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick perms
 
 # Import common functions.
 . ./common/filter
index ce748f854327e534751dacd34a7d83f193a43056..0f3e02d5b5584905881e5c2817cfe2c25f9e636e 100755 (executable)
@@ -24,6 +24,11 @@ _cleanup()
 . ./common/reflink
 
 # real QA test starts here
+
+# For NFS, a reflink is just a CLONE operation, and after that
+# point it's dealt with by the server.
+_supported_fs ^nfs
+
 _require_scratch_swapfile
 _require_scratch_reflink
 _require_cp_reflink
index 8c73ba3617b7d18bd316471c0824bbee49288b52..91fe5e2b446523089ed23c5f4d61a9af4d101b78 100755 (executable)
@@ -39,6 +39,7 @@ mkdir $testdir
 
 blocks=64
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 
 echo "Initialize file"
 _pwrite_byte 0x61 0 $((blocks * blksz)) $testdir/file >> $seqres.full
index 25692058e8bfd94d6f8c9ea93afbcb98c5b89fda..8ef4f846b17311586d44a32df5b961b8b38fbaf3 100755 (executable)
@@ -41,6 +41,7 @@ mkdir $testdir
 
 blocks=64
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=4
 halfway=$((blocks / 2 * blksz))
 quarter=$((blocks / 4 * blksz))
index dcd0a27bfad06608a5d52afe2c99754917438df7..6e0e81edfd25af02287b863beb5d99d3333ad150 100755 (executable)
@@ -7,7 +7,7 @@
 # RichACL apply-masks test
 #
 . ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
 
 # Import common functions.
 
index 8a4418696f703c80a67fe2ae1644fc08f6b3ff37..d3d888dbefb5c2778468966f0983ae9743fc14ad 100755 (executable)
@@ -7,7 +7,7 @@
 # RichACL auto-inheritance test
 #
 . ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
 
 # Import common functions.
 
index 538c0eb19f9d7c5fe2d89cc3d7eea3da06ccee16..2c721fea202c994b90a7257e9a9dbe5f4e0ae369 100755 (executable)
@@ -7,7 +7,7 @@
 # RichACL basic test
 #
 . ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
 
 # Import common functions.
 . ./common/attr
index 21f31b86392922d68ea2b727f27e84d84b6ed47d..d22f233a61a824316074765beeab12e55bc58b44 100755 (executable)
@@ -7,7 +7,7 @@
 # RichACL chmod test
 #
 . ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
 
 # Import common functions.
 
index e59691144e161e45c0f1820405a11d11f8d121ca..c005f8097474e22770a7b4c10f6cf57845fd4ef3 100755 (executable)
@@ -7,7 +7,7 @@
 # RichACL chown test
 #
 . ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
 
 # Import common functions.
 
index bed4521585a8c5e990c27baa3fea133f7aad4576..db7b2956f67382bed32a532a856cb16addeedc98 100755 (executable)
@@ -7,7 +7,7 @@
 # RichACL create test
 #
 . ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
 
 # Import common functions.
 
index 4b18e9f0aa7e53adea78ef6887d1695db8ef834d..d2259b348f45d7a8cdc05bf4913d5ead84001180 100755 (executable)
@@ -7,7 +7,7 @@
 # RichACL ctime test
 #
 . ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
 
 # Import common functions.
 
index dbd2010510a7e1f9b54fe5e2fa2b943b181a6461..526de2443a401a51b59c23073172c305db295bfa 100755 (executable)
@@ -7,7 +7,7 @@
 # RichACL delete test
 #
 . ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
 
 # Import common functions.
 
index acb0f424d7101ade4a42e117a3801b3efd9a493f..8c17af0ce723ac353fda7ecd3442775d8633c08a 100755 (executable)
@@ -7,7 +7,7 @@
 # RichACL write-vs-append test
 #
 . ./common/preamble
-_begin_fstest auto quick richacl
+_begin_fstest auto quick richacl perms
 
 # Import common functions.
 
index b83aa5988a065bddf38a490e7210c6b5366183ea..dac51dec1bbb9a51a4ba0e5a3feb6d2df47722d8 100755 (executable)
@@ -7,7 +7,7 @@
 # Check that bmap/fiemap accurately report shared extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
 
 _register_cleanup "_cleanup" BUS
 
@@ -39,6 +39,7 @@ mkdir $testdir
 
 blocks=5
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 sz=$((blocks * blksz))
 
 echo "Create the original files"
index 746cdd5586db7532b3910bfcb8311b85eb102012..438184b3081b53dce95329650dc6404c9fe9e2a6 100755 (executable)
@@ -8,7 +8,7 @@
 # owning group.
 #
 . ./common/preamble
-_begin_fstest auto quick acl
+_begin_fstest auto quick acl perms
 
 # Import common functions.
 . ./common/filter
index 9cd737e8ebfd997d5f0ef6b008c2c5869655621e..4a5be6698cbd9029267a17f809a9634db993a580 100755 (executable)
@@ -42,7 +42,7 @@ _scratch_mkfs >> $seqres.full 2>&1
 _require_metadata_journaling $SCRATCH_DEV
 _scratch_mount
 
-for i in $(seq 1 $((50 * TIME_FACTOR)) ); do
+while _soak_loop_running $((50 * TIME_FACTOR)); do
        ($FSSTRESS_PROG $FSSTRESS_AVOID -d $SCRATCH_MNT -n 999999 -p 4 >> $seqres.full &) \
                > /dev/null 2>&1
 
index 20c66e22946bf6140bf8543edc791e0ac271b14e..9a5feeb1c39899a74b186b39cb2d904101f461a7 100755 (executable)
@@ -2,7 +2,7 @@
 # SPDX-License-Identifier: GPL-2.0
 # Copyright (c) 2016 Red Hat, Inc.  All Rights Reserved.
 #
-# FS QA Test 999
+# FS QA Test 390
 #
 # Multi-threads freeze/unfreeze testing. This's a stress test case,
 # it won't do functional check.
@@ -14,8 +14,12 @@ _begin_fstest auto freeze stress
 _cleanup()
 {
        cd /
-       # Make sure $SCRATCH_MNT is unfreezed
+       # Kill freeze loops and make sure $SCRATCH_MNT is unfreezed
+       [ -n "$freeze_pids" ] && kill -9 $freeze_pids 2>/dev/null
+       wait $freeze_pids
        xfs_freeze -u $SCRATCH_MNT 2>/dev/null
+       [ -n "$fsstress_pid" ] && kill -9 $fsstress_pid 2>/dev/null
+       wait $fsstress_pid
        rm -f $tmp.*
 }
 
@@ -62,7 +66,9 @@ done
 
 wait $fsstress_pid
 result=$?
+unset fsstress_pid
 wait $freeze_pids
+unset freeze_pids
 
 # Exit with fsstress return value
 status=$result
index 748af9d89e0cc1e0c3ef728d7a924daf4a140975..cd99ee2e3c3c2be919905f0baed510df6198122b 100755 (executable)
@@ -9,7 +9,7 @@
 # to spurious -EEXIST failures from direct I/O reads.
 #
 . ./common/preamble
-_begin_fstest auto quick rw
+_begin_fstest auto quick rw prealloc
 
 # Override the default cleanup function.
 _cleanup()
index ac4014ab0c03a53565fbf474c5832d8f7bd4d51b..0c9efb6df41ca07b492bf51f068ad357c0ea9f01 100755 (executable)
@@ -6,7 +6,7 @@
 #
 # Test inode's metadata after fsync or fdatasync calls.
 # In the case of fsync, filesystem should recover all the inode metadata, while
-# recovering i_blocks and i_size at least for fdatasync.
+# recovering for fdatasync it should at least recovery i_size.
 #
 . ./common/preamble
 _begin_fstest shutdown auto quick metadata punch
@@ -37,9 +37,9 @@ check_inode_metadata()
 
        # fsync or fdatasync
        if [ $sync_mode = "fsync" ]; then
-               stat_opt='-c "b: %b s: %s a: %x m: %y c: %z"'
+               stat_opt='-c "s: %s a: %x m: %y c: %z"'
        else
-               stat_opt='-c "b: %b s: %s"'
+               stat_opt='-c "s: %s"'
        fi
 
        before=`stat "$stat_opt" $testfile`
index fc03e2f32a781363d6a5871d59eecb033f181f85..cbc2ce130713f92699f199d8d59a36bdd917de31 100755 (executable)
@@ -14,7 +14,7 @@ _cleanup()
 {
        cd /
        ulimit -f unlimited
-       rm -f $tmp.*
+       rm -f $tmp.* $TEST_DIR/$seq.*
 }
 
 # Import common functions.
index 939692eb38b7459945f9a3ff3d87191879f47215..ddbc04d59037b14673d4d3b2f9c3c010986f95e8 100755 (executable)
@@ -45,7 +45,7 @@
 # each block insert.
 #
 . ./common/preamble
-_begin_fstest auto quick insert
+_begin_fstest auto quick insert prealloc
 
 testfile=$TEST_DIR/$seq.file
 pattern=$tmp.pattern
@@ -69,7 +69,7 @@ _require_test
 _require_xfs_io_command "falloc"
 _require_xfs_io_command "finsert"
 
-blksize=`_get_block_size $TEST_DIR`
+blksize=`_get_file_block_size $TEST_DIR`
 
 # Generate a block with a repeating number represented as 4 bytes decimal.
 # The test generates unique pattern for each block in order to observe a
index 7a5004ed822ad8f600503b0091ac9f087e1b0c56..432befaca6826487f3b610fb38ee0cc7d7bacc4c 100755 (executable)
@@ -58,7 +58,7 @@ fs_stress()
                       -f chown=1 \
                       -f getdents=1 \
                       -f fiemap=1 \
-                      -d $target >/dev/null
+                      -d $target >>$seqres.full
        sync
 }
 
index f35f2f4a35eab937ed5856464d31462bd130152e..5fb5441a0c96e496ef153c6772108bdc653704a4 100755 (executable)
@@ -66,7 +66,7 @@ fs_stress()
                       -f chown=1 \
                       -f getdents=1 \
                       -f fiemap=1 \
-                      -d $target >/dev/null
+                      -d $target >>$seqres.full
        sync
 }
 
@@ -93,7 +93,7 @@ start_test()
 {
        local type=$1
 
-       _scratch_mkfs >$seqres.full 2>&1
+       _scratch_mkfs >>$seqres.full 2>&1
        _get_mount -t $FSTYP $SCRATCH_DEV $MNTHEAD
        $MOUNT_PROG --make-"${type}" $MNTHEAD
        mkdir $mpA $mpB $mpC
index 9852a49df2830e2bbc329b08481f22565e18ad9f..b2b8d550eb81078d093fd3b54418321be1a15d73 100755 (executable)
@@ -49,7 +49,7 @@ fs_stress()
                       -f chown=1 \
                       -f getdents=1 \
                       -f fiemap=1 \
-                      -d $target >/dev/null
+                      -d $target >>$seqres.full
        sync
 }
 
index 4f9e1fe07c552b6808cb4b9b6bb98bfe7b8c36cc..bd1b04a6248707f7502946adbed1940f91e585ac 100755 (executable)
@@ -7,12 +7,13 @@
 # mmap direct/buffered io between DAX and non-DAX mountpoints.
 #
 . ./common/preamble
-_begin_fstest auto quick dax
+_begin_fstest auto quick dax prealloc
 
 # Import common functions.
 . ./common/filter
 
 _supported_fs generic
+_require_hugepages
 _require_test
 _require_scratch_dax_mountopt "dax"
 _require_test_program "feature"
index 01b9da8ed994e52d175fb487b6e02f2acae72542..684b2bf2e3e61365283390da9fb0178b9555407b 100755 (executable)
@@ -8,7 +8,7 @@
 # block mapping extent.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
 
 _register_cleanup "_cleanup" BUS
 
@@ -39,6 +39,7 @@ mkdir $testdir
 
 blocks=32
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 sz=$((blocks * blksz))
 
 echo "Create the original files"
index deb05f07dd9125a0367ecd941a17253ed058bfe0..0f6e3bc9a17d8390d70e4c3654b6baeceefa6ddb 100755 (executable)
@@ -22,7 +22,7 @@ _supported_fs generic
 _require_scratch
 
 fs_size=$((128 * 1024 * 1024))
-page_size=$(get_page_size)
+page_size=$(_get_page_size)
 
 # We will never reach this number though
 nr_files=$(($fs_size / $page_size))
index 96dbe11b3c78d08b74d481fd325856aa652ec417..455d7aeb6862126f83fb72477590381f39845b12 100755 (executable)
@@ -9,7 +9,7 @@
 # delayed allocations.
 #
 . ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick prealloc
 
 # Import common functions.
 . ./common/filter
index 13a76563517c31469ccd478d59beb5540dc239dc..b43294f928053d8044cb0501f8c812f893a26ff5 100755 (executable)
@@ -8,7 +8,7 @@
 # block to hold extended attributes.
 #
 . ./common/preamble
-_begin_fstest auto quick attr
+_begin_fstest auto quick attr fiemap
 
 _register_cleanup "_cleanup" BUS
 
index 7fb24b41c0f3713f376d033e2b57a13067cfa4e9..d54af436ebd4cf0f60852c11cedf4c8db8792bb4 100755 (executable)
@@ -16,7 +16,7 @@ BASE_TEST_FILE=$TEST_DIR/seek_sanity_testfile
 # Override the default cleanup function.
 _cleanup()
 {
-       rm -f $tmp.* $BASE_TEST_FILE.*
+       rm -f $tmp.* $BASE_TEST_FILE*
 }
 
 # Import common functions.
index 0ec751daf72429f1b930294f59e0390a9001c529..85f29a3a8a747d5ca8f03e94d6a47b74b1f5100f 100755 (executable)
@@ -52,7 +52,7 @@ unset SCRATCH_RTDEV
 
 echo "Format and mount"
 _scratch_mkfs > $seqres.full 2>&1
-_dmerror_init
+_dmerror_init no_log
 _dmerror_mount
 
 _require_fs_space $SCRATCH_MNT 65536
index 8f6b7c2c77ca33c82125d304c8b3be46ae0bd7d2..71d2f6af036567ce3beaf88f97a77f0f0a666b52 100755 (executable)
@@ -8,7 +8,7 @@
 # in the owning group and directory has default ACLs.
 #
 . ./common/preamble
-_begin_fstest auto quick acl
+_begin_fstest auto quick acl perms
 
 # Import common functions.
 . ./common/filter
index 05d1c5b344da3de731a283205e590742f188bbcf..d9e9f84b07fa79b9452c3a9272828c28194167c7 100755 (executable)
@@ -16,7 +16,7 @@ BASE_TEST_FILE=$TEST_DIR/seek_sanity_testfile
 # Override the default cleanup function.
 _cleanup()
 {
-       rm -f $tmp.* $BASE_TEST_FILE.*
+       rm -f $tmp.* $BASE_TEST_FILE*
 }
 
 # Import common functions.
index 5f2d2edde03bce4092a038ad73e749621f5c1185..b69502cbf059d1ddb26ce09aff7a3c823825c34e 100755 (executable)
@@ -21,6 +21,7 @@ _begin_fstest auto quick rw punch
 _require_scratch
 _require_xfs_io_command "truncate"
 _require_xfs_io_command "fpunch"
+_require_odirect
 
 # format and mount
 _scratch_mkfs > $seqres.full 2>&1
index d66967bc60dc0339be5b6c399a465f25dfdb88ec..11945549b8268de56864967c1752718163beb9dd 100755 (executable)
@@ -15,7 +15,7 @@ BASE_TEST_FILE=$TEST_DIR/seek_sanity_testfile_$seq
 _cleanup()
 {
        cd /
-       rm -f $tmp.* $BASE_TEST_FILE
+       rm -f $tmp.* $BASE_TEST_FILE*
 }
 
 # Import common functions.
index 133e2e9d4a30dc5655cc619dc3534d79b6a242fd..3c9b39d059532d2de30caf22b8f1faa32b7c3a38 100755 (executable)
@@ -168,7 +168,7 @@ testf "combmark_\xe1\x80\x9c\xe1\x80\xad\xe1\x80\xaf.txt" "combining marks"
 testf "combmark_\xe1\x80\x9c\xe1\x80\xaf\xe1\x80\xad.txt" "combining marks"
 
 echo "Uniqueness of keys?"
-crazy_keys="$(_getfattr --absolute-names -d "${testfile}" | egrep -c '(french_|chinese_|greek_|arabic_|urk)')"
+crazy_keys="$(_getfattr --absolute-names -d "${testfile}" | grep -E -c '(french_|chinese_|greek_|arabic_|urk)')"
 expected_keys=11
 test "${crazy_keys}" -ne "${expected_keys}" && echo "Expected ${expected_keys} keys, saw ${crazy_keys}."
 
index 13d326e7edfa68cb8ee1e001d21db134d06fa976..da803de08feee7ce7c73d4b0eb1fe013cabdd321 100755 (executable)
@@ -25,6 +25,7 @@ _cleanup()
 _supported_fs generic
 _require_test
 _require_scratch_nocheck
+_require_no_logdev
 _require_log_writes
 _require_dm_target thin-pool
 
@@ -50,7 +51,8 @@ SANITY_DIR=$TEST_DIR/fsxtests
 rm -rf $SANITY_DIR
 mkdir $SANITY_DIR
 
-devsize=$((1024*1024*200 / 512))        # 200m phys/virt size
+size=$(_small_fs_size_mb 200)           # 200m phys/virt size
+devsize=$((1024*1024*size / 512))
 csize=$((1024*64 / 512))                # 64k cluster size
 lowspace=$((1024*1024 / 512))           # 1m low space threshold
 
@@ -76,7 +78,7 @@ FSX_OPTS="-N $NUM_OPS -d -P $SANITY_DIR -i $LOGWRITES_DMDEV"
 seeds=(0 0 0 0)
 # Run fsx for a while
 for j in `seq 0 $((NUM_FILES-1))`; do
-       run_check $here/ltp/fsx $FSX_OPTS -S ${seeds[$j]} -j $j $SCRATCH_MNT/testfile$j &
+       run_check $here/ltp/fsx $FSX_OPTS $FSX_AVOID -S ${seeds[$j]} -j $j $SCRATCH_MNT/testfile$j &
 done
 wait
 
index 7e0a31574c262803f4600aa37d0058b508b67160..03aeb8147585b030f3d56d1a70636e18a4b99c0d 100755 (executable)
@@ -26,6 +26,7 @@ _cleanup()
 _supported_fs generic
 _require_test
 _require_scratch_reflink
+_require_no_logdev
 _require_cp_reflink
 _require_log_writes
 _require_dm_target thin-pool
@@ -54,7 +55,8 @@ SANITY_DIR=$TEST_DIR/fsxtests
 rm -rf $SANITY_DIR
 mkdir $SANITY_DIR
 
-devsize=$((1024*1024*200 / 512))        # 200m phys/virt size
+size=$(_small_fs_size_mb 200)           # 200m phys/virt size
+devsize=$((1024*1024*size / 512))
 csize=$((1024*64 / 512))                # 64k cluster size
 lowspace=$((1024*1024 / 512))           # 1m low space threshold
 
@@ -82,7 +84,7 @@ FSX_OPTS="-N $NUM_OPS -d -k -P $SANITY_DIR -i $LOGWRITES_DMDEV"
 for j in `seq 0 $((NUM_FILES-1))`; do
        # clone the clone from prev iteration which may have already mutated
        _cp_reflink $SCRATCH_MNT/testfile$((j-1)) $SCRATCH_MNT/testfile$j
-       run_check $here/ltp/fsx $FSX_OPTS -S 0 -j $j $SCRATCH_MNT/testfile$j &
+       run_check $here/ltp/fsx $FSX_OPTS $FSX_AVOID -S 0 -j $j $SCRATCH_MNT/testfile$j &
 done
 wait
 
index 57d58e55c10d5d0b860d57665318d32e2bc9e4df..c3f0b2b06465c7ab7e54d9cb746bf60e7738d4d0 100755 (executable)
@@ -24,6 +24,8 @@ _begin_fstest auto freeze thin
 # Override the default cleanup function.
 _cleanup()
 {
+       # Make sure $SCRATCH_MNT is unfreezed
+       xfs_freeze -u $SCRATCH_MNT 2>/dev/null
        cd /
        rm -f $tmp.*
        $UMOUNT_PROG $SCRATCH_MNT >>$seqres.full 2>&1
@@ -45,6 +47,7 @@ _require_dm_target snapshot
 _require_command $LVM_PROG lvm
 _require_command "$THIN_CHECK_PROG" thin_check
 _require_freeze
+_require_odirect
 
 vgname=vg_$seq
 lvname=lv_$seq
@@ -54,6 +57,24 @@ origpsize=200
 virtsize=300
 newpsize=300
 
+# Check whether the filesystem has shutdown or remounted read-only. Shutdown
+# behavior can differ based on filesystem and configuration. Some fs' may not
+# have remounted without an additional write while others may have shutdown but
+# do not necessarily reflect read-only state in the mount options. Check both
+# here by first trying a simple write and following with an explicit ro check.
+is_shutdown_or_ro()
+{
+       ro=0
+
+       # if the fs has not shutdown, this may help trigger a remount-ro
+       touch $SCRATCH_MNT/newfile > /dev/null 2>&1 || ro=1
+
+       _fs_options /dev/mapper/$vgname-$snapname | grep -w "ro" > /dev/null
+       [ $? == 0 ] && ro=1
+
+       echo $ro
+}
+
 # Ensure we have enough disk space
 _scratch_mkfs_sized $((350 * 1024 * 1024)) >>$seqres.full 2>&1
 
@@ -110,13 +131,9 @@ ret=$?
 #      - The filesystem stays in Read-Write mode, but can be frozen/thawed
 #        without getting stuck.
 if [ $ret -ne 0 ]; then
-       # freeze failed, filesystem should reject further writes and remount
-       # as readonly. Sometimes the previous write process won't trigger
-       # ro-remount, e.g. on ext3/4, do additional touch here to make sure
-       # filesystems see the metadata I/O error.
-       touch $SCRATCH_MNT/newfile >/dev/null 2>&1
-       ISRO=$(_fs_options /dev/mapper/$vgname-$snapname | grep -w "ro")
-       if [ -n "$ISRO" ]; then
+       # freeze failed, filesystem should reject further writes
+       ISRO=`is_shutdown_or_ro`
+       if [ $ISRO == 1 ]; then
                echo "Test OK"
        else
                echo "Freeze failed and FS isn't Read-Only. Test Failed"
index 73fdfb5548af0a21ef2e179b1ef6e37d5c743234..0745d6a1dd3ac21899414bf0ef05961bf7018b07 100755 (executable)
@@ -21,7 +21,7 @@ _cleanup()
 . ./common/filter
 
 # real QA test starts here
-_supported_fs generic
+_supported_fs ^nfs
 
 _require_aiodio aio-dio-append-write-read-race
 _require_test_program "feature"
index 95752d3bfc9247ba004794a1bb84749adc396a89..f8d537f94190cb984fadb65fa6b912909af283cf 100755 (executable)
@@ -18,7 +18,7 @@
 # that inode metadata will be unchanged after recovery.
 #
 . ./common/preamble
-_begin_fstest shutdown auto quick metadata
+_begin_fstest shutdown auto quick metadata prealloc
 
 # Import common functions.
 . ./common/filter
index 42f6a4af5e90961611f53bef8a2ca3392aa2462a..81573972e6f6fe15337a54c08a2f0e85724e219c 100755 (executable)
@@ -14,7 +14,7 @@
 # the bug on XFS.
 #
 . ./common/preamble
-_begin_fstest auto quick punch zero
+_begin_fstest auto quick punch zero prealloc
 
 file=$TEST_DIR/$seq.fsx
 
index dd8525d7f1333b5d0485ebaf3f52d5a5cc94e5bd..6da2ec22b5c51b70a533a6e7f5518f2d349d2831 100755 (executable)
@@ -15,37 +15,29 @@ _cleanup()
 {
        cd /
        _log_writes_cleanup
-       _dmthin_cleanup
        rm -f $tmp.*
 }
 
 # Import common functions.
 . ./common/filter
-. ./common/dmthin
 . ./common/dmlogwrites
 
 # real QA test starts here
 _supported_fs generic
-_require_scratch_nocheck
+_require_scratch
+_require_no_logdev
 _require_log_writes_dax_mountopt "dax"
-_require_dm_target thin-pool
 _require_xfs_io_command "mmap" "-S"
 _require_xfs_io_command "log_writes"
+_require_command "$BLKDISCARD_PROG" blkdiscard
 
-devsize=$((1024*1024*200 / 512))        # 200m phys/virt size
-csize=$((1024*64 / 512))                # 64k cluster size
-lowspace=$((1024*1024 / 512))           # 1m low space threshold
-
-# Use a thin device to provide deterministic discard behavior. Discards are used
-# by the log replay tool for fast zeroing to prevent out-of-order replay issues.
-_dmthin_init $devsize $devsize $csize $lowspace
+MAPPED_LEN=$((512 * 1024 * 1024)) # 512 MiB
+LEN=$((1024 * 1024)) # 1 MiB
 
-_log_writes_init $DMTHIN_VOL_DEV
+_log_writes_init $SCRATCH_DEV $MAPPED_LEN
 _log_writes_mkfs >> $seqres.full 2>&1
 _log_writes_mount -o dax
 
-LEN=$((1024 * 1024)) # 1 MiB
-
 $XFS_IO_PROG -t -c "truncate $LEN" -c "mmap -S 0 $LEN" -c "mwrite 0 $LEN" \
        -c "log_writes -d $LOGWRITES_NAME -m preunmap" \
        -f $SCRATCH_MNT/test
@@ -53,14 +45,19 @@ $XFS_IO_PROG -t -c "truncate $LEN" -c "mmap -S 0 $LEN" -c "mwrite 0 $LEN" \
 # Unmount the scratch dir and tear down the log writes target
 _log_writes_unmount
 _log_writes_remove
-_dmthin_check_fs
+_check_scratch_fs
 
-# destroy previous filesystem so we can be sure our rebuild works
-_mkfs_dev $DMTHIN_VOL_DEV >> $seqres.full 2>&1
+# Forcibly zero the mapped range of scratch device and destroy
+# previous filesystem so we can be sure our rebuild works.
+# Note that blkdiscard -z will fall back to writing buffers of zeroes
+# if scratch device doesn't support write zeroes operation(i.e.
+# REQ_OP_WRITE_ZEROES).
+$BLKDISCARD_PROG -fzl $MAPPED_LEN $SCRATCH_DEV >> $seqres.full 2>&1
+_scratch_mkfs >> $seqres.full 2>&1
 
 # check pre-unmap state
-_log_writes_replay_log preunmap $DMTHIN_VOL_DEV
-_dmthin_mount
+_log_writes_replay_log preunmap $SCRATCH_DEV
+_scratch_mount
 
 # We should see $SCRATCH_MNT/test as having 1 MiB in block allocations
 du -sh $SCRATCH_MNT/test | _filter_scratch | _filter_spaces
index fbd0b12a9e3a8ab96f2f58e2a422cf2ad709517c..6d40d0e23799914297a4ffa6c95cd94ba0cc30c7 100755 (executable)
@@ -1,67 +1,39 @@
 #! /bin/bash
 # SPDX-License-Identifier: GPL-2.0
-# Copyright (c) 2017, SUSE Linux Products.  All Rights Reserved.
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
 #
-# FS QA Test No. 471
+# FS QA Test 471
 #
-# write a file with RWF_NOWAIT and it would fail because there are no
-# blocks allocated. Create a file with direct I/O and re-write it
-# using RWF_NOWAIT. I/O should finish within 50 microsecods since
-# block allocations are already performed.
+# Test that if names are added to a directory after an opendir(3) call and
+# before a rewinddir(3) call, future readdir(3) calls will return the names.
+# This is mandated by POSIX:
+#
+# https://pubs.opengroup.org/onlinepubs/007904875/functions/rewinddir.html
 #
 . ./common/preamble
-_begin_fstest auto quick rw
+_begin_fstest auto quick dir
 
-# Import common functions.
-. ./common/populate
-. ./common/filter
-. ./common/attr
+_cleanup()
+{
+       cd /
+       rm -fr $tmp.*
+       rm -fr $target_dir
+}
 
-# real QA test starts here
-_require_odirect
+_supported_fs generic
 _require_test
-_require_xfs_io_command pwrite -N
-
-# Remove reminiscence of previously run tests
-testdir=$TEST_DIR/$seq
-if [ -e $testdir ]; then
-       rm -Rf $testdir
-fi
-
-mkdir $testdir
-
-# Btrfs is a COW filesystem, so a RWF_NOWAIT write will always fail with -EAGAIN
-# when writing to a file range except if it's a NOCOW file and an extent for the
-# range already exists or if it's a COW file and preallocated/unwritten extent
-# exists in the target range. So to make sure that the last write succeeds on
-# all filesystems, use a NOCOW file on btrfs.
-if [ $FSTYP == "btrfs" ]; then
-       _require_chattr C
-       # Zoned btrfs does not support NOCOW
-       _require_non_zoned_device $TEST_DEV
-       touch $testdir/f1
-       $CHATTR_PROG +C $testdir/f1
-fi
-
-# Create a file with pwrite nowait (will fail with EAGAIN)
-$XFS_IO_PROG -f -d -c "pwrite -N -V 1 -b 1M 0 1M" $testdir/f1
-
-# Write the file without nowait
-$XFS_IO_PROG -f -d -c "pwrite -S 0xaa -W -w -V 1 -b 1M 0 8M" $testdir/f1 | _filter_xfs_io
+_require_test_program rewinddir-test
 
-time_taken=`$XFS_IO_PROG -d -c "pwrite -S 0xbb -N -V 1 -b 1M 2M 1M" $testdir/f1 | awk '/^1/ {print $5}'`
+[ $FSTYP == "btrfs" ] && _fixed_by_kernel_commit e60aa5da14d0 \
+       "btrfs: refresh dir last index during a rewinddir(3) call"
 
-# RWF_NOWAIT should finish within a short period of time so we are choosing
-# a conservative value of 50 ms. Anything longer means it is waiting
-# for something in the kernel which would be a fail.
-if (( $(echo "$time_taken < 0.05" | bc -l) )); then
-       echo "RWF_NOWAIT time is within limits."
-else
-       echo "RWF_NOWAIT took $time_taken seconds"
-fi
+target_dir="$TEST_DIR/test-$seq"
+rm -fr $target_dir
+mkdir $target_dir
 
-$XFS_IO_PROG -c "pread -v 0 8M" $testdir/f1 | _filter_xfs_io_unique
+$here/src/rewinddir-test $target_dir
 
 # success, all done
+echo "Silence is golden"
 status=0
 exit
index ab23272eecf74571d36ed86f60835b8542263f27..260f629ecffb13a0c4648f3bf54ff77c6165c132 100644 (file)
@@ -1,13 +1,2 @@
 QA output created by 471
-pwrite: Resource temporarily unavailable
-wrote 8388608/8388608 bytes at offset 0
-XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-RWF_NOWAIT time is within limits.
-00000000:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
-*
-00200000:  bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb  ................
-*
-00300000:  aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa  ................
-*
-read 8388608/8388608 bytes at offset 0
-XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Silence is golden
index a64735caa49f9a4e3ea547a0d3d7845e3f6cae79..7d11ba3700f45b233b0f07593e1a151e39feff97 100755 (executable)
@@ -57,7 +57,7 @@ swapfile_cycle $swapfile $((len + 3))
 # Create a ridiculously small swap file.  Each swap file must have at least
 # two pages after the header page.
 echo "tiny swap" | tee -a $seqres.full
-swapfile_cycle $swapfile $(($(get_page_size) * 3))
+swapfile_cycle $swapfile $(($(_get_page_size) * 3))
 
 status=0
 exit
index 892417707feabc225574fbb2af8c9b05608a1b38..125b95183ea7746159b3c5e1ce44fef4fb1bbac9 100755 (executable)
@@ -9,7 +9,7 @@
 # Also the test used 16k holes to be compatible with 16k block filesystems
 #
 . ./common/preamble
-_begin_fstest broken
+_begin_fstest broken fiemap
 
 # Import common functions.
 . ./common/punch
index c426402ede44bb12268915b790f7931091523b88..ce7fe013b1fc8e29b8d7019ba322be2a240aa151 100755 (executable)
@@ -12,7 +12,7 @@
 # testing efforts.
 #
 . ./common/preamble
-_begin_fstest shutdown auto log metadata eio recoveryloop
+_begin_fstest shutdown auto log metadata eio recoveryloop smoketest
 
 # Override the default cleanup function.
 _cleanup()
@@ -41,7 +41,7 @@ _require_metadata_journaling $SCRATCH_DEV
 _dmerror_init
 _dmerror_mount
 
-for i in $(seq 1 $((50 * TIME_FACTOR)) ); do
+while _soak_loop_running $((50 * TIME_FACTOR)); do
        ($FSSTRESS_PROG $FSSTRESS_AVOID -d $SCRATCH_MNT -n 999999 -p $((LOAD_FACTOR * 4)) >> $seqres.full &) \
                > /dev/null 2>&1
 
index 212373d17cef625670c492032f67ab7bdca3c1d5..b1ae4df4d4d883445da04cf5e1f664cff65ec1eb 100755 (executable)
@@ -8,7 +8,7 @@
 # bugs in the write path.
 #
 . ./common/preamble
-_begin_fstest auto rw
+_begin_fstest auto rw long_rw stress soak smoketest
 
 # Override the default cleanup function.
 _cleanup()
@@ -33,7 +33,10 @@ _scratch_mount >> $seqres.full 2>&1
 
 nr_cpus=$((LOAD_FACTOR * 4))
 nr_ops=$((25000 * nr_cpus * TIME_FACTOR))
-$FSSTRESS_PROG $FSSTRESS_AVOID -w -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus >> $seqres.full
+fsstress_args=(-w -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus)
+test -n "$SOAK_DURATION" && fsstress_args+=(--duration="$SOAK_DURATION")
+
+$FSSTRESS_PROG $FSSTRESS_AVOID "${fsstress_args[@]}" >> $seqres.full
 
 # success, all done
 status=0
index 480762d2241bcec2de226043a072fb9881fb98ba..419acc945bb47071209c50f327dc141672878968 100755 (executable)
@@ -99,33 +99,62 @@ _require_ofd_locks
 $XFS_IO_PROG -f -c "pwrite -S 0xFF 0 4096" \
        $TEST_DIR/testfile >> $seqres.full 2>&1
 
+mk_sem()
+{
+       SEMID=$(ipcmk -S 2 | cut -d ":" -f 2 | tr -d '[:space:]')
+       if [ -z "$SEMID" ]; then
+               echo "ipcmk failed"
+               exit 1
+       fi
+       SEMKEY=$(ipcs -s | grep $SEMID | cut -d " " -f 1)
+       if [ -z "$SEMKEY" ]; then
+               echo "ipcs failed"
+               exit 1
+       fi
+}
+
+rm_sem()
+{
+       ipcrm -s $SEMID 2>/dev/null
+}
+
 do_test()
 {
-       local soptions="$1"
-       local goptions="$2"
+       local soptions
+       local goptions
        # print options and getlk output for debug
        echo $* >> $seqres.full 2>&1
+       mk_sem
+       soptions="$1 -K $SEMKEY"
+       goptions="$2 -K $SEMKEY"
        # -s : do setlk
        $here/src/t_ofd_locks $soptions $TEST_DIR/testfile &
        # -g : do getlk
        $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \
                tee -a $seqres.full
        wait $!
+       rm_sem
 
+       mk_sem
        # add -F to clone with CLONE_FILES
-       soptions="$1 -F"
+       soptions="$1 -F -K $SEMKEY"
+       goptions="$2 -K $SEMKEY"
        # with -F, new locks are always file to place
        $here/src/t_ofd_locks $soptions $TEST_DIR/testfile &
        $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \
                tee -a $seqres.full
        wait $!
+       rm_sem
 
+       mk_sem
        # add -d to dup and close
-       soptions="$1 -d"
+       soptions="$1 -d -K $SEMKEY"
+       goptions="$2 -K $SEMKEY"
        $here/src/t_ofd_locks $soptions $TEST_DIR/testfile &
        $here/src/t_ofd_locks $goptions $TEST_DIR/testfile | \
                tee -a $seqres.full
        wait $!
+       rm_sem
 }
 
 # Always setlk at range [0,9], getlk at range [0,9] [5,24] or [20,29].
index c7e034d0ab47aaada468353b194285d777b70f7d..c647d24c2a40532e58dfa9cfa789cd980723c7be 100755 (executable)
@@ -49,6 +49,7 @@ _cleanup()
 # Modify as appropriate.
 _supported_fs generic
 
+_require_no_logdev
 _require_command "$KILLALL_PROG" killall
 # Use thin device as replay device, which requires $SCRATCH_DEV
 _require_scratch_nocheck
@@ -63,7 +64,9 @@ if [ $nr_cpus -gt 8 ]; then
 fi
 fsstress_args=$(_scale_fsstress_args -w -d $SCRATCH_MNT -n 512 -p $nr_cpus \
                $FSSTRESS_AVOID)
-devsize=$((1024*1024*200 / 512))       # 200m phys/virt size
+
+size=$(_small_fs_size_mb 200)           # 200m phys/virt size
+devsize=$((1024*1024*size / 512))
 csize=$((1024*64 / 512))               # 64k cluster size
 lowspace=$((1024*1024 / 512))          # 1m low space threshold
 
index e71203626c3a06a05cb056e45fffe5bba6bcf295..2b35f2856a128e48b0f03270b24e08cc9f5b3408 100755 (executable)
@@ -8,7 +8,7 @@
 # are placed beyond a file's size.
 #
 . ./common/preamble
-_begin_fstest auto quick log metadata
+_begin_fstest auto quick log metadata fiemap prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -35,6 +35,10 @@ _require_metadata_journaling $SCRATCH_DEV
 _init_flakey
 _mount_flakey
 
+# The fiemap results in the golden output requires file allocations to align to
+# 256K boundaries.
+_require_congruent_file_oplen $SCRATCH_MNT 262144
+
 # Create our test files.
 $XFS_IO_PROG -f -c "pwrite -S 0xea 0 256K" $SCRATCH_MNT/foo >/dev/null
 
index 40e83cbdb97774260ecdfcd51056da7de38757f0..4f413352b6bfe7ffd32192244c6838c39598795f 100755 (executable)
@@ -15,7 +15,7 @@
 #    buffer: record blockdev write errors in super_block that it backs
 
 . ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick eio
 
 # Override the default cleanup function.
 _cleanup()
index 2e0bc612f04ddc9c45fcc2797f872548a14232e2..8bab450ba3899f2dd04dbb92c37e81c85def3456 100755 (executable)
@@ -9,7 +9,7 @@
 #    7d83fb14258b ("xfs: prevent creating negative-sized file via INSERT_RANGE")
 #
 . ./common/preamble
-_begin_fstest auto quick insert
+_begin_fstest auto quick insert prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -30,7 +30,7 @@ _require_xfs_io_command "finsert"
 _require_xfs_io_command "truncate"
 
 block_size=$(_get_file_block_size $TEST_DIR)
-max_file_size=$(_get_max_file_size)
+max_file_size=$(_get_max_file_size $TEST_DIR)
 max_blocks=$((max_file_size / block_size))
 testfile=$TEST_DIR/testfile.$seq
 
index 7de198f93a7174abfe8c58350c3c3915ab4900c4..7dbfcb9835d962cf58fd08633fb88dd4ca474a33 100755 (executable)
@@ -41,7 +41,16 @@ filter_attr_output() {
                sed -e 's/has a [0-9]* byte value/has a NNNN byte value/g'
 }
 
-$here/src/attr_replace_test $SCRATCH_MNT/hello
+max_attr_size=65536
+
+# attr_replace_test can't easily auto-probe the attr size for ceph because:
+# - ceph imposes a maximum value for the total xattr names+values, and
+# - ceph reports the 'object size' in the block size, which is, by default, much
+#   larger than XATTR_SIZE_MAX (4M > 64k)
+# Hence, we need to provide it with a maximum size.
+[ "$FSTYP" = "ceph" ] && max_attr_size=65000
+
+$here/src/attr_replace_test -m $max_attr_size $SCRATCH_MNT/hello
 $ATTR_PROG -l $SCRATCH_MNT/hello >>$seqres.full 2>&1
 $ATTR_PROG -l $SCRATCH_MNT/hello | filter_attr_output
 
index fda8828df1a31b8b1d4b0936407ec1f29be5069f..3c9b223325d6f5148022fa935cebe98b8579c2dd 100755 (executable)
@@ -45,7 +45,7 @@ unset SCRATCH_RTDEV
 
 echo "Format and mount"
 _scratch_mkfs > $seqres.full 2>&1
-_dmerror_init
+_dmerror_init no_log
 _dmerror_mount
 
 datalen=65536
index e6e57dcd6946d335bced2ff475e50cb7d2a05fd0..5a586c122a0d0bd34fd4753343b702989926895b 100755 (executable)
 . ./common/preamble
 _begin_fstest auto quick freeze mount
 
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       # Make sure $SCRATCH_MNT is unfreezed
+       xfs_freeze -u $SCRATCH_MNT 2>/dev/null
+       rm -f $tmp.*
+}
+
 # Import common functions.
 . ./common/filter
 
@@ -35,7 +44,7 @@ xfs_freeze -f $SCRATCH_MNT
 
 # Read file while filesystem is frozen should succeed
 # without blocking
-$TIMEOUT_PROG -s KILL 1s cat $testfile
+$TIMEOUT_PROG -s KILL 5s cat $testfile
 
 xfs_freeze -u $SCRATCH_MNT
 
index 608f1715f24de80b694a9e69d31edab59857a417..84547f18233cc40685ef8de40de9f27d9a366aac 100755 (executable)
@@ -21,12 +21,16 @@ _require_sparse_files
 _scratch_mkfs >> $seqres.full 2>&1
 _scratch_mount
 
+blksize=$(_get_file_block_size $SCRATCH_MNT)
+test $blksize -eq $(getconf PAGE_SIZE) || \
+       _notrun "swap file allocation unit size must match page size"
+
 # We can't use _format_swapfile because we're using our custom mkswap and
 # swapon.
 touch "$SCRATCH_MNT/swap"
 $CHATTR_PROG +C "$SCRATCH_MNT/swap" >> $seqres.full 2>&1
 chmod 0600 "$SCRATCH_MNT/swap"
-$XFS_IO_PROG -c "truncate $(($(get_page_size) * 10))" "$SCRATCH_MNT/swap"
+$XFS_IO_PROG -c "truncate $(($(_get_page_size) * 10))" "$SCRATCH_MNT/swap"
 "$here/src/mkswap" "$SCRATCH_MNT/swap"
 "$here/src/swapon" "$SCRATCH_MNT/swap"
 swapoff "$SCRATCH_MNT/swap" >/dev/null 2>&1
index 4aeaffd31946fe46b4e4df4e5c8ef4987f5a3b4e..12f1bc4f94eaa429619552ab0c13cf0124fa452e 100755 (executable)
@@ -33,7 +33,7 @@ _scratch_mount >>$seqres.full 2>&1
 
 swapfile=$SCRATCH_MNT/swap
 len=$((2 * 1048576))
-page_size=$(get_page_size)
+page_size=$(_get_page_size)
 
 swapfile_cycle() {
        local swapfile="$1"
index 6188e3854bb014aa910a45d64a72fcd0da2a108e..05e368ab6f308e87f74142a3960591f35953b840 100755 (executable)
@@ -33,7 +33,7 @@ _scratch_mount >>$seqres.full 2>&1
 
 swapfile=$SCRATCH_MNT/swap
 len=$((2 * 1048576))
-page_size=$(get_page_size)
+page_size=$(_get_page_size)
 
 swapfile_cycle() {
        local swapfile="$1"
index 7159871f7f574c55267fd0f2fb862e62aa274e28..4b39c48b7232790c5493bb8e7f2b2e6e08afc7b0 100755 (executable)
@@ -8,7 +8,7 @@
 # eof to return nonzero contents.
 #
 . ./common/preamble
-_begin_fstest auto quick rw collapse zero
+_begin_fstest auto quick rw collapse zero prealloc
 
 # Import common functions.
 . ./common/punch
index bc84d219fa66d12be43f3bf4f33d0f20d038f64f..1151c8f2346e48183c67d50cd0a41313da256ca9 100755 (executable)
@@ -58,7 +58,8 @@ CLUSTER_SIZE=$((64 * 1024 / 512))             # 64K
 
 _dmthin_init $BACKING_SIZE $VIRTUAL_SIZE $CLUSTER_SIZE 0
 _dmthin_set_fail
-_dmthin_mkfs
+_dmthin_try_mkfs >> $seqres.full 2>&1 || \
+       _notrun "Could not format small thinp filesystem for test"
 _dmthin_mount
 
 # There're two bugs at here, one is dm-thin bug, the other is filesystem
index 8c3f627b574a22655fa8642d4304d1dd051b40e7..cb158ba56d3bbceff9aa31906bc6a7a485530818 100755 (executable)
@@ -34,6 +34,7 @@ _scratch_mkfs >>$seqres.full 2>&1
 _require_metadata_journaling $SCRATCH_DEV
 _init_flakey
 _mount_flakey
+_require_congruent_file_oplen $SCRATCH_MNT 2097152
 
 # Use file sizes and offsets/lengths for the clone operation that are multiples
 # of 64Kb, so that the test works on machine with any page size.
index b0f9077fc3b3058e78ad747c09145ef9cc093b01..b5589b8139d17a447d74c8028371372cc0900cee 100755 (executable)
@@ -34,6 +34,11 @@ _require_dm_target flakey
 
 _scratch_mkfs >>$seqres.full 2>&1
 _require_metadata_journaling $SCRATCH_DEV
+
+if [ $FSTYP = "f2fs" ]; then
+       export MOUNT_OPTIONS="-o fsync_mode=strict $MOUNT_OPTIONS"
+fi
+
 _init_flakey
 _mount_flakey
 
index a6971e631edc9fbb046815647c4690751474b8c9..a01d332724f0858ceac67a3dfeafa07c6da11d04 100755 (executable)
@@ -14,7 +14,7 @@
 # don't require the DAX mount option or a specific filesystem for the test.
 
 . ./common/preamble
-_begin_fstest auto quick dax punch collapse zero
+_begin_fstest auto quick dax punch collapse zero prealloc
 
 # Import common functions.
 . ./common/filter
@@ -38,6 +38,10 @@ _scratch_mkfs >> $seqres.full 2>&1
 export MOUNT_OPTIONS=""
 _scratch_mount >> $seqres.full 2>&1
 
+blksize=$(_get_file_block_size $SCRATCH_MNT)
+test $blksize -eq $(getconf PAGE_SIZE) || \
+       _notrun "file block size must match page size"
+
 # real QA test starts here
 $here/src/t_mmap_collision $TEST_DIR/testfile $SCRATCH_MNT/testfile
 
index 25a5b0f8c2dd12e275a4b321cc917f8ba69d9173..d4aac68bad8d4e50f1635e9cc2edddd8bb187d26 100755 (executable)
@@ -27,9 +27,11 @@ _begin_fstest shutdown auto quick metadata quota
 _supported_fs generic
 
 _require_scratch
+_require_quota
 _require_scratch_shutdown
 
 _scratch_mkfs >/dev/null 2>&1
+_scratch_enable_pquota
 _require_metadata_journaling $SCRATCH_DEV
 _qmount_option "prjquota"
 _qmount
index 058d8401ec82418ac65cb121668aca3a7381dbbf..61c21e42c456e045403262cce40c4792be579a08 100755 (executable)
@@ -8,7 +8,7 @@
 # eof to return nonzero contents.
 #
 . ./common/preamble
-_begin_fstest auto quick rw zero
+_begin_fstest auto quick rw zero prealloc
 
 # Import common functions.
 . ./common/punch
index dc082787ae4e776964d802b72565fda7b0ce0fb1..7ff845cea35b5a44b693c090b42900dc3a48a078 100755 (executable)
@@ -12,12 +12,14 @@ _begin_fstest auto quick clone
 # Import common functions.
 . ./common/filter
 . ./common/reflink
+. ./common/attr
 
 # real QA test starts here
 _supported_fs generic
 _require_scratch_reflink
 _require_command "$GETCAP_PROG" getcap
 _require_command "$SETCAP_PROG" setcap
+_require_attrs security
 
 _scratch_mkfs >>$seqres.full 2>&1
 _scratch_mount
index 2f3bd400860180b94cbb740fc8030d8ce4e77abb..1d537dec76ac8b7a7b6e4f0bb673c710220daef2 100755 (executable)
@@ -10,7 +10,7 @@
 # exposure bug uncovered by shared/010.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -30,6 +30,7 @@ _scratch_mount
 DONOR1=$SCRATCH_MNT/a
 TARGET=$SCRATCH_MNT/b
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 
 $XFS_IO_PROG -f -c "pwrite -S 0x72 0 $blksz" $DONOR1 >> $seqres.full
 
index 790ad5327d69dc1ac01944a6e024b1d8b4e92eeb..47af6237e10a5968b16115fd9891a908f396e4f5 100755 (executable)
@@ -9,7 +9,7 @@
 #   - Check that nothing changes in either file
 #
 . ./common/preamble
-_begin_fstest auto quick dedupe clone
+_begin_fstest auto quick dedupe clone fiemap
 
 # Override the default cleanup function.
 _cleanup()
@@ -31,6 +31,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
 _pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file1 >> $seqres.full
 _pwrite_byte 0x61 $((blksz * 2)) $((blksz * 6)) $testdir/file2 >> $seqres.full
 _pwrite_byte 0x62 $(((blksz * 6) - 33)) 1 $testdir/file2 >> $seqres.full
index 28747c218c00abee7f20aa2739b1a59f8a924d43..0839c6b856e8d52e338f4710fdd380ce36b5338a 100755 (executable)
@@ -8,7 +8,7 @@
 # 79b3dbe4adb3 fs: fix iomap_bmap position calculation
 #
 . ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick fiemap
 
 # Import common functions.
 . ./common/filter
index cde9d447755ae57986acfe2a3f3990e93da4d90f..0956e5017121663ecf43fbd0a37d7af32042090a 100755 (executable)
@@ -7,7 +7,7 @@
 # Long-soak directio fsx test
 #
 . ./common/preamble
-_begin_fstest soak long_rw
+_begin_fstest soak long_rw smoketest
 
 # Import common functions.
 . ./common/filter
@@ -35,6 +35,7 @@ fsx_args+=(-r $min_dio_sz)
 fsx_args+=(-t $min_dio_sz)
 fsx_args+=(-w $min_dio_sz)
 fsx_args+=(-Z)
+test -n "$SOAK_DURATION" && fsx_args+=(--duration="$SOAK_DURATION")
 
 run_fsx "${fsx_args[@]}" | sed -e '/^fsx.*/d'
 
index ae84fe04bb07d20047004db876aecc91cd6e120d..0e4e6009edac7ea3b71e56e6142f1ff6009a3e5d 100755 (executable)
@@ -7,7 +7,7 @@
 # Long-soak buffered fsx test
 #
 . ./common/preamble
-_begin_fstest soak long_rw
+_begin_fstest soak long_rw smoketest
 
 # Import common functions.
 . ./common/filter
@@ -29,6 +29,7 @@ fsx_args+=(-N $nr_ops)
 fsx_args+=(-p $((nr_ops / 100)))
 fsx_args+=(-o $op_sz)
 fsx_args+=(-l $file_sz)
+test -n "$SOAK_DURATION" && fsx_args+=(--duration="$SOAK_DURATION")
 
 run_fsx "${fsx_args[@]}" | sed -e '/^fsx.*/d'
 
index dc7cb36c198fcfb7043835cfa1fd6af7898e07fc..ada4dbeebd2253ebc7a09f894fa1eeacdf2e999e 100755 (executable)
@@ -32,6 +32,11 @@ _require_dm_target flakey
 
 _scratch_mkfs >>$seqres.full 2>&1
 _require_metadata_journaling $SCRATCH_DEV
+
+if [ $FSTYP = "f2fs" ]; then
+       export MOUNT_OPTIONS="-o fsync_mode=strict $MOUNT_OPTIONS"
+fi
+
 _init_flakey
 _mount_flakey
 
index 40cd1c6a29e8fc6fd360e79eab119c6696f7c47f..de09d1718f9c078666d31563d65619c5bbce3db9 100755 (executable)
@@ -32,6 +32,11 @@ _require_dm_target flakey
 
 _scratch_mkfs >>$seqres.full 2>&1
 _require_metadata_journaling $SCRATCH_DEV
+
+if [ $FSTYP = "f2fs" ]; then
+       export MOUNT_OPTIONS="-o fsync_mode=strict $MOUNT_OPTIONS"
+fi
+
 _init_flakey
 _mount_flakey
 
index 5e84ca977b44c6b588d4b8a5bfef311fb0e72e62..e5f3ddddd80cd025dd1742ca4af363040e4ab6d0 100755 (executable)
@@ -20,6 +20,7 @@ testfile=$TEST_DIR/$seq.txt
 # real QA test starts here
 _supported_fs generic
 _require_scratch
+_require_xfs_io_command "-T"
 _require_test_program "t_open_tmpfiles"
 
 _scratch_mkfs >> $seqres.full 2>&1
index 38e00f97e65e1af087605c6f8588213f4eb44134..290e05d0d5bbe17c73bca291811479cfe605d6cd 100755 (executable)
@@ -17,7 +17,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
 
 # Import common functions.
 . ./common/filter
@@ -38,6 +38,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _pwrite_byte 0x64 0 $((blksz * nr)) $testdir/file2 >> $seqres.full
index 89b2adadef554d0c4391e6d20cfd3b3a92a2e4e9..e6f0fa3c70a6a8bbd03086c3a92487817d7f98e0 100755 (executable)
@@ -17,7 +17,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
 
 # Import common functions.
 . ./common/filter
@@ -38,6 +38,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _pwrite_byte 0x64 0 $((blksz * nr)) $testdir/file2 >> $seqres.full
index e7682f593ebaf2b9f32aeb67f7a0fc24802423e6..4d907d8a4ba3032247b8ea97a4bde9e2df731bff 100755 (executable)
@@ -17,7 +17,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
 
 # Import common functions.
 . ./common/filter
@@ -38,6 +38,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _pwrite_byte 0x64 0 $((blksz * nr)) $testdir/file2 >> $seqres.full
index 624cfc410856f3c83f25bb7031a475017e85fa7d..928b761f98c00adced79f0c33b70edc44ecfafbd 100755 (executable)
@@ -17,7 +17,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
 
 # Import common functions.
 . ./common/filter
@@ -38,6 +38,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 _pwrite_byte 0x64 0 $((blksz * nr)) $testdir/file2 >> $seqres.full
index 4dbaea4de09226269007a4c554bec4c8cd65672f..a4f654afb1985c9d0ace85cf14e8f58df3bb23a0 100755 (executable)
@@ -27,6 +27,7 @@ _scratch_mkfs > $seqres.full 2>&1
 _scratch_mount >> $seqres.full 2>&1
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=2
 filesize=$((blksz * nr))
 testdir=$SCRATCH_MNT/test-$seq
index 7723b980703d6d0553a3fedea49af3f183913a8f..2eb99543edc12b0ef95da85077f71b8606a2508d 100755 (executable)
@@ -12,7 +12,7 @@
 # All operations above should not fail.
 #
 . ./common/preamble
-_begin_fstest auto quick clone enospc log
+_begin_fstest auto quick clone enospc log prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -39,6 +39,7 @@ _scratch_mkfs_sized $((512 * 1024 * 1024)) >> $seqres.full 2>&1
 _require_metadata_journaling $SCRATCH_DEV
 _init_flakey
 _mount_flakey
+_require_congruent_file_oplen $SCRATCH_MNT 4096
 
 # Create preallocated extent where we can write into
 $XFS_IO_PROG -f -c 'falloc 8k 64m' "$SCRATCH_MNT/foobar" >> $seqres.full
index 2689270aa3ddf50703c2f55a2abcb4ae1f3542cb..58c7f9a516c7834a5107388f3016798fb844d108 100755 (executable)
@@ -56,16 +56,18 @@ do_test()
        truncsize=$(((RANDOM * diosize + RANDOM % diosize) % max_io_size_b))
 
        $AIO_TEST -t $truncsize $oper_list $localfile
-       if [ $? -ne 0 ];then
+       ret=$?
+       if [ $ret -ne 0 ];then
                echo "$AIO_TEST -t $truncsize $oper_list $localfile"
                echo "==========^^ Fail ^^=========="
        fi
+       return $ret
 }
 
 testimes=$((LOAD_FACTOR * 100))
 while [ $testimes -gt 0 ]; do
        echo > $localfile
-       do_test
+       do_test || break
        ((testimes--))
 done
 
index 4e22ce656b8c8011c7b6078f66e4e55ec2ae1499..510b06f281dfed7c834e2d5ca64ad4f70e0ad6f8 100755 (executable)
@@ -19,9 +19,8 @@ create_file()
        local prefix=$3
        local i=0
 
-       while [ $i -lt $nr_file ]; do
+       for ((i = 0; i < nr_file; i++)); do
                echo -n > $dir/${prefix}_${i}
-               let i=$i+1
        done
 }
 
@@ -39,15 +38,25 @@ _scratch_mkfs_sized $((1024 * 1024 * 1024)) >>$seqres.full 2>&1
 _scratch_mount
 
 i=0
-free_inode=`_get_free_inode $SCRATCH_MNT`
-file_per_dir=1000
-loop=$((free_inode / file_per_dir + 1))
+free_inodes=$(_get_free_inode $SCRATCH_MNT)
+# Round the number of inodes to create up to the nearest 1000, like the old
+# code did to make sure that we *cannot* allocate any more inodes at all.
+free_inodes=$(( ( (free_inodes + 999) / 1000) * 1000 ))
+nr_cpus=$(( $($here/src/feature -o) * 4 * LOAD_FACTOR ))
+echo "free inodes: $free_inodes nr_cpus: $nr_cpus" >> $seqres.full
+
+if ((free_inodes <= nr_cpus)); then
+       nr_cpus=1
+       files_per_dir=$free_inodes
+else
+       files_per_dir=$(( (free_inodes + nr_cpus - 1) / nr_cpus ))
+fi
 mkdir -p $SCRATCH_MNT/testdir
+echo "nr_cpus: $nr_cpus files_per_dir: $files_per_dir" >> $seqres.full
 
-echo "Create $((loop * file_per_dir)) files in $SCRATCH_MNT/testdir" >>$seqres.full
-while [ $i -lt $loop ]; do
-       create_file $SCRATCH_MNT/testdir $file_per_dir $i >>$seqres.full 2>&1 &
-       let i=$i+1
+echo "Create $((nr_cpus * files_per_dir)) files in $SCRATCH_MNT/testdir" >>$seqres.full
+for ((i = 0; i < nr_cpus; i++)); do
+       create_file $SCRATCH_MNT/testdir $files_per_dir $i >>$seqres.full 2>&1 &
 done
 wait
 
index c5dcfefe2fda593d262bc501683518983a1e4883..eeaf3f1637f65f241bd967e001e456d1120c6315 100755 (executable)
@@ -25,6 +25,7 @@ _supported_fs generic
 _require_xfs_io_command "falloc"
 _require_test_program swapon
 _require_scratch_swapfile
+_require_odirect
 
 _scratch_mkfs > $seqres.full 2>&1
 _scratch_mount >> $seqres.full 2>&1
index 126b222d10d24b92f976f96e3d3b9c7a55608fac..2143c742c4e8840cbe4f50848794f13680b74c93 100755 (executable)
@@ -25,6 +25,7 @@ _supported_fs generic
 _require_test_program swapon
 _require_scratch_nocheck
 _require_block_device $SCRATCH_DEV
+_require_odirect
 # We cannot create swap on a zoned device because it can cause random write IOs
 _require_non_zoned_device "$SCRATCH_DEV"
 test -e /dev/snapshot && _notrun "userspace hibernation to swap is enabled"
index cded9ac6428fff5d5a1ba1a2fd296f1416faaf41..d8071a348b26e33f16d7fa16b623a35f21a48637 100755 (executable)
@@ -9,7 +9,7 @@
 # - conditions for enabling verity
 # - verity files have correct contents and size
 # - can't change contents of verity files, but can change metadata
-# - can retrieve a verity file's measurement via FS_IOC_MEASURE_VERITY
+# - can retrieve a verity file's digest via FS_IOC_MEASURE_VERITY
 #
 . ./common/preamble
 _begin_fstest auto quick verity
@@ -48,15 +48,6 @@ verify_data_readable()
        md5sum $file > /dev/null
 }
 
-verify_data_unreadable()
-{
-       local file=$1
-
-       # try both reading just the first data block, and reading until EOF
-       head -c $FSV_BLOCK_SIZE $file 2>&1 >/dev/null | filter_output
-       md5sum $file |& filter_output
-}
-
 _fsv_scratch_begin_subtest "Enabling verity on file with verity already enabled fails with EEXIST"
 _fsv_create_enable_file $fsv_file
 echo "(trying again)"
@@ -94,7 +85,7 @@ verify_data_readable $fsv_file
 _fsv_scratch_begin_subtest "Enabling verity can be interrupted"
 dd if=/dev/zero of=$fsv_file bs=1 count=0 seek=$((1 << 34)) status=none
 start_time=$(date +%s)
-$FSVERITY_PROG enable $fsv_file &
+$FSVERITY_PROG enable --block-size=$FSV_BLOCK_SIZE $fsv_file &
 sleep 0.5
 kill %1
 wait
@@ -106,7 +97,7 @@ fi
 _fsv_scratch_begin_subtest "Enabling verity on file with verity already being enabled fails with EBUSY"
 dd if=/dev/zero of=$fsv_file bs=1 count=0 seek=$((1 << 34)) status=none
 start_time=$(date +%s)
-$FSVERITY_PROG enable $fsv_file &
+$FSVERITY_PROG enable --block-size=$FSV_BLOCK_SIZE $fsv_file &
 sleep 0.5
 _fsv_enable $fsv_file |& filter_output
 kill %1
@@ -129,7 +120,7 @@ verify_data_readable $fsv_file
 
 _fsv_scratch_begin_subtest "verity file can be measured"
 _fsv_create_enable_file $fsv_file >> $seqres.full
-_fsv_measure $fsv_file
+_fsv_measure $fsv_file | _filter_fsverity_digest
 
 _fsv_scratch_begin_subtest "verity file can be renamed"
 _fsv_create_enable_file $fsv_file
@@ -170,8 +161,8 @@ verify_data_readable $fsv_file
 
 # Test files <= 1 block in size.  These are a bit of a special case since there
 # are no hash blocks; the root hash is calculated directly over the data block.
+_fsv_scratch_begin_subtest "verity on small files"
 for size in 1 $((FSV_BLOCK_SIZE - 1)) $FSV_BLOCK_SIZE; do
-       _fsv_scratch_begin_subtest "verity on $size-byte file"
        head -c $size /dev/urandom > $fsv_orig_file
        cp $fsv_orig_file $fsv_file
        _fsv_enable $fsv_file
@@ -179,7 +170,7 @@ for size in 1 $((FSV_BLOCK_SIZE - 1)) $FSV_BLOCK_SIZE; do
        rm -f $fsv_file
 done
 
-_fsv_scratch_begin_subtest "verity on 100M file (multiple levels in hash tree)"
+_fsv_scratch_begin_subtest "verity on 100MB file (multiple levels in hash tree)"
 head -c 100000000 /dev/urandom > $fsv_orig_file
 cp $fsv_orig_file $fsv_file
 _fsv_enable $fsv_file
index ad381629d83dd9b02ed0672b9aeea6a2e6171b21..d703835b88ab3620a352ac1312f220c73c3343bf 100644 (file)
@@ -39,7 +39,7 @@ bash: SCRATCH_MNT/file.fsv: Operation not permitted
 # verity file can be read
 
 # verity file can be measured
-sha256:be54121da3877f8852c65136d731784f134c4dd9d95071502e80d7be9f99b263
+sha256:<digest>
 
 # verity file can be renamed
 
@@ -58,16 +58,12 @@ sha256:be54121da3877f8852c65136d731784f134c4dd9d95071502e80d7be9f99b263
 # Trying to measure non-verity file fails with ENODATA
 ERROR: FS_IOC_MEASURE_VERITY failed on 'SCRATCH_MNT/file.fsv': No data available
 
-# verity on 1-byte file
+# verity on small files
 Files matched
-
-# verity on 4095-byte file
 Files matched
-
-# verity on 4096-byte file
 Files matched
 
-# verity on 100M file (multiple levels in hash tree)
+# verity on 100MB file (multiple levels in hash tree)
 Files matched
 
 # verity on sparse file
index 63c0aef5bccc34529ceeaa492ae0bdcd8823f75c..ca0f27f98f2c6e433db7b9534ad98b8bf5775b8b 100755 (executable)
@@ -36,23 +36,23 @@ fsv_file=$SCRATCH_MNT/file.fsv
 _fsv_scratch_begin_subtest "FS_IOC_ENABLE_VERITY doesn't require root"
 echo foo > $fsv_file
 chmod 666 $fsv_file
-_user_do "$FSVERITY_PROG enable $fsv_file"
+_user_do "$FSVERITY_PROG enable --block-size=$FSV_BLOCK_SIZE $fsv_file"
 
 _fsv_scratch_begin_subtest "FS_IOC_ENABLE_VERITY requires write access"
 echo foo > $fsv_file >> $seqres.full
 chmod 444 $fsv_file
-_user_do "$FSVERITY_PROG enable $fsv_file" |& _filter_scratch
+_user_do "$FSVERITY_PROG enable --block-size=$FSV_BLOCK_SIZE $fsv_file" |& _filter_scratch
 
 _fsv_scratch_begin_subtest "FS_IOC_ENABLE_VERITY requires !append-only"
 echo foo > $fsv_file >> $seqres.full
 $CHATTR_PROG +a $fsv_file
-$FSVERITY_PROG enable $fsv_file |& _filter_scratch
+_fsv_enable $fsv_file |& _filter_scratch
 $CHATTR_PROG -a $fsv_file
 
 _fsv_scratch_begin_subtest "FS_IOC_ENABLE_VERITY requires !immutable"
 echo foo > $fsv_file >> $seqres.full
 $CHATTR_PROG +i $fsv_file
-$FSVERITY_PROG enable $fsv_file |& _filter_scratch
+_fsv_enable $fsv_file |& _filter_scratch
 $CHATTR_PROG -i $fsv_file
 
 _fsv_scratch_begin_subtest "FS_IOC_MEASURE_VERITY doesn't require root"
index 882baa21a3fee7404b54322165a93aa7099595db..cb42baaa67aa851cb211e68782cbce7819f6b29a 100755 (executable)
@@ -28,6 +28,7 @@ _cleanup()
 _supported_fs generic
 _require_scratch_verity
 _disable_fsverity_signatures
+_require_fsverity_corruption
 
 _scratch_mkfs_verity &>> $seqres.full
 _scratch_mount
@@ -36,115 +37,190 @@ fsv_file=$SCRATCH_MNT/file.fsv
 
 setup_zeroed_file()
 {
-       local len=$1
-       local sparse=$2
+       local block_size=$1
+       local file_len=$2
+       local sparse=$3
 
        if $sparse; then
-               dd if=/dev/zero of=$fsv_orig_file bs=1 count=0 seek=$len \
+               dd if=/dev/zero of=$fsv_orig_file bs=1 count=0 seek=$file_len \
                        status=none
        else
-               head -c $len /dev/zero > $fsv_orig_file
+               head -c $file_len /dev/zero > $fsv_orig_file
        fi
        cp $fsv_orig_file $fsv_file
-       _fsv_enable $fsv_file
-       md5sum $fsv_file |& _filter_scratch
-}
-
-filter_sigbus()
-{
-       sed -e 's/.*Bus error.*/Bus error/'
+       _fsv_enable $fsv_file --block-size=$block_size
+       cmp $fsv_orig_file $fsv_file
 }
 
 round_up_to_page_boundary()
 {
        local n=$1
-       local page_size=$(get_page_size)
+       local page_size=$(_get_page_size)
 
        echo $(( (n + page_size - 1) & ~(page_size - 1) ))
 }
 
+mread()
+{
+       local file=$1
+       local offset=$2
+       local length=$3
+       local map_len=$(round_up_to_page_boundary $(_get_filesize $file))
+
+       # Some callers expect xfs_io to crash with SIGBUS due to the mread,
+       # causing the shell to print "Bus error" to stderr.  To allow this
+       # message to be redirected, execute xfs_io in a new shell instance.
+       # However, for this to work reliably, we also need to prevent the new
+       # shell instance from optimizing out the fork and directly exec'ing
+       # xfs_io.  The easiest way to do that is to append 'true' to the
+       # commands, so that xfs_io is no longer the last command the shell sees.
+       # Don't let it write core files to the filesystem.
+       bash -c "trap '' SIGBUS; ulimit -c 0; $XFS_IO_PROG -r $file \
+               -c 'mmap -r 0 $map_len' \
+               -c 'mread -v $offset $length'; true"
+}
+
 corruption_test()
 {
-       local file_len=$1
-       local zap_offset=$2
-       local zap_len=$3
-       local is_merkle_tree=${4:-false} # if true, zap tree instead of data
-       local use_sparse_file=${5:-false}
-       local page_aligned_eof=$(round_up_to_page_boundary $file_len)
-       local measurement
+       local block_size=$1
+       local file_len=$2
+       local zap_offset=$3
+       local zap_len=$4
+       local is_merkle_tree=${5:-false} # if true, zap tree instead of data
+       local use_sparse_file=${6:-false}
+
+       local paramstr="block_size=$block_size"
+       paramstr+=", file_len=$file_len"
+       paramstr+=", zap_offset=$zap_offset"
+       paramstr+=", zap_len=$zap_len"
+       paramstr+=", is_merkle_tree=$is_merkle_tree"
+       paramstr+=", use_sparse_file=$use_sparse_file"
 
        if $is_merkle_tree; then
                local corrupt_func=_fsv_scratch_corrupt_merkle_tree
        else
                local corrupt_func=_fsv_scratch_corrupt_bytes
        fi
+       local fs_block_size=$(_get_block_size $SCRATCH_MNT)
 
-       local msg="Corruption test:"
-       msg+=" file_len=$file_len"
-       if $use_sparse_file; then
-               msg+=" (sparse)"
-       fi
-       msg+=" zap_offset=$zap_offset"
-       if $is_merkle_tree; then
-               msg+=" (in Merkle tree)"
-       fi
-       msg+=" zap_len=$zap_len"
+       rm -rf "${SCRATCH_MNT:?}"/*
+       setup_zeroed_file $block_size $file_len $use_sparse_file
 
-       _fsv_scratch_begin_subtest "$msg"
-       setup_zeroed_file $file_len $use_sparse_file
-       cmp $fsv_file $fsv_orig_file
-       echo "Corrupting bytes..."
+       # Corrupt part of the file (data or Merkle tree).
        head -c $zap_len /dev/zero | tr '\0' X \
                | $corrupt_func $fsv_file $zap_offset
 
-       echo "Validating corruption (reading full file)..."
+       # Reading the full file with buffered I/O should fail.
        _scratch_cycle_mount
-       md5sum $fsv_file |& _filter_scratch
+       if cat $fsv_file >/dev/null 2>$tmp.err; then
+               echo "Unexpectedly was able to read full file ($paramstr)"
+       elif ! grep -q 'Input/output error' $tmp.err; then
+               echo "Wrong error reading full file ($paramstr):"
+               cat $tmp.err
+       fi
 
-       echo "Validating corruption (direct I/O)..."
+       # Reading the full file with direct I/O should fail.
        _scratch_cycle_mount
-       dd if=$fsv_file bs=$FSV_BLOCK_SIZE iflag=direct status=none \
-               of=/dev/null |& _filter_scratch
+       if dd if=$fsv_file bs=$fs_block_size iflag=direct status=none \
+               of=/dev/null 2>$tmp.err
+       then
+               echo "Unexpectedly was able to read full file with DIO ($paramstr)"
+       elif ! grep -q 'Input/output error' $tmp.err; then
+               echo "Wrong error reading full file with DIO ($paramstr):"
+               cat $tmp.err
+       fi
 
-       if (( zap_offset < file_len )) && ! $is_merkle_tree; then
-               echo "Validating corruption (reading just corrupted part)..."
-               dd if=$fsv_file bs=1 skip=$zap_offset count=$zap_len \
-                       of=/dev/null status=none |& _filter_scratch
+       # Reading just the corrupted part of the file should fail.
+       if ! $is_merkle_tree; then
+               if dd if=$fsv_file bs=1 skip=$zap_offset count=$zap_len \
+                       of=/dev/null status=none 2>$tmp.err; then
+                       echo "Unexpectedly was able to read corrupted part ($paramstr)"
+               elif ! grep -q 'Input/output error' $tmp.err; then
+                       echo "Wrong error reading corrupted part ($paramstr):"
+                       cat $tmp.err
+               fi
        fi
 
-       echo "Validating corruption (reading full file via mmap)..."
-       bash -c "trap '' SIGBUS; $XFS_IO_PROG -r $fsv_file \
-               -c 'mmap -r 0 $page_aligned_eof' \
-               -c 'mread 0 $file_len'" |& filter_sigbus
+       # Reading the full file via mmap should fail.
+       mread $fsv_file 0 $file_len >/dev/null 2>$tmp.err
+       if ! grep -q 'Bus error' $tmp.err; then
+               echo "Didn't see SIGBUS when reading file via mmap"
+               cat $tmp.err
+       fi
 
+       # Reading just the corrupted part via mmap should fail.
        if ! $is_merkle_tree; then
-               echo "Validating corruption (reading just corrupted part via mmap)..."
-               bash -c "trap '' SIGBUS; $XFS_IO_PROG -r $fsv_file \
-                       -c 'mmap -r 0 $page_aligned_eof' \
-                       -c 'mread $zap_offset $zap_len'" |& filter_sigbus
+               mread $fsv_file $zap_offset $zap_len >/dev/null 2>$tmp.err
+               if ! grep -q 'Bus error' $tmp.err; then
+                       echo "Didn't see SIGBUS when reading corrupted part via mmap"
+                       cat $tmp.err
+               fi
        fi
 }
 
-# Note: these tests just overwrite some bytes without checking their original
-# values.  Therefore, make sure to overwrite at least 5 or so bytes, to make it
-# nearly guaranteed that there will be a change -- even when the test file is
-# encrypted due to the test_dummy_encryption mount option being specified.
+# Reading the last block of the file with mmap is tricky, so we need to be
+# a bit careful. Some filesystems read the last block in full, while others
+# return zeros in the last block past EOF, regardless of the contents on
+# disk. In the former, corruption should be detected and result in SIGBUS,
+# while in the latter we would expect zeros past EOF, but no error.
+corrupt_eof_block_test()
+{
+       local block_size=$1
+       local file_len=$2
+       local zap_len=$3
+
+       rm -rf "${SCRATCH_MNT:?}"/*
+       setup_zeroed_file $block_size $file_len false
+       head -c $zap_len /dev/zero | tr '\0' X \
+               | _fsv_scratch_corrupt_bytes $fsv_file $file_len
+
+       mread $fsv_file $file_len $zap_len >$tmp.out 2>$tmp.err
 
-corruption_test 131072 0 5
-corruption_test 131072 4091 5
-corruption_test 131072 65536 65536
-corruption_test 131072 131067 5
+       head -c $file_len /dev/zero >$tmp.zeroes
+       mread $tmp.zeroes $file_len $zap_len >$tmp.zeroes_out
 
-# Non-zeroed bytes in the final partial block beyond EOF should cause reads to
-# fail too.  Such bytes would be visible via mmap().
-corruption_test 130999 131000 72
+       grep -q 'Bus error' $tmp.err || diff $tmp.out $tmp.zeroes_out
+}
 
-# Merkle tree corruption.
-corruption_test 200000 100 10 true
+test_block_size()
+{
+       local block_size=$1
+
+       # Note: these tests just overwrite some bytes without checking their
+       # original values.  Therefore, make sure to overwrite at least 5 or so
+       # bytes, to make it nearly guaranteed that there will be a change --
+       # even when the test file is encrypted due to the test_dummy_encryption
+       # mount option being specified.
+       corruption_test $block_size 131072 0 5
+       corruption_test $block_size 131072 4091 5
+       corruption_test $block_size 131072 65536 65536
+       corruption_test $block_size 131072 131067 5
+
+       corrupt_eof_block_test $block_size 130999 72
+
+       # Merkle tree corruption.
+       corruption_test $block_size 200000 100 10 true
+
+       # Sparse file.  Corrupting the Merkle tree should still cause reads to
+       # fail, i.e. the filesystem must verify holes.
+       corruption_test $block_size 200000 100 10 true true
+}
 
-# Sparse file.  Corrupting the Merkle tree should still cause reads to fail,
-# i.e. the filesystem must verify holes.
-corruption_test 200000 100 10 true true
+# Always test FSV_BLOCK_SIZE.  Also test some other block sizes if they happen
+# to be supported.
+_fsv_scratch_begin_subtest "Testing block_size=FSV_BLOCK_SIZE"
+test_block_size $FSV_BLOCK_SIZE
+for block_size in 1024 4096 16384 65536; do
+       _fsv_scratch_begin_subtest "Testing block_size=$block_size if supported"
+       if (( block_size == FSV_BLOCK_SIZE )); then
+               continue # Skip redundant test case.
+       fi
+       if ! _fsv_can_enable $fsv_file --block-size=$block_size; then
+               echo "block_size=$block_size is unsupported" >> $seqres.full
+               continue
+       fi
+       test_block_size $block_size
+done
 
 # success, all done
 status=0
index 3c08d3e8029484ae67615bd3c761a98b8f293daf..3ed57f1c26b9cf5b1153de4b93433f0b811d3338 100644 (file)
@@ -1,89 +1,11 @@
 QA output created by 574
 
-# Corruption test: file_len=131072 zap_offset=0 zap_len=5
-0dfbe8aa4c20b52e1b8bf3cb6cbdf193  SCRATCH_MNT/file.fsv
-Corrupting bytes...
-Validating corruption (reading full file)...
-md5sum: SCRATCH_MNT/file.fsv: Input/output error
-Validating corruption (direct I/O)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading just corrupted part)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading full file via mmap)...
-Bus error
-Validating corruption (reading just corrupted part via mmap)...
-Bus error
+# Testing block_size=FSV_BLOCK_SIZE
 
-# Corruption test: file_len=131072 zap_offset=4091 zap_len=5
-0dfbe8aa4c20b52e1b8bf3cb6cbdf193  SCRATCH_MNT/file.fsv
-Corrupting bytes...
-Validating corruption (reading full file)...
-md5sum: SCRATCH_MNT/file.fsv: Input/output error
-Validating corruption (direct I/O)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading just corrupted part)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading full file via mmap)...
-Bus error
-Validating corruption (reading just corrupted part via mmap)...
-Bus error
+# Testing block_size=1024 if supported
 
-# Corruption test: file_len=131072 zap_offset=65536 zap_len=65536
-0dfbe8aa4c20b52e1b8bf3cb6cbdf193  SCRATCH_MNT/file.fsv
-Corrupting bytes...
-Validating corruption (reading full file)...
-md5sum: SCRATCH_MNT/file.fsv: Input/output error
-Validating corruption (direct I/O)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading just corrupted part)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading full file via mmap)...
-Bus error
-Validating corruption (reading just corrupted part via mmap)...
-Bus error
+# Testing block_size=4096 if supported
 
-# Corruption test: file_len=131072 zap_offset=131067 zap_len=5
-0dfbe8aa4c20b52e1b8bf3cb6cbdf193  SCRATCH_MNT/file.fsv
-Corrupting bytes...
-Validating corruption (reading full file)...
-md5sum: SCRATCH_MNT/file.fsv: Input/output error
-Validating corruption (direct I/O)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading just corrupted part)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading full file via mmap)...
-Bus error
-Validating corruption (reading just corrupted part via mmap)...
-Bus error
+# Testing block_size=16384 if supported
 
-# Corruption test: file_len=130999 zap_offset=131000 zap_len=72
-f5cca0d7fbb8b02bc6118a9954d5d306  SCRATCH_MNT/file.fsv
-Corrupting bytes...
-Validating corruption (reading full file)...
-md5sum: SCRATCH_MNT/file.fsv: Input/output error
-Validating corruption (direct I/O)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading full file via mmap)...
-Bus error
-Validating corruption (reading just corrupted part via mmap)...
-Bus error
-
-# Corruption test: file_len=200000 zap_offset=100 (in Merkle tree) zap_len=10
-4a1e4325031b13f933ac4f1db9ecb63f  SCRATCH_MNT/file.fsv
-Corrupting bytes...
-Validating corruption (reading full file)...
-md5sum: SCRATCH_MNT/file.fsv: Input/output error
-Validating corruption (direct I/O)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading full file via mmap)...
-Bus error
-
-# Corruption test: file_len=200000 (sparse) zap_offset=100 (in Merkle tree) zap_len=10
-4a1e4325031b13f933ac4f1db9ecb63f  SCRATCH_MNT/file.fsv
-Corrupting bytes...
-Validating corruption (reading full file)...
-md5sum: SCRATCH_MNT/file.fsv: Input/output error
-Validating corruption (direct I/O)...
-dd: error reading 'SCRATCH_MNT/file.fsv': Input/output error
-Validating corruption (reading full file via mmap)...
-Bus error
+# Testing block_size=65536 if supported
index ffa6b61d13360936bf9ae3a7c47e8ebde36eaf87..344fd2b91093523b82458866079602832ce19d92 100755 (executable)
@@ -4,7 +4,7 @@
 #
 # FS QA Test generic/575
 #
-# Test that fs-verity is using the correct measurement values.  This test
+# Test that fs-verity is using the correct file digest values.  This test
 # verifies that fs-verity is doing its Merkle tree-based hashing correctly,
 # i.e. that it hasn't been broken by a change.
 #
@@ -26,9 +26,6 @@ _cleanup()
 # real QA test starts here
 _supported_fs generic
 _require_scratch_verity
-if [ $FSV_BLOCK_SIZE != 4096 ]; then
-       _notrun "4096-byte verity block size not supported on this platform"
-fi
 _disable_fsverity_signatures
 
 _scratch_mkfs_verity &>> $seqres.full
@@ -36,24 +33,42 @@ _scratch_mount
 fsv_orig_file=$SCRATCH_MNT/file
 fsv_file=$SCRATCH_MNT/file.fsv
 
+# Try multiple hash algorithms.
 algs=(sha256 sha512)
 
+# Try multiple Merkle tree block sizes.
+block_sizes=(1024 4096)
+
 # Try files with 0, 1, and multiple Merkle tree levels.
 file_sizes=(0 4096 65536 65536 100000000)
 
 # Try both unsalted and salted, and check that empty salt is the same as no salt
 salts=('' '' '' '--salt=' '--salt=f3c93fa6fb828c0e1587e5714ecf6f56')
 
-# The expected file measurements are here rather than in the expected output
-# file because not all hash algorithms may be available.
-sha256_vals=(
+# The expected file digests are here rather than in the expected output file
+# because the kernel might not support all hash algorithms and block sizes.
+sha256_vals_bsize1024=(
+sha256:f2cca36b9b1b7f07814e4284b10121809133e7cb9c4528c8f6846e85fc624ffa
+sha256:ea08590a4fe9c3d6c9dafe0eedacd9dffff8f24e24f1865ee3af132a495ab087
+sha256:527496288d703686e31092f5cca7e1306b2467f00b247ad01056ee5ec35a4bb9
+sha256:527496288d703686e31092f5cca7e1306b2467f00b247ad01056ee5ec35a4bb9
+sha256:087818b23312acb15dff9ff6e2b4f601406d08bb36013542444cc15248f47016
+)
+sha256_vals_bsize4096=(
 sha256:3d248ca542a24fc62d1c43b916eae5016878e2533c88238480b26128a1f1af95
 sha256:babc284ee4ffe7f449377fbf6692715b43aec7bc39c094a95878904d34bac97e
 sha256:011e3f2b1dc89b75d78cddcc2a1b85cd8a64b2883e5f20f277ae4c0617e0404f
 sha256:011e3f2b1dc89b75d78cddcc2a1b85cd8a64b2883e5f20f277ae4c0617e0404f
 sha256:9d33cab743468fcbe4edab91a275b30dd543c12dd5e6ce6f2f737f66a1558f06
 )
-sha512_vals=(
+sha512_vals_bsize1024=(
+sha512:8451664f25b2ad3f24391280e0c5681cb843389c180baa719f8fdfb063f5ddfa2d1c4433e55e2b6fbb3ba6aa2df8a4f41bf56cb7e0a3b617b6919a42c80f034c
+sha512:ab3c6444ab377bbe54c604c26cad241b146d85dc29727703a0d8134f70a8172fb3fa67d171355106b69cc0a9e7e9debb335f9461b3aba44093914867f7c73233
+sha512:e6a11353c24dd7b4603cb8ffa50a7041dbea7382d4698474ccbc2d8b34f3a83d8bf16df25c64ed31ee27213a8a3cbd001fb1ccde46384c23b81305c2393c1046
+sha512:e6a11353c24dd7b4603cb8ffa50a7041dbea7382d4698474ccbc2d8b34f3a83d8bf16df25c64ed31ee27213a8a3cbd001fb1ccde46384c23b81305c2393c1046
+sha512:517d573bd50d5f3787f5766c2ac60c7af854b0901b69757b4ef8dd70aa6b30fcc10d81629ce923bdd062a01c20fad0f063a081a2f3b0814ac06547b26bedc0d9
+)
+sha512_vals_bsize4096=(
 sha512:ccf9e5aea1c2a64efa2f2354a6024b90dffde6bbc017825045dce374474e13d10adb9dadcc6ca8e17a3c075fbd31336e8f266ae6fa93a6c3bed66f9e784e5abf
 sha512:928922686c4caf32175f5236a7f964e9925d10a74dc6d8344a8bd08b23c228ff5792573987d7895f628f39c4f4ebe39a7367d7aeb16aaa0cd324ac1d53664e61
 sha512:eab7224ce374a0a4babcb2db25e24836247f38b87806ad9be9e5ba4daac2f5b814fc0cbdfd9f1f8499b3c9a6c1b38fe08974cce49883ab4ccd04462fd2f9507f
@@ -61,20 +76,27 @@ sha512:eab7224ce374a0a4babcb2db25e24836247f38b87806ad9be9e5ba4daac2f5b814fc0cbdf
 sha512:f7083a38644880d25539488313e9e5b41a4d431a0e383945129ad2c36e3c1d0f28928a424641bb1363c12b6e770578102566acea73baf1ce8ee15336f5ba2446
 )
 
-test_alg()
+test_alg_with_block_size()
 {
        local alg=$1
-       local -n vals=${alg}_vals
+       local block_size=$2
+       local -n vals=${alg}_vals_bsize${block_size}
        local i
        local file_size
        local expected actual salt_arg
 
-       _fsv_scratch_begin_subtest "Check for expected measurement values ($alg)"
+       _fsv_scratch_begin_subtest "Testing alg=$alg, block_size=$block_size if supported"
 
-       if ! _fsv_have_hash_algorithm $alg $fsv_file; then
-               if [ "$alg" = sha256 ]; then
-                       _fail "Something is wrong - sha256 hash should always be available"
+       if ! _fsv_can_enable $fsv_file --hash-alg=$alg --block-size=$block_size
+       then
+               # Since this is after _require_scratch_verity, sha256 with
+               # FSV_BLOCK_SIZE must be supported.
+               if [ "$alg" = "sha256" -a "$block_size" = "$FSV_BLOCK_SIZE" ]
+               then
+                       _fail "Failed to enable verity with default params"
                fi
+               # This combination of parameters is unsupported; skip it.
+               echo "alg=$alg, block_size=$block_size is unsupported" >> $seqres.full
                return 0
        fi
 
@@ -85,7 +107,8 @@ test_alg()
 
                head -c $file_size /dev/zero > $fsv_orig_file
                cp $fsv_orig_file $fsv_file
-               _fsv_enable --hash-alg=$alg $salt_arg $fsv_file
+               _fsv_enable $fsv_file --hash-alg=$alg --block-size=$block_size \
+                       $salt_arg
                actual=$(_fsv_measure $fsv_file)
                if [ "$actual" != "$expected" ]; then
                        echo "Mismatch: expected $expected, kernel calculated $actual (file_size=$file_size)"
@@ -96,7 +119,9 @@ test_alg()
 }
 
 for alg in ${algs[@]}; do
-       test_alg $alg
+       for block_size in ${block_sizes[@]}; do
+               test_alg_with_block_size $alg $block_size
+       done
 done
 
 # success, all done
index 77bec43ed49023115e819f546ec03a490638421f..5ad70773aaadd9cbc47295f1b79028503cbdca9c 100644 (file)
@@ -1,5 +1,9 @@
 QA output created by 575
 
-# Check for expected measurement values (sha256)
+# Testing alg=sha256, block_size=1024 if supported
 
-# Check for expected measurement values (sha512)
+# Testing alg=sha256, block_size=4096 if supported
+
+# Testing alg=sha512, block_size=1024 if supported
+
+# Testing alg=sha512, block_size=4096 if supported
index 3ef04953a27cc3544deeff511546e05e85104721..c8862de2d32173f4710759058d5441ba32ffd8e0 100755 (executable)
@@ -28,6 +28,7 @@ _supported_fs generic
 _require_scratch_verity
 _require_scratch_encryption
 _require_command "$KEYCTL_PROG" keyctl
+_require_fsverity_corruption
 _disable_fsverity_signatures
 
 _scratch_mkfs_encrypted_verity &>> $seqres.full
index 98c3888f84a67d9ca698afb80642b82e304c7722..bbbfdb0aa6642f4da76cb9bd1268b85934871878 100755 (executable)
@@ -38,6 +38,11 @@ sigfile=$tmp.sig
 otherfile=$SCRATCH_MNT/otherfile
 othersigfile=$tmp.othersig
 
+sign()
+{
+       _fsv_sign "$@" | _filter_scratch | _filter_fsverity_digest
+}
+
 # Setup
 
 echo -e "\n# Generating certificates and private keys"
@@ -57,14 +62,13 @@ _enable_fsverity_signatures
 echo -e "\n# Generating file and signing it for fs-verity"
 head -c 100000 /dev/zero > $fsv_orig_file
 for suffix in '' '.2'; do
-       _fsv_sign $fsv_orig_file $sigfile$suffix --key=$keyfile$suffix \
-               --cert=$certfile$suffix | _filter_scratch
+       sign $fsv_orig_file $sigfile$suffix --key=$keyfile$suffix \
+               --cert=$certfile$suffix
 done
 
 echo -e "\n# Signing a different file for fs-verity"
 head -c 100000 /dev/zero | tr '\0' 'X' > $otherfile
-_fsv_sign $otherfile $othersigfile --key=$keyfile --cert=$certfile \
-       | _filter_scratch
+sign $otherfile $othersigfile --key=$keyfile --cert=$certfile
 
 # Actual tests
 
@@ -106,24 +110,23 @@ _fsv_enable $fsv_file --signature=$tmp.malformed_sig |& _filter_scratch
 
 echo -e "\n# Testing salt"
 reset_fsv_file
-_fsv_sign $fsv_orig_file $sigfile.salted --key=$keyfile --cert=$certfile \
-       --salt=abcd | _filter_scratch
+sign $fsv_orig_file $sigfile.salted --key=$keyfile --cert=$certfile --salt=abcd
 _fsv_enable $fsv_file --signature=$sigfile.salted --salt=abcd
 cmp $fsv_file $fsv_orig_file
 
 echo -e "\n# Testing non-default hash algorithm"
-if _fsv_have_hash_algorithm sha512 $fsv_file; then
+if _fsv_can_enable $fsv_file --hash-alg=sha512; then
        reset_fsv_file
-       _fsv_sign $fsv_orig_file $sigfile.sha512 --key=$keyfile \
-               --cert=$certfile --hash-alg=sha512 > /dev/null
+       sign $fsv_orig_file $sigfile.sha512 --key=$keyfile --cert=$certfile \
+               --hash-alg=sha512 > /dev/null
        _fsv_enable $fsv_file --signature=$sigfile.sha512 --hash-alg=sha512
        cmp $fsv_file $fsv_orig_file
 fi
 
 echo -e "\n# Testing empty file"
+rm -f $fsv_file
 echo -n > $fsv_file
-_fsv_sign $fsv_file $sigfile.emptyfile --key=$keyfile --cert=$certfile | \
-               _filter_scratch
+sign $fsv_file $sigfile.emptyfile --key=$keyfile --cert=$certfile
 _fsv_enable $fsv_file --signature=$sigfile.emptyfile
 
 # success, all done
index 0ca417c42d8e6bde1fbdee307065142601151daa..4f360d57b2b5fbb3a0d6b1cb16079d15fb083588 100644 (file)
@@ -9,11 +9,11 @@ QA output created by 577
 # Enabling fs.verity.require_signatures
 
 # Generating file and signing it for fs-verity
-Signed file 'SCRATCH_MNT/file' (sha256:ecabbfca4efd69a721be824965da10d27900b109549f96687b35a4d91d810dac)
-Signed file 'SCRATCH_MNT/file' (sha256:ecabbfca4efd69a721be824965da10d27900b109549f96687b35a4d91d810dac)
+Signed file 'SCRATCH_MNT/file' (sha256:<digest>)
+Signed file 'SCRATCH_MNT/file' (sha256:<digest>)
 
 # Signing a different file for fs-verity
-Signed file 'SCRATCH_MNT/otherfile' (sha256:b2a419c5a8c767a78c6275d6729794bf51e52ddf8713e31d12a93d61d961f49f)
+Signed file 'SCRATCH_MNT/otherfile' (sha256:<digest>)
 
 # Enabling verity with valid signature (should succeed)
 
@@ -33,9 +33,9 @@ ERROR: FS_IOC_ENABLE_VERITY failed on 'SCRATCH_MNT/file.fsv': Key was rejected b
 ERROR: FS_IOC_ENABLE_VERITY failed on 'SCRATCH_MNT/file.fsv': Bad message
 
 # Testing salt
-Signed file 'SCRATCH_MNT/file' (sha256:1cb173bcd199133eb80e9ea4f0f741001b9e73227aa8812685156f2bc8ff45f5)
+Signed file 'SCRATCH_MNT/file' (sha256:<digest>)
 
 # Testing non-default hash algorithm
 
 # Testing empty file
-Signed file 'SCRATCH_MNT/file.fsv' (sha256:3d248ca542a24fc62d1c43b916eae5016878e2533c88238480b26128a1f1af95)
+Signed file 'SCRATCH_MNT/file.fsv' (sha256:<digest>)
index 01929a280f8c3406ee4e0120fc638ed3bc6078f3..e8bb97f7b96c463a8876e7824df5ca0cb965b700 100755 (executable)
@@ -7,7 +7,7 @@
 # Make sure that we can handle multiple mmap writers to the same file.
 
 . ./common/preamble
-_begin_fstest auto quick rw clone
+_begin_fstest auto quick rw clone fiemap
 
 # Override the default cleanup function.
 _cleanup()
@@ -24,6 +24,7 @@ _cleanup()
 _supported_fs generic
 _require_test_program "mmap-write-concurrent"
 _require_command "$FILEFRAG_PROG" filefrag
+_require_xfs_io_command "fiemap"
 _require_test_reflink
 _require_cp_reflink
 
@@ -41,6 +42,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
 filesz=$((blksz * 4))
 _pwrite_byte 0x61 0 $filesz $testdir/file1 >> $seqres.full
 _cp_reflink $testdir/file1 $testdir/file2 >> $seqres.full
index 19b047ac43256c2e9aa115edc7ab6243d7938bc8..ebfeea1d17bbe17d92076af9386ea8327460bbcf 100755 (executable)
@@ -25,6 +25,7 @@ _require_user
 _require_quota
 _require_xfs_io_command "falloc"
 _require_scratch
+_require_odirect
 
 cat > $tmp.awk << ENDL
 {
index 563ff65e3b1b3a00138a0eff348beb6896b59b3a..a915a73e40f488cdfc141681cda54b1e2121e49e 100755 (executable)
@@ -35,6 +35,8 @@ _require_metadata_journaling $SCRATCH_DEV
 _init_flakey
 _mount_flakey
 
+_require_congruent_file_oplen $SCRATCH_MNT 65536
+
 # Create our test file with two 256Kb extents, one at file offset 0 and the
 # other at file offset 256Kb.
 $XFS_IO_PROG -f -c "pwrite -S 0xa3 0 256K" \
index c03cf1fca20368c28db563233a7b730fef76af12..bfc7407a1c08bce4c00057017ac357587a95da10 100755 (executable)
@@ -48,7 +48,7 @@ fs_stress()
 {
        local target=$1
 
-       $FSSTRESS_PROG -n 50 -p 3 -d $target >/dev/null
+       $FSSTRESS_PROG -n 50 -p 3 -d $target >>$seqres.full
        sync
 }
 
index 5efc5136f31a086edbf936f7e720a9ddeecc6009..4de50e2a4ec938332cfd9c9f8991be379094defc 100755 (executable)
@@ -24,9 +24,11 @@ _require_test
 _require_odirect
 _require_test_program "splice-test"
 
-$here/src/splice-test -r $TEST_DIR/a
+diosize=`_min_dio_alignment $TEST_DEV`
+
+$here/src/splice-test -s $diosize -r $TEST_DIR/a
 $here/src/splice-test -rd $TEST_DIR/a
-$here/src/splice-test $TEST_DIR/a
+$here/src/splice-test -s $diosize $TEST_DIR/a
 $here/src/splice-test -d $TEST_DIR/a
 
 # success, all done
index d61811ee9c7003c42556a1150f1fcd349da9d590..e9fffd1da472a296af11d7487c56491b157666eb 100644 (file)
@@ -1,7 +1,5 @@
 QA output created by 591
 concurrent reader with O_DIRECT
-concurrent reader with O_DIRECT
-concurrent reader without O_DIRECT
 concurrent reader without O_DIRECT
 sequential reader with O_DIRECT
 sequential reader without O_DIRECT
index ff985ca61915107bd6740bb9c81d5e1940cd4a52..a3035489aea387459c956d509e0e167406b5fb6d 100755 (executable)
@@ -32,8 +32,10 @@ _require_test
 _require_sysctl_variable fs.protected_symlinks
 _require_sysctl_variable fs.protected_hardlinks
 _require_user fsgqa2
+_require_group fsgqa2
 # Do this SECOND so that qa_user is fsgqa, and _user_do uses that account
 _require_user fsgqa
+_require_group fsgqa
 _require_symlinks
 
 OWNER=fsgqa2
index 769c1b1a1ad94b7e9a6e9943b1553d5efc0e4207..31b7fde48868752f10bd27fbf482d9e4459c197f 100755 (executable)
@@ -32,8 +32,10 @@ _require_test
 _require_sysctl_variable fs.protected_regular
 _require_sysctl_variable fs.protected_fifos
 _require_user fsgqa2
+_require_group fsgqa2
 # Do this SECOND so that qa_user is fsgqa, and _user_do uses that account
 _require_user fsgqa
+_require_group fsgqa
 _require_chmod
 
 USER1=fsgqa2
index 3c6b76a4b604b9c0dbe5903f66cae5b397a6afeb..1109a52c65914dc3d4a4feb062d3779f43dac16a 100755 (executable)
@@ -22,10 +22,14 @@ _require_scratch
 _scratch_mkfs > /dev/null 2>&1
 _scratch_mount
 for i in $(seq 0 500); do
-       $XFS_IO_PROG -c "pwrite 0 4K" $SCRATCH_MNT/$i >/dev/null 2>&1
+       $XFS_IO_PROG -f -c "pwrite 0 4K" $SCRATCH_MNT/$i >/dev/null
 done
-_scratch_unmount &
-_scratch_mount
+# For overlayfs, avoid unmounting the base fs after _scratch_mount tries to
+# mount the base fs.  Delay the mount attempt by a small amount in the hope
+# that the mount() call will try to lock s_umount /after/ umount has already
+# taken it.
+$UMOUNT_PROG $SCRATCH_MNT &
+sleep 0.01s ; _scratch_mount
 wait
 
 echo "Silence is golden"
index 1db58491cf2165db47b71f2d4046d18912b2b31e..7e814d5ba1dba8ab551006960126a08969687921 100755 (executable)
@@ -7,12 +7,13 @@
 # Test per-inode DAX flag by mmap direct/buffered IO.
 #
 . ./common/preamble
-_begin_fstest auto attr quick dax
+_begin_fstest auto attr quick dax prealloc
 
 # Import common functions.
 . ./common/filter
 
 _supported_fs generic
+_require_hugepages
 _require_scratch_dax_mountopt "dax=always"
 _require_test_program "feature"
 _require_test_program "t_mmap_dio"
index f75cf979b2d3d714c8e53fddfc4ef93b2b779166..18cfcfff59bceee4edef7056c4ef36353288dc77 100755 (executable)
@@ -9,7 +9,7 @@
 # and the respective range return zeroes on subsequent reads.
 #
 . ./common/preamble
-_begin_fstest auto quick prealloc zero
+_begin_fstest auto quick prealloc zero punch
 
 # Import common functions.
 . ./common/filter
index 06630f2249c74bd32b7897159191b5ee51bd5a7f..3e2a587c8f9019e47fbe0595c62bc9bc86ebba7e 100755 (executable)
@@ -16,6 +16,7 @@ _begin_fstest auto quick rw
 # real QA test starts here
 _supported_fs generic
 _require_scratch
+_require_scratch_delalloc
 
 _scratch_mkfs >>$seqres.full 2>&1
 _scratch_mount
index 4979306d56e3b92f9d585011dd84ba7e9c957c71..9411229874c57668289fd43826b8b67e13a823dd 100755 (executable)
@@ -21,11 +21,10 @@ _require_odirect
 
 stat_loop()
 {
-       trap "wait; exit" SIGTERM
        local filepath=$1
        local blocks
 
-       while :; do
+       while [ -e "$loop_file" ]; do
                blocks=$(stat -c %b $filepath)
                if [ $blocks -eq 0 ]; then
                    echo "error: stat(2) reported zero blocks"
@@ -39,6 +38,8 @@ _scratch_mount
 
 $XFS_IO_PROG -f -s -c "pwrite -b 64K 0 64K" $SCRATCH_MNT/foo > /dev/null
 
+loop_file=$tmp.loopfile
+touch $loop_file
 stat_loop $SCRATCH_MNT/foo &
 loop_pid=$!
 
@@ -64,6 +65,7 @@ for ((i = 0; i < 2000; i++)); do
        $XFS_IO_PROG -d -c "pwrite -b 64K 0 64K" $SCRATCH_MNT/foo > /dev/null
 done
 
+rm -f $loop_file
 kill $loop_pid &> /dev/null
 wait
 
index 538b480ba7998c660cb120853137d840a2484665..5b0b02c5e4c95e3cfea34b6c1e9da973b7ea3e85 100755 (executable)
@@ -8,7 +8,7 @@
 # fsx ops to limit the testing time to be an auto group test.
 #
 . ./common/preamble
-_begin_fstest auto rw io_uring stress
+_begin_fstest auto rw io_uring stress soak
 
 # Import common functions.
 . ./common/filter
@@ -33,6 +33,7 @@ fsx_args+=(-N $nr_ops)
 fsx_args+=(-p $((nr_ops / 100)))
 fsx_args+=(-o $op_sz)
 fsx_args+=(-l $file_sz)
+test -n "$SOAK_DURATION" && fsx_args+=(--duration="$SOAK_DURATION")
 
 run_fsx "${fsx_args[@]}" | sed -e '/^fsx.*/d'
 
index 3bb3112e9902bc21434f0c05e3935f29923c29dd..a977870023b3ac3267d7c17822d71f047ace832b 100755 (executable)
@@ -8,7 +8,7 @@
 # fsx ops to limit the testing time to be an auto group test.
 #
 . ./common/preamble
-_begin_fstest auto rw io_uring stress
+_begin_fstest auto rw io_uring stress soak
 
 # Import common functions.
 . ./common/filter
@@ -39,6 +39,7 @@ fsx_args+=(-r $min_dio_sz)
 fsx_args+=(-t $min_dio_sz)
 fsx_args+=(-w $min_dio_sz)
 fsx_args+=(-Z)
+test -n "$SOAK_DURATION" && fsx_args+=(--duration="$SOAK_DURATION")
 
 run_fsx "${fsx_args[@]}" | sed -e '/^fsx.*/d'
 
index 6e42d677c89b6bdcb647bab765b211cc262951e2..c4bdfbced3f732a6f646b1ca133a74c5014593bb 100755 (executable)
@@ -24,7 +24,7 @@
 #               retrying")
 #
 . ./common/preamble
-_begin_fstest auto rw enospc
+_begin_fstest auto rw enospc prealloc
 
 FS_SIZE=$((240*1024*1024)) # 240MB
 DEBUG=1 # set to 0 to disable debug statements in shell and c-prog
index 324251b7c642eb13d800d14792eb14ef9dc3cdc7..ea016d91a1c651ffa31dd6cf03a08f18d0677934 100755 (executable)
@@ -12,6 +12,9 @@ _begin_fstest auto quick shutdown
 . ./common/filter
 
 _supported_fs generic
+_fixed_by_kernel_commit e4826691cc7e \
+       "xfs: restore shutdown check in mapped write fault path"
+
 _require_scratch_nocheck
 _require_scratch_shutdown
 
index 89fbf256570f8dc095a84462994647572ed0ac3c..db4b6731031971e631b9064a91f23e9054c44cf2 100755 (executable)
 . ./common/preamble
 _begin_fstest auto quick verity
 
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       _restore_fsverity_signatures
+       rm -f $tmp.*
+}
+
 . ./common/filter
 . ./common/verity
 
 _supported_fs generic
 _require_scratch_verity
 _disable_fsverity_signatures
-# For the output of this test to always be the same, it has to use a specific
-# Merkle tree block size.
-if [ $FSV_BLOCK_SIZE != 4096 ]; then
-       _notrun "4096-byte verity block size not supported on this platform"
-fi
+fsv_orig_file=$SCRATCH_MNT/file
+fsv_file=$SCRATCH_MNT/file.fsv
 
 _scratch_mkfs_verity &>> $seqres.full
 _scratch_mount
-
-echo -e "\n# Creating a verity file"
-fsv_file=$SCRATCH_MNT/file
-# Always use the same file contents, so that the output of the test is always
-# the same.  Also use a file that is large enough to have multiple Merkle tree
-# levels, so that the test verifies that the blocks are returned in the expected
-# order.  A 1 MB file with SHA-256 and a Merkle tree block size of 4096 will
-# have 3 Merkle tree blocks (3*4096 bytes): two at level 0 and one at level 1.
-head -c 1000000 /dev/zero > $fsv_file
-merkle_tree_size=$((3 * FSV_BLOCK_SIZE))
-fsverity_descriptor_size=256
-_fsv_enable $fsv_file --salt=abcd
+_fsv_create_enable_file $fsv_file
 _require_fsverity_dump_metadata $fsv_file
-_fsv_measure $fsv_file
 
-echo -e "\n# Dumping Merkle tree"
-_fsv_dump_merkle_tree $fsv_file | sha256sum
+# Test FS_IOC_READ_VERITY_METADATA on a file that uses the given Merkle tree
+# block size.
+test_block_size()
+{
+       local block_size=$1
+       local digest_size=32 # assuming SHA-256
+       local i
+
+       # Create the file.  Make the file size big enough to result in multiple
+       # Merkle tree levels being needed.  The following expression computes a
+       # file size that needs 2 blocks at level 0, and thus 1 block at level 1.
+       local file_size=$((block_size * (2 * (block_size / digest_size))))
+       head -c $file_size /dev/zero > $fsv_orig_file
+       local tree_params=("--salt=abcd" "--block-size=$block_size")
+       cp $fsv_orig_file $fsv_file
+       _fsv_enable $fsv_file "${tree_params[@]}"
+
+       # Use the 'fsverity digest' command to compute the expected Merkle tree,
+       # descriptor, and file digest.
+       #
+       # Ideally we'd just hard-code expected values into the .out file and
+       # echo the actual values.  That doesn't quite work here, since all these
+       # values depend on the Merkle tree block size, and the Merkle tree block
+       # sizes that are supported (and thus get tested here) vary.  Therefore,
+       # we calculate the expected values in userspace with the help of
+       # 'fsverity digest', then do explicit comparisons with them.  This works
+       # fine as long as fsverity-utils and the kernel don't get broken in the
+       # same way, in which case generic/575 should detect the problem anyway.
+       local expected_file_digest=$(_fsv_digest $fsv_orig_file \
+               "${tree_params[@]}" \
+               --out-merkle-tree=$tmp.merkle_tree.expected \
+               --out-descriptor=$tmp.descriptor.expected)
+       local merkle_tree_size=$(_get_filesize $tmp.merkle_tree.expected)
+       local descriptor_size=$(_get_filesize $tmp.descriptor.expected)
+
+       # 'fsverity measure' should return the expected file digest.
+       local actual_file_digest=$(_fsv_measure $fsv_file)
+       if [ "$actual_file_digest" != "$expected_file_digest" ]; then
+               echo "Measure returned $actual_file_digest but expected $expected_file_digest"
+       fi
+
+       # Test dumping the Merkle tree.
+       _fsv_dump_merkle_tree $fsv_file > $tmp.merkle_tree.actual
+       if ! cmp $tmp.merkle_tree.expected $tmp.merkle_tree.actual; then
+               echo "Dumped Merkle tree didn't match"
+       fi
+
+       # Test dumping the Merkle tree in chunks.
+       for (( i = 0; i < merkle_tree_size; i += 997 )); do
+               _fsv_dump_merkle_tree $fsv_file --offset=$i --length=997
+       done > $tmp.merkle_tree.actual
+       if ! cmp $tmp.merkle_tree.expected $tmp.merkle_tree.actual; then
+               echo "Dumped Merkle tree (in chunks) didn't match"
+       fi
 
-echo -e "\n# Dumping Merkle tree (in chunks)"
-# The above test may get the whole tree in one read, so also try reading it in
-# chunks.
-for (( i = 0; i < merkle_tree_size; i += 997 )); do
-       _fsv_dump_merkle_tree $fsv_file --offset=$i --length=997
-done | sha256sum
+       # Test dumping the descriptor.
+       _fsv_dump_descriptor $fsv_file > $tmp.descriptor.actual
+       if ! cmp $tmp.descriptor.expected $tmp.descriptor.actual; then
+               echo "Dumped descriptor didn't match"
+       fi
 
-echo -e "\n# Dumping descriptor"
-# Note that the hash that is printed here should be the same hash that was
-# printed by _fsv_measure above.
-_fsv_dump_descriptor $fsv_file | sha256sum
+       # Test dumping the descriptor in chunks.
+       for (( i = 0; i < descriptor_size; i += 13 )); do
+               _fsv_dump_descriptor $fsv_file --offset=$i --length=13
+       done > $tmp.descriptor.actual
+       if ! cmp $tmp.descriptor.expected $tmp.descriptor.actual; then
+               echo "Dumped descriptor (in chunks) didn't match"
+       fi
+}
 
-echo -e "\n# Dumping descriptor (in chunks)"
-for (( i = 0; i < fsverity_descriptor_size; i += 13 )); do
-       _fsv_dump_descriptor $fsv_file --offset=$i --length=13
-done | sha256sum
+# Always test FSV_BLOCK_SIZE.  Also test some other block sizes if they happen
+# to be supported.
+_fsv_scratch_begin_subtest "Testing block_size=FSV_BLOCK_SIZE"
+test_block_size $FSV_BLOCK_SIZE
+for block_size in 1024 4096 16384 65536; do
+       _fsv_scratch_begin_subtest "Testing block_size=$block_size if supported"
+       if (( block_size == FSV_BLOCK_SIZE )); then
+               continue # Skip redundant test case.
+       fi
+       if ! _fsv_can_enable $fsv_file --block-size=$block_size; then
+               echo "block_size=$block_size is unsupported" >> $seqres.full
+               continue
+       fi
+       test_block_size $block_size
+done
 
 # success, all done
 status=0
index 912826d30f5c1191f60f8ac8dfc1b912784050e0..0ea19ee55946ce2196652ce35f9fe4376a3295de 100644 (file)
@@ -1,16 +1,11 @@
 QA output created by 624
 
-# Creating a verity file
-sha256:11e4f886bf2d70a6ef3a8b6ce8e8c62c9e5d3263208b9f120ae46791f124be73
+# Testing block_size=FSV_BLOCK_SIZE
 
-# Dumping Merkle tree
-db88cdad554734cd648a1bfbb5be7f86646c54397847aab0b3f42a28829fed17  -
+# Testing block_size=1024 if supported
 
-# Dumping Merkle tree (in chunks)
-db88cdad554734cd648a1bfbb5be7f86646c54397847aab0b3f42a28829fed17  -
+# Testing block_size=4096 if supported
 
-# Dumping descriptor
-11e4f886bf2d70a6ef3a8b6ce8e8c62c9e5d3263208b9f120ae46791f124be73  -
+# Testing block_size=16384 if supported
 
-# Dumping descriptor (in chunks)
-11e4f886bf2d70a6ef3a8b6ce8e8c62c9e5d3263208b9f120ae46791f124be73  -
+# Testing block_size=65536 if supported
index e82a42db587ef8417f6a36c94c39d8ebc57d598f..9a7359e6e075a4afd90bbaab9bfc843bca4da939 100755 (executable)
@@ -17,7 +17,7 @@
 # size < page size.
 #
 . ./common/preamble
-_begin_fstest auto aio rw stress
+_begin_fstest auto aio rw stress prealloc
 
 fio_config=$tmp.fio
 fio_out=$tmp.fio.out
index 7dc6dfcdd2ece8d6c143d867e46323252273de89..380a7f0b32546c4ac9f2dd89bca94434fab85356 100755 (executable)
@@ -11,7 +11,7 @@
 # 5ffce3cc22a0 ("xfs: force the log after remapping a synchronous-writes file")
 
 . ./common/preamble
-_begin_fstest auto quick rw clone
+_begin_fstest auto quick rw clone eio
 
 # Override the default cleanup function.
 _cleanup()
index f501555e3f10319916aeb6889634d1be543c32eb..dbd7ada84b642c9c1978c4586008e481dbbf27ab 100755 (executable)
@@ -11,7 +11,7 @@
 # 5ffce3cc22a0 ("xfs: force the log after remapping a synchronous-writes file")
 
 . ./common/preamble
-_begin_fstest auto quick rw copy_range
+_begin_fstest auto quick rw copy_range eio
 
 # Override the default cleanup function.
 _cleanup()
index 219f7a05e7fc4ba0fd66c949aa9bd90c53a266ac..ff1bb11301b62976de079f9e633187ca0d888e5a 100755 (executable)
@@ -40,6 +40,8 @@ _require_scratch
 _require_attrs trusted
 _supported_fs ^overlay
 _require_extra_fs overlay
+_fixed_by_kernel_commit 6da1b4b1ab36 \
+       "xfs: fix an ABBA deadlock in xfs_rename"
 
 _scratch_mkfs >> $seqres.full
 _scratch_mount
index 10e658b68a551c24debbf700f27e37b06546d84a..afb9df986b5c5d6bd679c54c858d690380f1486d 100755 (executable)
@@ -24,7 +24,7 @@ _scratch_mount
 touch "$SCRATCH_MNT/swap"
 $CHATTR_PROG +C "$SCRATCH_MNT/swap" >> $seqres.full 2>&1
 chmod 0600 "$SCRATCH_MNT/swap"
-_pwrite_byte 0x61 0 $(get_page_size) "$SCRATCH_MNT/swap" >> $seqres.full
+_pwrite_byte 0x61 0 $(_get_page_size) "$SCRATCH_MNT/swap" >> $seqres.full
 "$here/src/mkswap" "$SCRATCH_MNT/swap"
 "$here/src/swapon" "$SCRATCH_MNT/swap"
 swapoff "$SCRATCH_MNT/swap" >/dev/null 2>&1
index 41b3504b08d7412f453927255ea08670a5fea3df..124f2e1dae9f4ab23f93c3dd44c891865ac09a8d 100755 (executable)
@@ -9,7 +9,7 @@
 # assignment to unsigned sis->pages in iomap_swapfile_activate").
 #
 . ./common/preamble
-_begin_fstest auto quick swap
+_begin_fstest auto quick swap collapse
 
 # Import common functions
 . ./common/filter
@@ -40,7 +40,7 @@ make_unaligned_swapfile()
 
 _scratch_mkfs >> $seqres.full 2>&1
 _scratch_mount
-psize=`get_page_size`
+psize=`_get_page_size`
 bsize=`_get_file_block_size $SCRATCH_MNT`
 # Due to we need page-unaligned blocks, so blocksize < pagesize is necessary.
 # If not, try to make a smaller enough block size
index c0e274d84338f8f0a00a8a3dd2d4693d1b0704ce..4d0c41fd5d5142d071640b8c881139979764f356 100755 (executable)
@@ -8,7 +8,7 @@
 # bugs in the xattr code.
 #
 . ./common/preamble
-_begin_fstest auto soak attr long_rw stress
+_begin_fstest auto soak attr long_rw stress smoketest
 
 _cleanup()
 {
@@ -49,6 +49,7 @@ for verb in attr_remove removefattr; do
 done
 args+=('-f' "setfattr=20")
 args+=('-f' "attr_set=60")     # sets larger xattrs
+test -n "$SOAK_DURATION" && args+=(--duration="$SOAK_DURATION")
 
 $FSSTRESS_PROG "${args[@]}" $FSSTRESS_AVOID -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus >> $seqres.full
 
index 79d3f17cb4ed9313c426f5ade9bcec51236726a9..027df5570f81ca5c56762b3ffac14c64f82b17fa 100755 (executable)
@@ -15,6 +15,9 @@
 _begin_fstest auto quick recoveryloop shutdown
 
 # real QA test starts here
+_supported_fs generic
+_fixed_by_kernel_commit 50d25484bebe \
+       "xfs: sync lazy sb accounting on quiesce of read-only mounts"
 
 _require_scratch
 _require_scratch_shutdown
index d7bf5697e1fff70361725d191d750a6974685a32..3b3544ff49c3283ac387560094061f84dcc7dacb 100755 (executable)
@@ -74,14 +74,14 @@ snap_loop_fs() {
 
 fsstress=($FSSTRESS_PROG $FSSTRESS_AVOID -d "$loopmnt" -n 999999 -p "$((LOAD_FACTOR * 4))")
 
-for i in $(seq 1 $((25 * TIME_FACTOR)) ); do
+while _soak_loop_running $((25 * TIME_FACTOR)); do
        touch $scratch_aliveflag
        snap_loop_fs >> $seqres.full 2>&1 &
 
        if ! _mount $loopimg $loopmnt -o loop; then
                rm -f $scratch_aliveflag
                _metadump_dev $loopimg $seqres.loop.$i.md
-               _fail "iteration $i loopimg mount failed"
+               _fail "iteration $SOAK_LOOPIDX loopimg mount failed"
                break
        fi
 
@@ -126,12 +126,12 @@ for i in $(seq 1 $((25 * TIME_FACTOR)) ); do
        done
        if [ $is_unmounted -ne 0 ];then
                cat $tmp.unmount.err
-               _fail "iteration $i scratch unmount failed"
+               _fail "iteration $SOAK_LOOPIDX scratch unmount failed"
        fi
        _dmerror_load_working_table
        if ! _dmerror_mount; then
                _metadump_dev $DMERROR_DEV $seqres.scratch.$i.md
-               _fail "iteration $i scratch mount failed"
+               _fail "iteration $SOAK_LOOPIDX scratch mount failed"
        fi
 done
 
index a6aabfaf724233837c1aa43b54728c647606bbe7..2e156dfefb87c27b866934d97a4b2df4722f3175 100755 (executable)
@@ -16,7 +16,7 @@
 # unshare a hole.
 #
 . ./common/preamble
-_begin_fstest auto clone unshare
+_begin_fstest auto clone unshare punch
 
 # Override the default cleanup function.
 _cleanup()
@@ -33,6 +33,9 @@ _cleanup()
 
 # Modify as appropriate.
 _supported_fs generic
+_fixed_by_kernel_commit 72a048c1056a \
+       "xfs: only set IOMAP_F_SHARED when providing a srcmap to a write"
+
 _require_cp_reflink
 _require_test_reflink
 _require_test_program "punch-alternating"
index 05a48ef0fd1e0849d50db183f37ec10a00512cba..e152c39858fdd76371a2c2c2e59ac64d226aa0a9 100755 (executable)
@@ -8,7 +8,10 @@
 # hotplugging to shake out bugs in the write path.
 #
 . ./common/preamble
-_begin_fstest auto rw stress
+_begin_fstest auto rw stress soak
+
+[ "$FSTYP" = "xfs" ] && _fixed_by_kernel_commit ecd49f7a36fb \
+        "xfs: fix per-cpu CIL structure aggregation racing with dying cpus"
 
 # Override the default cleanup function.
 _cleanup()
@@ -60,13 +63,25 @@ sentinel_file=$tmp.hotplug
 touch $sentinel_file
 exercise_cpu_hotplug &
 
+fsstress_args=(-w -d $stress_dir)
+
 # Cap the number of fsstress threads at one per hotpluggable CPU if we exceed
 # 1024 IO threads, per maintainer request.
 nr_cpus=$((LOAD_FACTOR * nr_hotplug_cpus))
 test "$nr_cpus" -gt 1024 && nr_cpus="$nr_hotplug_cpus"
+fsstress_args+=(-p $nr_cpus)
+if [ -n "$SOAK_DURATION" ]; then
+       test "$SOAK_DURATION" -lt 10 && SOAK_DURATION=10
+       fsstress_args+=(--duration="$((SOAK_DURATION / 10))")
+fi
+
+nr_ops=$((2500 * TIME_FACTOR))
+fsstress_args+=(-n $nr_ops)
+for ((i = 0; i < 10; i++)); do
+       $FSSTRESS_PROG $FSSTRESS_AVOID -w "${fsstress_args[@]}" >> $seqres.full
+       _test_cycle_mount
+done
 
-nr_ops=$((25000 * TIME_FACTOR))
-$FSSTRESS_PROG $FSSTRESS_AVOID -w -d $stress_dir -n $nr_ops -p $nr_cpus >> $seqres.full
 rm -f $sentinel_file
 
 # success, all done
index 42af175f6b3356ae4d6d157aa08afb2d1e56fb73..d7b74e0e64cdc835277ae6c79c9259a140220ede 100755 (executable)
@@ -10,7 +10,7 @@
 # the golden output; we can only compare to a check file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
index 8c18c136f5e353e22e3c9a1be19be4b4161e46dd..a63c71384ef361f9d9e953aa740f338a37123552 100755 (executable)
@@ -10,7 +10,7 @@
 # the golden output; we can only compare to a check file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
index da7eaf4d05df54469f590b84c4463e9e6129a69c..f73ae81bec69575e606495894e51100dd5345a4d 100755 (executable)
@@ -10,7 +10,7 @@
 # the golden output; we can only compare to a check file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
 
 # Import common functions.
 . ./common/filter
index ec8366e3a1388275ab3dc0671c16ce60f57f93b5..a131b1d1ccf6e991cb283aff499edd07ac445447 100755 (executable)
@@ -11,7 +11,7 @@
 # the golden output; we can only compare to a check file.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
 
 # Import common functions.
 . ./common/filter
index a296f88b3827fcb46ced08f718be24187330b63e..a5cbadaaa5609e1eb8dbe8868aa1094ecd7c7809 100755 (executable)
@@ -12,7 +12,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
index e42077e2ce5c62bfba355e81a25e0439fd9e7369..ccc2d7950df0f153d5b0cf7b06c1923833c1da0f 100755 (executable)
@@ -12,7 +12,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
index 7d339aadbcd4c2067de863ba6b0ba7da223be802..bc17dc5e59e756aeaaa6c2453d0625a29a11b580 100755 (executable)
@@ -12,7 +12,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
index 295a6a8d7e7b5ae4dd316bcc5d90c1a2b06cd756..788dae7e3d66a6882adc50fd7fee05ed0acee5c6 100755 (executable)
@@ -13,7 +13,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
index a09a467f0983fba983daa797e930e111447c7e0a..3fdfb4e00a72e7a54630d0700d0a9ace76e13d1b 100755 (executable)
@@ -17,7 +17,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
 
 # Import common functions.
 . ./common/filter
index dcd8c86173f7dd0c23f4e96ce344824eaf879fa2..658a5b7004782c3231538f0cac535a2dadd27338 100755 (executable)
@@ -13,7 +13,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
index 4dc313fc0d64ec4106cfb3bb2b2926b3c76fbc90..3009101fdcab261153035a1f57a6a2bf552c7700 100755 (executable)
@@ -15,7 +15,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
index 6f21aaff8d19f1e368657dee5f27b6deac6e4660..86ba5787202e878f3833a10c3c441700376cf697 100755 (executable)
@@ -15,7 +15,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
index 47dd4ce4be58ca652ae2b596cf8ac482e6e27079..5e4f306219d22acde1e2215bb94729059a2fe554 100755 (executable)
@@ -16,7 +16,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
index e9f07feb9664c745ce5000c3dcb62ec8794f78cf..9f1cb1be61f1ffb87bdad5b2124093758586bb77 100755 (executable)
@@ -16,7 +16,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
 
 # Import common functions.
 . ./common/filter
index b703713b86a0179789c8d42723d5e0402cd008cf..41e03ae8e6c485d3558f011d073e10ff277c1269 100755 (executable)
@@ -16,7 +16,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
 
 # Import common functions.
 . ./common/filter
index 8e744e8945b685b38d6c242f62761e122dad27e0..c881604271994d194e1c1903297035172a221758 100755 (executable)
@@ -16,7 +16,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
 
 # Import common functions.
 . ./common/filter
index 4a895d90670a6d90cafde6f9047640fdee39d1c2..67de167405e96f43751219b38f9e62728ec0f672 100755 (executable)
@@ -43,7 +43,7 @@ fbytes() {
        # Different with generic/164,165, mread copies data from mmapped area
        # one-byte-at-a-time, which may cause races during reflink_range().
        # So the result of _mread_range() may be a mix of 61 and 62.
-       egrep -v '((61|62) ){15}(61|62)'
+       grep -E -v '((61|62) ){15}(61|62)'
 }
 
 reader() {
index e40e672a8583dff9fdd1ae507e7db8cce4a51d00..ac8b8c09fa5bc812a160839e8d4d27d4e7ab52f0 100755 (executable)
@@ -7,7 +7,7 @@
 # Functional test for dropping suid and sgid bits as part of a reflink.
 #
 . ./common/preamble
-_begin_fstest auto clone quick
+_begin_fstest auto clone quick perms
 
 # Import common functions.
 . ./common/filter
@@ -22,6 +22,7 @@ _require_scratch_reflink
 
 _scratch_mkfs >> $seqres.full
 _scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 1048576
 chmod a+rw $SCRATCH_MNT/
 
 setup_testfile() {
@@ -101,26 +102,14 @@ setup_testfile
 chmod a+rwxs $SCRATCH_MNT/a
 commit_and_check
 
-#Commit to a non-exec file by an unprivileged user leaves sgid.
-echo "Test 9 - qa_user, non-exec file, only sgid"
-setup_testfile
-chmod a+rw,g+rws $SCRATCH_MNT/a
-commit_and_check "$qa_user"
-
 #Commit to a group-exec file by an unprivileged user clears sgid
-echo "Test 10 - qa_user, group-exec file, only sgid"
+echo "Test 9 - qa_user, group-exec file, only sgid"
 setup_testfile
 chmod a+rw,g+rwxs $SCRATCH_MNT/a
 commit_and_check "$qa_user"
 
-#Commit to a user-exec file by an unprivileged user clears sgid
-echo "Test 11 - qa_user, user-exec file, only sgid"
-setup_testfile
-chmod a+rw,u+x,g+rws $SCRATCH_MNT/a
-commit_and_check "$qa_user"
-
 #Commit to a all-exec file by an unprivileged user clears sgid.
-echo "Test 12 - qa_user, all-exec file, only sgid"
+echo "Test 10 - qa_user, all-exec file, only sgid"
 setup_testfile
 chmod a+rwx,g+rwxs $SCRATCH_MNT/a
 commit_and_check "$qa_user"
index 0817857d68c6c61b10b1e1333049f2fb88ab350f..4276fa016a099c7df70b855fd574837665d8368a 100644 (file)
@@ -47,25 +47,13 @@ Test 8 - root, all-exec file
 3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
 6777 -rwsrwsrwx SCRATCH_MNT/a
 
-Test 9 - qa_user, non-exec file, only sgid
-310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
-2666 -rw-rwSrw- SCRATCH_MNT/a
-3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
-2666 -rw-rwSrw- SCRATCH_MNT/a
-
-Test 10 - qa_user, group-exec file, only sgid
+Test 9 - qa_user, group-exec file, only sgid
 310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
 2676 -rw-rwsrw- SCRATCH_MNT/a
 3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
 676 -rw-rwxrw- SCRATCH_MNT/a
 
-Test 11 - qa_user, user-exec file, only sgid
-310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
-2766 -rwxrwSrw- SCRATCH_MNT/a
-3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
-2766 -rwxrwSrw- SCRATCH_MNT/a
-
-Test 12 - qa_user, all-exec file, only sgid
+Test 10 - qa_user, all-exec file, only sgid
 310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
 2777 -rwxrwsrwx SCRATCH_MNT/a
 3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
index 920ed5f2bbf1904c9f8ddd77fd702a5db5124c2d..2ed022df8a046c4bf31aeddc305bf227998d2d1e 100755 (executable)
@@ -7,7 +7,7 @@
 # Functional test for dropping suid and sgid bits as part of a deduplication.
 #
 . ./common/preamble
-_begin_fstest auto clone quick
+_begin_fstest auto clone quick perms dedupe
 
 # Import common functions.
 . ./common/filter
@@ -23,6 +23,7 @@ _require_xfs_io_command dedupe
 
 _scratch_mkfs >> $seqres.full
 _scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 1048576
 chmod a+rw $SCRATCH_MNT/
 
 setup_testfile() {
index 23b7e54584d3c9ec44a95110bf2ec7669e5e064a..cc4309e45a04c6189887193a21befa62177603f2 100755 (executable)
@@ -12,6 +12,7 @@ _begin_fstest auto clone quick
 # Import common functions.
 . ./common/filter
 . ./common/reflink
+. ./common/attr
 
 # real QA test starts here
 
@@ -21,9 +22,11 @@ _require_user
 _require_command "$GETCAP_PROG" getcap
 _require_command "$SETCAP_PROG" setcap
 _require_scratch_reflink
+_require_attrs security
 
 _scratch_mkfs >> $seqres.full
 _scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 1048576
 chmod a+rw $SCRATCH_MNT/
 
 setup_testfile() {
index 39af90a91fb25913b38d5bff46a3e2a4bf9176cd..84146c5e04ece49290c930241d01586195a01790 100755 (executable)
@@ -9,7 +9,7 @@
 # after we mount the filesystem.
 #
 . ./common/preamble
-_begin_fstest auto quick log prealloc
+_begin_fstest auto quick log prealloc fiemap
 
 _cleanup()
 {
@@ -38,6 +38,10 @@ _require_metadata_journaling $SCRATCH_DEV
 _init_flakey
 _mount_flakey
 
+# The fiemap results in the golden output requires file allocations to align to
+# 1MB boundaries.
+_require_congruent_file_oplen $SCRATCH_MNT 1048576
+
 # Create our test file with many extents.
 # On btrfs this results in having multiple leaves of metadata full of file
 # extent items, a condition necessary to trigger the original bug.
index a0094e4878ac287bf096f55a204baec1921d8c5a..ddf975a2cf864a8aa6bc0f3099341dad8532e94f 100755 (executable)
@@ -9,7 +9,7 @@
 # space to allocate extents for the holes.
 #
 . ./common/preamble
-_begin_fstest auto quick prealloc
+_begin_fstest auto quick prealloc fiemap
 
 . ./common/filter
 . ./common/punch
index 090da795ce64b8cce62bb30e126bb599735c05dd..f41647741a7d69052a29aa7d5fc029956ad72d85 100755 (executable)
@@ -30,7 +30,7 @@ _scratch_mkfs > "$seqres.full" 2>&1
 _qmount_option usrquota
 _qmount
 
-blocksize=$(_get_block_size $SCRATCH_MNT)
+blocksize=$(_get_dir_block_size $SCRATCH_MNT)
 scratchdir=$SCRATCH_MNT/dir
 scratchfile=$SCRATCH_MNT/file
 mkdir $scratchdir
index b4cd0cd9dd2ed3e20c7424f304940306c9a9b76c..417598804bde70bb0f4e68da6d6eba6cbd21d2df 100755 (executable)
@@ -30,7 +30,7 @@ _scratch_mkfs > "$seqres.full" 2>&1
 _qmount_option usrquota
 _qmount
 
-blocksize=$(_get_block_size $SCRATCH_MNT)
+blocksize=$(_get_dir_block_size $SCRATCH_MNT)
 scratchdir=$SCRATCH_MNT/dir
 scratchfile=$SCRATCH_MNT/file
 stagedir=$SCRATCH_MNT/staging
@@ -54,6 +54,12 @@ repquota -upn $SCRATCH_MNT >> $seqres.full
 echo $(ls $scratchdir | wc -l) files in $scratchdir  >> $seqres.full
 ls -sld $scratchdir  >> $seqres.full
 
+_filter_mv_output()
+{
+       sed -e "s,cannot move .* to \(.*\):\(.*\),cannot overwrite \1:\2,g" \
+           -e 's/y[0-9]*/yXXX/g'
+}
+
 # Fail at renaming into the directory as qa_user to ensure quota enforcement
 # works
 chmod a+rwx $stagedir
@@ -62,7 +68,7 @@ for ((i = 0; i < dirents; i++)); do
        name=$(printf "y%0254d" $i)
        ln $scratchfile $stagedir/$name
        su - "$qa_user" -c "mv $stagedir/$name $scratchdir/$name" 2>&1 | \
-               _filter_scratch | sed -e 's/y[0-9]*/yXXX/g'
+               _filter_scratch | _filter_mv_output
        test "${PIPESTATUS[0]}" -ne 0 && break
 done
 repquota -upn $SCRATCH_MNT >> $seqres.full
index b74708117237c63181d548a7b63f48ba3c31fcb4..db22d5f60487a3d60f130499d73b351a56d049da 100644 (file)
@@ -1,3 +1,3 @@
 QA output created by 682
-mv: cannot move 'SCRATCH_MNT/staging/yXXX' to 'SCRATCH_MNT/dir/yXXX': Disk quota exceeded
+mv: cannot overwrite 'SCRATCH_MNT/dir/yXXX': Disk quota exceeded
 Silence is golden
index 746ead86f4f415256a3ffc1f25a47d0793c58f96..304b1a4863498ba7d1d402b89205dcdd691f502e 100755 (executable)
@@ -7,7 +7,7 @@
 # Functional test for dropping suid and sgid bits as part of a fallocate.
 #
 . ./common/preamble
-_begin_fstest auto clone quick
+_begin_fstest auto clone quick perms prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -28,6 +28,7 @@ _require_user
 _require_test
 verb=falloc
 _require_xfs_io_command $verb
+_require_congruent_file_oplen $TEST_DIR 65536
 
 junk_dir=$TEST_DIR/$seq
 junk_file=$junk_dir/a
@@ -109,26 +110,14 @@ setup_testfile
 chmod a+rwxs $junk_file
 commit_and_check "" "$verb" 64k 64k
 
-# Commit to a non-exec file by an unprivileged user leaves sgid.
-echo "Test 9 - qa_user, non-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
 # Commit to a group-exec file by an unprivileged user clears sgid
-echo "Test 10 - qa_user, group-exec file $verb, only sgid"
+echo "Test 9 - qa_user, group-exec file $verb, only sgid"
 setup_testfile
 chmod a+rw,g+rwxs $junk_file
 commit_and_check "$qa_user" "$verb" 64k 64k
 
-# Commit to a user-exec file by an unprivileged user clears sgid
-echo "Test 11 - qa_user, user-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,u+x,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
 # Commit to a all-exec file by an unprivileged user clears sgid.
-echo "Test 12 - qa_user, all-exec file $verb, only sgid"
+echo "Test 10 - qa_user, all-exec file $verb, only sgid"
 setup_testfile
 chmod a+rwx,g+rwxs $junk_file
 commit_and_check "$qa_user" "$verb" 64k 64k
index ca29f6e6760e67db735170355d0039bf42413b0d..de18ea5f64c4cdb87d8edd83dcc7b56ab7ac9369 100644 (file)
@@ -31,19 +31,11 @@ Test 8 - root, all-exec file falloc
 6777 -rwsrwsrwx TEST_DIR/683/a
 6777 -rwsrwsrwx TEST_DIR/683/a
 
-Test 9 - qa_user, non-exec file falloc, only sgid
-2666 -rw-rwSrw- TEST_DIR/683/a
-2666 -rw-rwSrw- TEST_DIR/683/a
-
-Test 10 - qa_user, group-exec file falloc, only sgid
+Test 9 - qa_user, group-exec file falloc, only sgid
 2676 -rw-rwsrw- TEST_DIR/683/a
 676 -rw-rwxrw- TEST_DIR/683/a
 
-Test 11 - qa_user, user-exec file falloc, only sgid
-2766 -rwxrwSrw- TEST_DIR/683/a
-2766 -rwxrwSrw- TEST_DIR/683/a
-
-Test 12 - qa_user, all-exec file falloc, only sgid
+Test 10 - qa_user, all-exec file falloc, only sgid
 2777 -rwxrwsrwx TEST_DIR/683/a
 777 -rwxrwxrwx TEST_DIR/683/a
 
index 4bebeff0399b4bc3558ea08e73fda2bd8c22322e..1ebffb017aa7bf9e16797979110db631f0730b3b 100755 (executable)
@@ -7,7 +7,7 @@
 # Functional test for dropping suid and sgid bits as part of a fpunch.
 #
 . ./common/preamble
-_begin_fstest auto clone quick
+_begin_fstest auto clone quick perms punch
 
 # Override the default cleanup function.
 _cleanup()
@@ -28,6 +28,7 @@ _require_user
 _require_test
 verb=fpunch
 _require_xfs_io_command $verb
+_require_congruent_file_oplen $TEST_DIR 65536
 
 junk_dir=$TEST_DIR/$seq
 junk_file=$junk_dir/a
@@ -109,26 +110,14 @@ setup_testfile
 chmod a+rwxs $junk_file
 commit_and_check "" "$verb" 64k 64k
 
-# Commit to a non-exec file by an unprivileged user leaves sgid.
-echo "Test 9 - qa_user, non-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
 # Commit to a group-exec file by an unprivileged user clears sgid
-echo "Test 10 - qa_user, group-exec file $verb, only sgid"
+echo "Test 9 - qa_user, group-exec file $verb, only sgid"
 setup_testfile
 chmod a+rw,g+rwxs $junk_file
 commit_and_check "$qa_user" "$verb" 64k 64k
 
-# Commit to a user-exec file by an unprivileged user clears sgid
-echo "Test 11 - qa_user, user-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,u+x,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
 # Commit to a all-exec file by an unprivileged user clears sgid.
-echo "Test 12 - qa_user, all-exec file $verb, only sgid"
+echo "Test 10 - qa_user, all-exec file $verb, only sgid"
 setup_testfile
 chmod a+rwx,g+rwxs $junk_file
 commit_and_check "$qa_user" "$verb" 64k 64k
index 2e084ceda4f191a12b533ad4d0732a20ba014b51..da5ada5e8a2d6ffddb39b71ae1025462d07d4649 100644 (file)
@@ -31,19 +31,11 @@ Test 8 - root, all-exec file fpunch
 6777 -rwsrwsrwx TEST_DIR/684/a
 6777 -rwsrwsrwx TEST_DIR/684/a
 
-Test 9 - qa_user, non-exec file fpunch, only sgid
-2666 -rw-rwSrw- TEST_DIR/684/a
-2666 -rw-rwSrw- TEST_DIR/684/a
-
-Test 10 - qa_user, group-exec file fpunch, only sgid
+Test 9 - qa_user, group-exec file fpunch, only sgid
 2676 -rw-rwsrw- TEST_DIR/684/a
 676 -rw-rwxrw- TEST_DIR/684/a
 
-Test 11 - qa_user, user-exec file fpunch, only sgid
-2766 -rwxrwSrw- TEST_DIR/684/a
-2766 -rwxrwSrw- TEST_DIR/684/a
-
-Test 12 - qa_user, all-exec file fpunch, only sgid
+Test 10 - qa_user, all-exec file fpunch, only sgid
 2777 -rwxrwsrwx TEST_DIR/684/a
 777 -rwxrwxrwx TEST_DIR/684/a
 
index 03447e0031f3d87ec400cfa8769a3d3625796042..e4ada8e754fbeb6112807cd0b35f3d42daabfda3 100755 (executable)
@@ -7,7 +7,7 @@
 # Functional test for dropping suid and sgid bits as part of a fzero.
 #
 . ./common/preamble
-_begin_fstest auto clone quick
+_begin_fstest auto clone quick perms zero
 
 # Override the default cleanup function.
 _cleanup()
@@ -28,6 +28,7 @@ _require_user
 _require_test
 verb=fzero
 _require_xfs_io_command $verb
+_require_congruent_file_oplen $TEST_DIR 65536
 
 junk_dir=$TEST_DIR/$seq
 junk_file=$junk_dir/a
@@ -109,26 +110,14 @@ setup_testfile
 chmod a+rwxs $junk_file
 commit_and_check "" "$verb" 64k 64k
 
-# Commit to a non-exec file by an unprivileged user leaves sgid.
-echo "Test 9 - qa_user, non-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
 # Commit to a group-exec file by an unprivileged user clears sgid
-echo "Test 10 - qa_user, group-exec file $verb, only sgid"
+echo "Test 9 - qa_user, group-exec file $verb, only sgid"
 setup_testfile
 chmod a+rw,g+rwxs $junk_file
 commit_and_check "$qa_user" "$verb" 64k 64k
 
-# Commit to a user-exec file by an unprivileged user clears sgid
-echo "Test 11 - qa_user, user-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,u+x,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
 # Commit to a all-exec file by an unprivileged user clears sgid.
-echo "Test 12 - qa_user, all-exec file $verb, only sgid"
+echo "Test 10 - qa_user, all-exec file $verb, only sgid"
 setup_testfile
 chmod a+rwx,g+rwxs $junk_file
 commit_and_check "$qa_user" "$verb" 64k 64k
index e611da3e9c8580146a455aff5167af8b75e2c369..03eef362ee6d46aec72f77f31830114583a52883 100644 (file)
@@ -31,19 +31,11 @@ Test 8 - root, all-exec file fzero
 6777 -rwsrwsrwx TEST_DIR/685/a
 6777 -rwsrwsrwx TEST_DIR/685/a
 
-Test 9 - qa_user, non-exec file fzero, only sgid
-2666 -rw-rwSrw- TEST_DIR/685/a
-2666 -rw-rwSrw- TEST_DIR/685/a
-
-Test 10 - qa_user, group-exec file fzero, only sgid
+Test 9 - qa_user, group-exec file fzero, only sgid
 2676 -rw-rwsrw- TEST_DIR/685/a
 676 -rw-rwxrw- TEST_DIR/685/a
 
-Test 11 - qa_user, user-exec file fzero, only sgid
-2766 -rwxrwSrw- TEST_DIR/685/a
-2766 -rwxrwSrw- TEST_DIR/685/a
-
-Test 12 - qa_user, all-exec file fzero, only sgid
+Test 10 - qa_user, all-exec file fzero, only sgid
 2777 -rwxrwsrwx TEST_DIR/685/a
 777 -rwxrwxrwx TEST_DIR/685/a
 
index eae3cbda5fb5308834fd43ae1267a61620c9c886..d56aa7ccc1a51ce2548f6a1acc03834a5ee1c82c 100755 (executable)
@@ -7,7 +7,7 @@
 # Functional test for dropping suid and sgid bits as part of a finsert.
 #
 . ./common/preamble
-_begin_fstest auto clone quick
+_begin_fstest auto clone insert quick perms
 
 # Override the default cleanup function.
 _cleanup()
@@ -28,6 +28,7 @@ _require_user
 _require_test
 verb=finsert
 _require_xfs_io_command $verb
+_require_congruent_file_oplen $TEST_DIR 65536
 
 junk_dir=$TEST_DIR/$seq
 junk_file=$junk_dir/a
@@ -109,26 +110,14 @@ setup_testfile
 chmod a+rwxs $junk_file
 commit_and_check "" "$verb" 64k 64k
 
-# Commit to a non-exec file by an unprivileged user leaves sgid.
-echo "Test 9 - qa_user, non-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
 # Commit to a group-exec file by an unprivileged user clears sgid
-echo "Test 10 - qa_user, group-exec file $verb, only sgid"
+echo "Test 9 - qa_user, group-exec file $verb, only sgid"
 setup_testfile
 chmod a+rw,g+rwxs $junk_file
 commit_and_check "$qa_user" "$verb" 64k 64k
 
-# Commit to a user-exec file by an unprivileged user clears sgid
-echo "Test 11 - qa_user, user-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,u+x,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
 # Commit to a all-exec file by an unprivileged user clears sgid.
-echo "Test 12 - qa_user, all-exec file $verb, only sgid"
+echo "Test 10 - qa_user, all-exec file $verb, only sgid"
 setup_testfile
 chmod a+rwx,g+rwxs $junk_file
 commit_and_check "$qa_user" "$verb" 64k 64k
index aa1e64715dde715c98e03e5035ad1d8116025911..562e1ab98495f55f482db07d47e91ece2a984661 100644 (file)
@@ -31,19 +31,11 @@ Test 8 - root, all-exec file finsert
 6777 -rwsrwsrwx TEST_DIR/686/a
 6777 -rwsrwsrwx TEST_DIR/686/a
 
-Test 9 - qa_user, non-exec file finsert, only sgid
-2666 -rw-rwSrw- TEST_DIR/686/a
-2666 -rw-rwSrw- TEST_DIR/686/a
-
-Test 10 - qa_user, group-exec file finsert, only sgid
+Test 9 - qa_user, group-exec file finsert, only sgid
 2676 -rw-rwsrw- TEST_DIR/686/a
 676 -rw-rwxrw- TEST_DIR/686/a
 
-Test 11 - qa_user, user-exec file finsert, only sgid
-2766 -rwxrwSrw- TEST_DIR/686/a
-2766 -rwxrwSrw- TEST_DIR/686/a
-
-Test 12 - qa_user, all-exec file finsert, only sgid
+Test 10 - qa_user, all-exec file finsert, only sgid
 2777 -rwxrwsrwx TEST_DIR/686/a
 777 -rwxrwxrwx TEST_DIR/686/a
 
index 0bd421e5ec32196bd19fecfb1f6aa55c09d656c3..3a7f1fd5b4600a6939649ebdfdf0adf206209942 100755 (executable)
@@ -7,7 +7,7 @@
 # Functional test for dropping suid and sgid bits as part of a fcollapse.
 #
 . ./common/preamble
-_begin_fstest auto clone quick
+_begin_fstest auto clone quick perms collapse
 
 # Override the default cleanup function.
 _cleanup()
@@ -28,6 +28,7 @@ _require_user
 _require_test
 verb=fcollapse
 _require_xfs_io_command $verb
+_require_congruent_file_oplen $TEST_DIR 65536
 
 junk_dir=$TEST_DIR/$seq
 junk_file=$junk_dir/a
@@ -109,26 +110,14 @@ setup_testfile
 chmod a+rwxs $junk_file
 commit_and_check "" "$verb" 64k 64k
 
-# Commit to a non-exec file by an unprivileged user leaves sgid.
-echo "Test 9 - qa_user, non-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
 # Commit to a group-exec file by an unprivileged user clears sgid
-echo "Test 10 - qa_user, group-exec file $verb, only sgid"
+echo "Test 9 - qa_user, group-exec file $verb, only sgid"
 setup_testfile
 chmod a+rw,g+rwxs $junk_file
 commit_and_check "$qa_user" "$verb" 64k 64k
 
-# Commit to a user-exec file by an unprivileged user clears sgid
-echo "Test 11 - qa_user, user-exec file $verb, only sgid"
-setup_testfile
-chmod a+rw,u+x,g+rws $junk_file
-commit_and_check "$qa_user" "$verb" 64k 64k
-
 # Commit to a all-exec file by an unprivileged user clears sgid.
-echo "Test 12 - qa_user, all-exec file $verb, only sgid"
+echo "Test 10 - qa_user, all-exec file $verb, only sgid"
 setup_testfile
 chmod a+rwx,g+rwxs $junk_file
 commit_and_check "$qa_user" "$verb" 64k 64k
index c5116c2794220ac89ebd44c142725e5ebb0b8196..f72f6d30f33b87f30900ebc7cd3feb5cb207e1d8 100644 (file)
@@ -31,19 +31,11 @@ Test 8 - root, all-exec file fcollapse
 6777 -rwsrwsrwx TEST_DIR/687/a
 6777 -rwsrwsrwx TEST_DIR/687/a
 
-Test 9 - qa_user, non-exec file fcollapse, only sgid
-2666 -rw-rwSrw- TEST_DIR/687/a
-2666 -rw-rwSrw- TEST_DIR/687/a
-
-Test 10 - qa_user, group-exec file fcollapse, only sgid
+Test 9 - qa_user, group-exec file fcollapse, only sgid
 2676 -rw-rwsrw- TEST_DIR/687/a
 676 -rw-rwxrw- TEST_DIR/687/a
 
-Test 11 - qa_user, user-exec file fcollapse, only sgid
-2766 -rwxrwSrw- TEST_DIR/687/a
-2766 -rwxrwSrw- TEST_DIR/687/a
-
-Test 12 - qa_user, all-exec file fcollapse, only sgid
+Test 10 - qa_user, all-exec file fcollapse, only sgid
 2777 -rwxrwsrwx TEST_DIR/687/a
 777 -rwxrwxrwx TEST_DIR/687/a
 
index 905c46ac6279c5c2ccbb7decb3063b479963b8cf..e2bf12b4457d81981be81e4be5684f73e98ea86b 100755 (executable)
@@ -18,6 +18,7 @@ _cleanup()
 
 # Import common functions.
 . ./common/filter
+. ./common/attr
 
 # real QA test starts here
 
@@ -28,6 +29,8 @@ _require_command "$GETCAP_PROG" getcap
 _require_command "$SETCAP_PROG" setcap
 _require_xfs_io_command falloc
 _require_test
+_require_congruent_file_oplen $TEST_DIR 65536
+_require_attrs security
 
 junk_dir=$TEST_DIR/$seq
 junk_file=$junk_dir/a
diff --git a/tests/generic/692 b/tests/generic/692
new file mode 100755 (executable)
index 0000000..3fb8ac0
--- /dev/null
@@ -0,0 +1,85 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Meta, Inc.  All Rights Reserved.
+#
+# FS QA Test 692
+#
+# fs-verity requires the filesystem to decide how it stores the Merkle tree,
+# which can be quite large.
+# It is convenient to treat the Merkle tree as past EOF, and ext4, f2fs, and
+# btrfs do so in at least some fashion. This leads to an edge case where a
+# large file can be under the file system file size limit, but trigger EFBIG
+# on enabling fs-verity. Test enabling verity on some large files to exercise
+# EFBIG logic for filesystems with fs-verity specific limits.
+#
+. ./common/preamble
+_begin_fstest auto quick verity
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       _restore_fsverity_signatures
+       rm -f $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/verity
+
+# real QA test starts here
+_supported_fs generic
+_require_test
+_require_math
+_require_scratch_verity
+_require_fsverity_max_file_size_limit
+_disable_fsverity_signatures
+
+_scratch_mkfs_verity &>> $seqres.full
+_scratch_mount
+
+fsv_file=$SCRATCH_MNT/file.fsv
+
+max_sz=$(_get_max_file_size $SCRATCH_MNT)
+_fsv_scratch_begin_subtest "way too big: fail on first merkle block"
+truncate -s $max_sz $fsv_file
+_fsv_enable $fsv_file |& _filter_scratch
+
+# The goal of this second test is to make a big enough file that we trip the
+# EFBIG codepath, but not so big that we hit it immediately when writing the
+# first Merkle leaf.
+#
+# The Merkle tree is stored with the leaf node level (L0) last, but it is
+# written first.  To get an interesting overflow, we need the maximum file size
+# ($max_sz) to be in the middle of L0 -- ideally near the beginning of L0 so
+# that we don't have to write many blocks before getting an error.
+
+bs=$FSV_BLOCK_SIZE
+hash_size=32   # SHA-256
+hashes_per_block=$(echo "scale=30; $bs/$hash_size" | $BC -q)
+
+# Compute the proportion of the original file size that the non-leaf levels of
+# the Merkle tree take up.  Ignoring padding, this is 1/($hashes_per_block^2) +
+# 1/($hashes_per_block^3) + 1/($hashes_per_block^4) + ...  Compute it using the
+# formula for the sum of a geometric series: \sum_{k=0}^{\infty} ar^k = a/(1-r).
+a=$(echo "scale=30; 1/($hashes_per_block^2)" | $BC -q)
+r=$(echo "scale=30; 1/$hashes_per_block" | $BC -q)
+nonleaves_relative_size=$(echo "scale=30; $a/(1-$r)" | $BC -q)
+
+# Compute the original file size where the leaf level L0 starts at $max_sz.
+# Padding is still ignored, so the real value is slightly smaller than this.
+sz=$(echo "$max_sz/(1+$nonleaves_relative_size)" | $BC -q)
+
+# Finally, to get a file size where $max_sz occurs just after the start of L0
+# rather than *at* the start of L0, subtract an overestimate of the padding: 64K
+# after the file contents, then $bs per level for 11 levels.  11 levels is the
+# most levels that can ever be needed, assuming the block size is at least 1K.
+sz=$(echo "$sz - 65536 - $bs*11" | $BC -q)
+
+_fsv_scratch_begin_subtest "still too big: fail on first invalid merkle block"
+truncate -s $sz $fsv_file
+_fsv_enable $fsv_file |& _filter_scratch
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/692.out b/tests/generic/692.out
new file mode 100644 (file)
index 0000000..0599671
--- /dev/null
@@ -0,0 +1,7 @@
+QA output created by 692
+
+# way too big: fail on first merkle block
+ERROR: FS_IOC_ENABLE_VERITY failed on 'SCRATCH_MNT/file.fsv': File too large
+
+# still too big: fail on first invalid merkle block
+ERROR: FS_IOC_ENABLE_VERITY failed on 'SCRATCH_MNT/file.fsv': File too large
diff --git a/tests/generic/693 b/tests/generic/693
new file mode 100755 (executable)
index 0000000..1596865
--- /dev/null
@@ -0,0 +1,31 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2022 Google LLC
+#
+# FS QA Test No. 693
+#
+# Verify ciphertext for v2 encryption policies that use AES-256-XTS to encrypt
+# file contents and AES-256-HCTR2 to encrypt file names.
+#
+# HCTR2 was introduced in kernel commit 6b2a51ff03bf ("fscrypt: Add HCTR2
+# support for filename encryption")
+#
+. ./common/preamble
+_begin_fstest auto quick encrypt
+
+# Import common functions.
+. ./common/filter
+. ./common/encrypt
+
+# real QA test starts here
+_supported_fs generic
+
+_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-HCTR2 v2
+_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-HCTR2 \
+       v2 iv_ino_lblk_32
+_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-HCTR2 \
+       v2 iv_ino_lblk_64
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/693.out b/tests/generic/693.out
new file mode 100644 (file)
index 0000000..91ff7f2
--- /dev/null
@@ -0,0 +1,16 @@
+QA output created by 693
+
+Verifying ciphertext with parameters:
+       contents_encryption_mode: AES-256-XTS
+       filenames_encryption_mode: AES-256-HCTR2
+       options: v2
+
+Verifying ciphertext with parameters:
+       contents_encryption_mode: AES-256-XTS
+       filenames_encryption_mode: AES-256-HCTR2
+       options: v2 iv_ino_lblk_32
+
+Verifying ciphertext with parameters:
+       contents_encryption_mode: AES-256-XTS
+       filenames_encryption_mode: AES-256-HCTR2
+       options: v2 iv_ino_lblk_64
diff --git a/tests/generic/694 b/tests/generic/694
new file mode 100755 (executable)
index 0000000..c96c215
--- /dev/null
@@ -0,0 +1,53 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022  Red Hat Inc. All Rights Reserved.
+#
+# FS QA Test 694
+#
+# Verify that i_blocks for files larger than 4 GiB have correct
+# values.
+#
+# This test verifies the problem fixed in kernel with commit
+# 0c336d6e33f4 exfat: fix incorrect loading of i_blocks for large files
+#
+. ./common/preamble
+_begin_fstest auto
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $junk_dir
+}
+
+_supported_fs generic
+_fixed_by_kernel_commit 0c336d6e33f4 \
+       "exfat: fix incorrect loading of i_blocks for large file"
+
+_require_test
+_require_fs_space $TEST_DIR $((4 * 1024 * 1024)) #kB
+
+echo "Silence is golden"
+
+junk_dir=$TEST_DIR/$seq
+junk_file=$junk_dir/junk
+mkdir -p $junk_dir
+
+_create_file_sized 4G $junk_file
+if [ $? -ne 0 ]; then
+       echo "Could not create 4G test file"
+fi
+
+iblocks=`stat -c '%b' $junk_file`
+
+_test_cycle_mount
+
+iblocks_after_remount=`stat -c '%b' $junk_file`
+
+if [ "$iblocks" != "$iblocks_after_remount" ]; then
+       echo "Number of blocks needs to be same: $iblocks, $iblocks_after_remount"
+fi
+
+status=0
+
+exit
diff --git a/tests/generic/694.out b/tests/generic/694.out
new file mode 100644 (file)
index 0000000..d1fb84a
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 694
+Silence is golden
diff --git a/tests/generic/695 b/tests/generic/695
new file mode 100755 (executable)
index 0000000..d53457d
--- /dev/null
@@ -0,0 +1,91 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 695
+#
+# Test that if we punch a hole adjacent to an existing hole, fsync the file and
+# then power fail, the new hole exists after mounting again the filesystem.
+#
+# This is motivated by a regression on btrfs, fixed by the commit mentioned
+# below, when not using the no-holes feature (which is enabled by default since
+# btrfs-progs 5.15).
+#
+. ./common/preamble
+_begin_fstest auto quick log punch fiemap
+
+_cleanup()
+{
+       _cleanup_flakey
+       cd /
+       rm -r -f $tmp.*
+}
+
+. ./common/filter
+. ./common/dmflakey
+. ./common/punch
+
+_supported_fs generic
+_fixed_by_kernel_commit e6e3dec6c3c288 \
+        "btrfs: update generation of hole file extent item when merging holes"
+_require_scratch
+_require_dm_target flakey
+_require_xfs_io_command "fpunch"
+_require_xfs_io_command "fiemap"
+
+_scratch_mkfs >>$seqres.full 2>&1
+_require_metadata_journaling $SCRATCH_DEV
+_init_flakey
+_mount_flakey
+
+# We punch 2M holes and require extent allocations to align to 2M in fiemap
+# results.
+_require_congruent_file_oplen $SCRATCH_MNT $((2 * 1024 * 1024))
+
+# Create our test file with the following layout:
+#
+# [0, 2M)    - hole
+# [2M, 10M)  - extent
+# [10M, 12M) - hole
+$XFS_IO_PROG -f -c "truncate 12M" \
+            -c "pwrite -S 0xab 2M 8M" \
+            $SCRATCH_MNT/foobar | _filter_xfs_io
+
+# Persist everything, commit the filesystem's transaction.
+sync
+
+# Now punch two holes in the file:
+#
+# 1) For the range [2M, 4M), which is adjacent to the existing hole in the range
+#    [0, 2M);
+# 2) For the range [8M, 10M), which is adjacent to the existing hole in the
+#    range [10M, 12M).
+#
+# These operations start a new filesystem transaction.
+# Then finally fsync the file.
+$XFS_IO_PROG -c "fpunch 2M 2M" \
+            -c "fpunch 8M 2M" \
+            -c "fsync" $SCRATCH_MNT/foobar
+
+# Simulate a power failure and mount the filesystem to check that everything
+# is in the same state as before the power failure.
+_flakey_drop_and_remount
+
+# We expect the following file layout:
+#
+# [0, 4M)    - hole
+# [4M, 8M)   - extent
+# [8M, 12M)  - hole
+echo "File layout after power failure:"
+$XFS_IO_PROG -c "fiemap -v" $SCRATCH_MNT/foobar | _filter_fiemap
+
+# When reading the file we expect to get the range [4M, 8M) filled with bytes
+# that have a value of 0xab and 0x00 for anything outside that range.
+echo "File content after power failure:"
+_hexdump $SCRATCH_MNT/foobar
+
+_unmount_flakey
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/695.out b/tests/generic/695.out
new file mode 100644 (file)
index 0000000..447ef5c
--- /dev/null
@@ -0,0 +1,15 @@
+QA output created by 695
+wrote 8388608/8388608 bytes at offset 2097152
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+File layout after power failure:
+0: [0..8191]: hole
+1: [8192..16383]: data
+2: [16384..24575]: hole
+File content after power failure:
+000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  >................<
+*
+400000 ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab  >................<
+*
+800000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  >................<
+*
+c00000
diff --git a/tests/generic/696 b/tests/generic/696
new file mode 100755 (executable)
index 0000000..55a2fd5
--- /dev/null
@@ -0,0 +1,49 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 696
+#
+# Test S_ISGID stripping whether works correctly when call process
+# uses umask(S_IXGRP).
+#
+# It is also a regression test for
+# commit ac6800e279a2 ("fs: Add missing umask strip in vfs_tmpfile")
+# commit 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers")
+
+. ./common/preamble
+_begin_fstest auto quick cap idmapped mount perms rw unlink
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+_supported_fs generic
+_require_test
+_require_scratch
+_fixed_by_kernel_commit ac6800e279a2 \
+       "fs: Add missing umask strip in vfs_tmpfile" \
+1639a49ccdce "fs: move S_ISGID stripping into the vfs_*() helpers"
+
+_scratch_mkfs >$seqres.full 2>&1
+
+$here/src/vfs/vfstest --test-setgid-create-umask \
+        --device "$TEST_DEV" --mount "$TEST_DIR" --fstype "$FSTYP"
+
+if [ "$FSTYP" != afs ]
+then
+    export MOUNT_OPTIONS="-o noacl $MOUNT_OPTIONS"
+fi
+
+# Also test S_ISGID stripping whether works correctly on underflying filesystem
+# that supports noacl feature.
+# noacl will earse acl flag in superblock, so kernel will use current_umask in
+# vfs directly instead of calling posix_acl_create on underflying filesystem.
+_try_scratch_mount >>$seqres.full 2>&1 && \
+       $here/src/vfs/vfstest --test-setgid-create-umask \
+        --device "$SCRATCH_DEV" --mount "$SCRATCH_MNT" --fstype "$FSTYP"
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/generic/696.out b/tests/generic/696.out
new file mode 100644 (file)
index 0000000..c07efdf
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 696
+Silence is golden
diff --git a/tests/generic/697 b/tests/generic/697
new file mode 100755 (executable)
index 0000000..8d7ad65
--- /dev/null
@@ -0,0 +1,33 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 697
+#
+# Test S_ISGID stripping whether works correctly when call process
+# uses posix acl.
+#
+# It is also a regression test for
+# commit 1639a49ccdce ("fs: move S_ISGID stripping into the vfs_*() helpers")
+
+. ./common/preamble
+_begin_fstest auto quick cap acl idmapped mount perms rw unlink
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+
+_supported_fs generic
+_require_test
+_require_acls
+_fixed_by_kernel_commit 1639a49ccdce \
+       "fs: move S_ISGID stripping into the vfs_*() helpers"
+
+$here/src/vfs/vfstest --test-setgid-create-acl \
+        --device "$TEST_DEV" --mount "$TEST_DIR" --fstype "$FSTYP"
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/generic/697.out b/tests/generic/697.out
new file mode 100644 (file)
index 0000000..8aab5a9
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 697
+Silence is golden
diff --git a/tests/generic/698 b/tests/generic/698
new file mode 100755 (executable)
index 0000000..143490b
--- /dev/null
@@ -0,0 +1,117 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Christian Brauner (Microsoft).  All Rights Reserved.
+#
+# FS QA Test 698
+#
+# Test that users can changed group ownership of a file they own to a group
+# they are a member of.
+#
+# Regression test for commit:
+# 168f91289340 ("fs: account for group membership")
+#
+. ./common/preamble
+_begin_fstest auto quick perms attr idmapped mount
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       $UMOUNT_PROG $SCRATCH_MNT/target-mnt 2>/dev/null
+       $UMOUNT_PROG $SCRATCH_MNT 2>/dev/null
+       rm -r -f $tmp.*
+}
+
+# real QA test starts here
+_supported_fs generic
+_fixed_by_kernel_commit 168f91289340 \
+       "fs: account for group membership"
+_require_scratch
+_require_chown
+_require_idmapped_mounts
+_require_test_program "vfs/mount-idmapped"
+_require_user fsgqa2
+_require_group fsgqa2
+# Do this SECOND so that qa_user is fsgqa, and _user_do uses that account
+_require_user fsgqa
+_require_group fsgqa
+
+user_foo=`id -u fsgqa`
+group_foo=`id -g fsgqa`
+user_bar=`id -u fsgqa2`
+group_bar=`id -g fsgqa2`
+
+setup_tree()
+{
+       mkdir -p $SCRATCH_MNT/source-mnt
+       chmod 0777 $SCRATCH_MNT/source-mnt
+       touch $SCRATCH_MNT/source-mnt/file1
+       chown 65534:65534 $SCRATCH_MNT
+       chown 65534:65534 $SCRATCH_MNT/source-mnt
+       chown 65534:65535 $SCRATCH_MNT/source-mnt/file1
+
+       mkdir -p $SCRATCH_MNT/target-mnt
+       chmod 0777 $SCRATCH_MNT/target-mnt
+}
+
+# Setup an idmapped mount where uid and gid 65534 are mapped to fsgqa and uid
+# and gid 65535 are mapped to fsgqa2.
+setup_idmapped_mnt()
+{
+       $here/src/vfs/mount-idmapped \
+               --map-mount=u:65534:$user_foo:1 \
+               --map-mount=g:65534:$group_foo:1 \
+               --map-mount=u:65535:$user_bar:1 \
+               --map-mount=g:65535:$group_bar:1 \
+               $SCRATCH_MNT/source-mnt $SCRATCH_MNT/target-mnt
+}
+
+# We've created a layout where fsgqa owns the target file but the group of the
+# target file is owned by another group. We now test that user fsgqa can change
+# the group ownership of the file to a group they control. In this case to the
+# fsgqa group.
+change_group_ownership()
+{
+       local path="$1"
+
+       stat -c '%U:%G' $path
+       _user_do "id -u --name; id -g --name; chgrp $group_foo $path"
+       stat -c '%U:%G' $path
+       _user_do "id -u --name; id -g --name; chgrp $group_bar $path > /dev/null 2>&1"
+       stat -c '%U:%G' $path
+}
+
+run_base_test()
+{
+       mkdir -p $SCRATCH_MNT/source-mnt
+       chmod 0777 $SCRATCH_MNT/source-mnt
+       touch $SCRATCH_MNT/source-mnt/file1
+       chown $user_foo:$group_foo $SCRATCH_MNT
+       chown $user_foo:$group_foo $SCRATCH_MNT/source-mnt
+       chown $user_foo:$group_bar $SCRATCH_MNT/source-mnt/file1
+
+       echo ""
+       echo "base test"
+       change_group_ownership "$SCRATCH_MNT/source-mnt/file1"
+       rm -rf "$SCRATCH_MNT/source-mnt"
+}
+
+# Basic test as explained in the comment for change_group_ownership().
+run_idmapped_test()
+{
+       echo ""
+       echo "base idmapped test"
+       change_group_ownership "$SCRATCH_MNT/target-mnt/file1"
+}
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+run_base_test
+setup_tree
+setup_idmapped_mnt
+run_idmapped_test
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/698.out b/tests/generic/698.out
new file mode 100644 (file)
index 0000000..519234b
--- /dev/null
@@ -0,0 +1,19 @@
+QA output created by 698
+
+base test
+fsgqa:fsgqa2
+fsgqa
+fsgqa
+fsgqa:fsgqa
+fsgqa
+fsgqa
+fsgqa:fsgqa
+
+base idmapped test
+fsgqa:fsgqa2
+fsgqa
+fsgqa
+fsgqa:fsgqa
+fsgqa
+fsgqa
+fsgqa:fsgqa
diff --git a/tests/generic/699 b/tests/generic/699
new file mode 100755 (executable)
index 0000000..82e8364
--- /dev/null
@@ -0,0 +1,163 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Christian Brauner (Microsoft).  All Rights Reserved.
+#
+# FS QA Test 698
+#
+# This's copied from generic/698, extend it to test overlayfs on top of idmapped
+# mounts specifically.
+#
+. ./common/preamble
+. ./common/overlay
+_begin_fstest auto quick perms attr idmapped mount
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       $UMOUNT_PROG $SCRATCH_MNT/target-mnt
+       $UMOUNT_PROG $SCRATCH_MNT/ovl-merge 2>/dev/null
+       $UMOUNT_PROG $SCRATCH_MNT 2>/dev/null
+       rm -r -f $tmp.*
+}
+
+# real QA test starts here
+_supported_fs ^overlay
+_require_extra_fs overlay
+_require_scratch
+_require_chown
+_require_idmapped_mounts
+_require_test_program "vfs/mount-idmapped"
+_require_user fsgqa2
+_require_group fsgqa2
+# Do this SECOND so that qa_user is fsgqa, and _user_do uses that account
+_require_user fsgqa
+_require_group fsgqa
+
+user_foo=`id -u fsgqa`
+group_foo=`id -g fsgqa`
+user_bar=`id -u fsgqa2`
+group_bar=`id -g fsgqa2`
+
+setup_tree()
+{
+       mkdir -p $SCRATCH_MNT/source-mnt
+       chmod 0777 $SCRATCH_MNT/source-mnt
+       touch $SCRATCH_MNT/source-mnt/file1
+       chown 65534:65534 $SCRATCH_MNT
+       chown 65534:65534 $SCRATCH_MNT/source-mnt
+       chown 65534:65535 $SCRATCH_MNT/source-mnt/file1
+
+       mkdir -p $SCRATCH_MNT/target-mnt
+       chmod 0777 $SCRATCH_MNT/target-mnt
+}
+
+# Setup an idmapped mount where uid and gid 65534 are mapped to fsgqa and uid
+# and gid 65535 are mapped to fsgqa2.
+setup_idmapped_mnt()
+{
+       $here/src/vfs/mount-idmapped \
+               --map-mount=u:65534:$user_foo:1 \
+               --map-mount=g:65534:$group_foo:1 \
+               --map-mount=u:65535:$user_bar:1 \
+               --map-mount=g:65535:$group_bar:1 \
+               $SCRATCH_MNT/source-mnt $SCRATCH_MNT/target-mnt
+}
+
+# We've created a layout where fsgqa owns the target file but the group of the
+# target file is owned by another group. We now test that user fsgqa can change
+# the group ownership of the file to a group they control. In this case to the
+# fsgqa group.
+change_group_ownership()
+{
+       local path="$1"
+
+       stat -c '%U:%G' $path
+       _user_do "id -u --name; id -g --name; chgrp $group_foo $path"
+       stat -c '%U:%G' $path
+       _user_do "id -u --name; id -g --name; chgrp $group_bar $path > /dev/null 2>&1"
+       stat -c '%U:%G' $path
+}
+
+lower="$SCRATCH_MNT/target-mnt"
+upper="$SCRATCH_MNT/ovl-upper"
+work="$SCRATCH_MNT/ovl-work"
+merge="$SCRATCH_MNT/ovl-merge"
+
+reset_ownership()
+{
+       local path="$SCRATCH_MNT/source-mnt/file1"
+
+       echo ""
+       echo "reset ownership"
+       chown 65534:65534 $path
+       stat -c '%u:%g' $path
+       chown 65534:65535 $path
+       stat -c '%u:%g' $path
+}
+
+# Prepare overlayfs with metacopy turned off.
+setup_overlayfs_idmapped_lower_metacopy_off()
+{
+       mkdir -p $upper $work $merge
+       _overlay_mount_dirs $lower $upper $work \
+                           overlay $merge -ometacopy=off || \
+                           _notrun "overlayfs doesn't support idmappped layers"
+}
+
+# Prepare overlayfs with metacopy turned on.
+setup_overlayfs_idmapped_lower_metacopy_on()
+{
+       mkdir -p $upper $work $merge
+       _overlay_mount_dirs $lower $upper $work overlay $merge -ometacopy=on
+}
+
+reset_overlayfs()
+{
+       $UMOUNT_PROG $SCRATCH_MNT/ovl-merge 2>/dev/null
+       rm -rf $upper $work $merge
+}
+
+# Overlayfs can be mounted on top of idmapped layers. Make sure that the basic
+# test explained in the comment for change_group_ownership() passes with
+# overlayfs mounted on top of it.
+# This tests overlayfs with metacopy turned off, i.e., changing a file copies
+# up data and metadata.
+run_overlayfs_idmapped_lower_metacopy_off()
+{
+       echo ""
+       echo "overlayfs idmapped lower metacopy off"
+       change_group_ownership "$SCRATCH_MNT/ovl-merge/file1"
+       reset_overlayfs
+       reset_ownership
+}
+
+# Overlayfs can be mounted on top of idmapped layers. Make sure that the basic
+# test explained in the comment for change_group_ownership() passes with
+# overlayfs mounted on top of it.
+# This tests overlayfs with metacopy turned on, i.e., changing a file tries to
+# only copy up metadata.
+run_overlayfs_idmapped_lower_metacopy_on()
+{
+       echo ""
+       echo "overlayfs idmapped lower metacopy on"
+       change_group_ownership "$SCRATCH_MNT/ovl-merge/file1"
+       reset_overlayfs
+       reset_ownership
+}
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+_supports_filetype $SCRATCH_MNT || _notrun "overlayfs test requires d_type"
+
+setup_tree
+setup_idmapped_mnt
+setup_overlayfs_idmapped_lower_metacopy_off
+run_overlayfs_idmapped_lower_metacopy_off
+
+setup_overlayfs_idmapped_lower_metacopy_on
+run_overlayfs_idmapped_lower_metacopy_on
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/699.out b/tests/generic/699.out
new file mode 100644 (file)
index 0000000..e5786d8
--- /dev/null
@@ -0,0 +1,27 @@
+QA output created by 699
+
+overlayfs idmapped lower metacopy off
+fsgqa:fsgqa2
+fsgqa
+fsgqa
+fsgqa:fsgqa
+fsgqa
+fsgqa
+fsgqa:fsgqa
+
+reset ownership
+65534:65534
+65534:65535
+
+overlayfs idmapped lower metacopy on
+fsgqa:fsgqa2
+fsgqa
+fsgqa
+fsgqa:fsgqa
+fsgqa
+fsgqa
+fsgqa:fsgqa
+
+reset ownership
+65534:65534
+65534:65535
diff --git a/tests/generic/700 b/tests/generic/700
new file mode 100755 (executable)
index 0000000..fcf4e3f
--- /dev/null
@@ -0,0 +1,64 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Red Hat, Copyright.  All Rights Reserved.
+#
+# FS QA Test No. 700
+#
+# Verify selinux label can be kept after RENAME_WHITEOUT. This is
+# a regression test for:
+#   70b589a37e1a ("xfs: add selinux labels to whiteout inodes")
+#
+. ./common/preamble
+_begin_fstest auto quick rename attr whiteout
+
+# Import common functions.
+. ./common/attr
+. ./common/renameat2
+
+# real QA test starts here
+_supported_fs generic
+_require_scratch
+_require_attrs
+_require_renameat2 whiteout
+
+_fixed_by_kernel_commit 70b589a37e1a \
+       xfs: add selinux labels to whiteout inodes
+
+get_selinux_label()
+{
+       local label
+
+       label=$(_getfattr --absolute-names -n security.selinux $@ | sed -n 's/security.selinux=\"\(.*\)\"/\1/p')
+       if [ ${PIPESTATUS[0]} -ne 0 -o -z "$label" ];then
+               _fail "Fail to get selinux label: $label"
+       fi
+       echo $label
+}
+
+_scratch_mkfs >> $seqres.full 2>&1
+# SELINUX_MOUNT_OPTIONS will be set in common/config if selinux is enabled
+if [ -z "$SELINUX_MOUNT_OPTIONS" ]; then
+       _notrun "Require selinux to be enabled"
+fi
+# This test need to verify selinux labels in objects, so unset this selinux
+# mount option
+export SELINUX_MOUNT_OPTIONS=""
+_scratch_mount
+
+touch $SCRATCH_MNT/f1
+echo "Before RENAME_WHITEOUT" >> $seqres.full
+ls -lZ $SCRATCH_MNT >> $seqres.full 2>&1
+# Expect f1 and f2 have same label after RENAME_WHITEOUT
+$here/src/renameat2 -w $SCRATCH_MNT/f1 $SCRATCH_MNT/f2
+echo "After RENAME_WHITEOUT" >> $seqres.full
+ls -lZ $SCRATCH_MNT >> $seqres.full 2>&1
+label1=$(get_selinux_label $SCRATCH_MNT/f1)
+label2=$(get_selinux_label $SCRATCH_MNT/f2)
+if [ "$label1" != "$label2" ];then
+       echo "$label1 != $label2"
+fi
+
+echo "Silence is golden"
+# success, all done
+status=0
+exit
diff --git a/tests/generic/700.out b/tests/generic/700.out
new file mode 100644 (file)
index 0000000..f9acaa7
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 700
+Silence is golden
diff --git a/tests/generic/701 b/tests/generic/701
new file mode 100755 (executable)
index 0000000..26dec40
--- /dev/null
@@ -0,0 +1,51 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022  Red Hat Inc. All Rights Reserved.
+#
+# FS QA Test No. 701
+#
+# Verify that i_blocks for truncated files larger than 4 GiB have correct
+# values.
+#
+# This test verifies the problem fixed in kernel with commit
+# 92fba084b79e exfat: fix i_blocks for files truncated over 4 GiB
+#
+. ./common/preamble
+. ./common/filter
+
+_begin_fstest auto
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $junk_dir
+}
+
+_supported_fs generic
+_fixed_by_kernel_commit 92fba084b79e \
+       "exfat: fix i_blocks for files truncated over 4 GiB"
+
+_require_test
+_require_fs_space $TEST_DIR $((5 * 1024 * 1024)) #kB
+
+junk_dir=$TEST_DIR/$seq
+junk_file=$junk_dir/junk
+mkdir -p $junk_dir
+
+_create_file_sized 5G $junk_file
+if [ $? -ne 0 ]; then
+       echo "Could not create 5G test file"
+fi
+
+truncate -s 4G $junk_file
+
+block_size=`stat -c '%B' $junk_file`
+iblocks_after_truncate=`stat -c '%b' $junk_file`
+iblocks_expected=$((4 * 1024 * 1024 * 1024 / $block_size))
+
+_within_tolerance "Number of allocated blocks after truncate" $iblocks_after_truncate $iblocks_expected 1% -v
+
+status=0
+
+exit
diff --git a/tests/generic/701.out b/tests/generic/701.out
new file mode 100644 (file)
index 0000000..0b914fa
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 701
+Number of allocated blocks after truncate is in range
diff --git a/tests/generic/702 b/tests/generic/702
new file mode 100755 (executable)
index 0000000..f93bc94
--- /dev/null
@@ -0,0 +1,92 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 702
+#
+# Test that if we have two consecutive extents and only one of them is cloned,
+# then fiemap correctly reports which one is shared and reports the other as not
+# shared.
+#
+. ./common/preamble
+_begin_fstest auto quick clone fiemap
+
+. ./common/filter
+. ./common/reflink
+
+_fixed_by_kernel_commit ac3c0d36a2a2f7 \
+       "btrfs: make fiemap more efficient and accurate reporting extent sharedness"
+
+_supported_fs generic
+_require_scratch_reflink
+_require_xfs_io_command "fiemap"
+
+fiemap_test_file()
+{
+       local filepath=$1
+
+       # Skip the first two lines of xfs_io's fiemap output (file path and
+       # header describing the output columns).
+       #
+       # Print the first column (extent number), second column (file range),
+       # fourth column (extent size) and fifth column (flags) of the fiemap
+       # output.
+       #
+       # We filter the flags column to only tell us if an extent is shared or
+       # not (flag 0x2000, which matches FIEMAP_EXTENT_SHARED) because on some
+       # filesystem configs we may have other flags printed - for example
+       # running btrfs with "-o compress" we get the flag 0x8 as well (which
+       # is FIEMAP_EXTENT_ENCODED).
+       #
+       # The third column is the physical location of the extents, so it's
+       # omitted because the location varies between different filesystems.
+       #
+       $XFS_IO_PROG -c "fiemap -v" $filepath | tail -n +3 | \
+               $AWK_PROG '{ print $1, $2, $4, \
+                         and(strtonum($5), 0x2000) ? "shared" : "not_shared" }'
+}
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+# We create 128K extents in the test files below.
+_require_congruent_file_oplen $SCRATCH_MNT $((128 * 1024))
+
+# Create file foo with 2 consecutive extents, each one with a size of 128K.
+echo "Creating file foo"
+$XFS_IO_PROG -f -c "pwrite -b 128K 0 128K" -c "fsync" \
+            -c "pwrite -b 128K 128K 128K" -c "fsync" \
+            $SCRATCH_MNT/foo | _filter_xfs_io
+
+# Clone only the first extent into another file.
+echo "Cloning first extent of file foo to file bar"
+$XFS_IO_PROG -f -c "reflink $SCRATCH_MNT/foo 0 0 128K" $SCRATCH_MNT/bar | \
+       _filter_xfs_io
+
+# Now fiemap file foo, it should report the first 128K extent as shared and the
+# second 128K extent as not shared.
+echo "fiemap of file foo:"
+fiemap_test_file $SCRATCH_MNT/foo
+
+# Now do a similar test as above, except that this time only the second 128K
+# extent is cloned, the first extent is not cloned.
+
+# Create file foo2 with 2 consecutive extents, each one with a size of 128K.
+echo "Creating file foo2"
+$XFS_IO_PROG -f -c "pwrite -b 128K 0 128K" -c "fsync" \
+            -c "pwrite -b 128K 128K 128K" -c "fsync" \
+            $SCRATCH_MNT/foo2 | _filter_xfs_io
+
+# Clone only the second extent of foo2 into another file.
+echo "Cloning second extent of file foo2 to file bar2"
+$XFS_IO_PROG -f -c "reflink $SCRATCH_MNT/foo2 128K 0 128K" $SCRATCH_MNT/bar2 | \
+       _filter_xfs_io
+
+# Now fiemap file foo2, it should report the first 128K extent as not shared and
+# the second 128K extent as shared
+echo "fiemap of file foo2:"
+fiemap_test_file $SCRATCH_MNT/foo2
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/702.out b/tests/generic/702.out
new file mode 100644 (file)
index 0000000..576bb5e
--- /dev/null
@@ -0,0 +1,23 @@
+QA output created by 702
+Creating file foo
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 131072
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Cloning first extent of file foo to file bar
+linked 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+fiemap of file foo:
+0: [0..255]: 256 shared
+1: [256..511]: 256 not_shared
+Creating file foo2
+wrote 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 131072/131072 bytes at offset 131072
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Cloning second extent of file foo2 to file bar2
+linked 131072/131072 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+fiemap of file foo2:
+0: [0..255]: 256 not_shared
+1: [256..511]: 256 shared
diff --git a/tests/generic/703 b/tests/generic/703
new file mode 100755 (executable)
index 0000000..7afb746
--- /dev/null
@@ -0,0 +1,103 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 703
+#
+# Test that direct IO writes with io_uring and O_DSYNC are durable if a power
+# failure happens after they complete.
+#
+. ./common/preamble
+_begin_fstest auto quick log prealloc io_uring
+
+_cleanup()
+{
+       _cleanup_flakey
+       cd /
+       rm -r -f $tmp.*
+}
+
+. ./common/dmflakey
+
+fio_config=$tmp.fio
+fio_out=$tmp.fio.out
+test_file="${SCRATCH_MNT}/foo"
+
+[ $FSTYP == "btrfs" ] &&
+       _fixed_by_kernel_commit 8184620ae212 \
+       "btrfs: fix lost file sync on direct IO write with nowait and dsync iocb"
+
+_supported_fs generic
+# We allocate 256M of data for the test file, so require a higher size of 512M
+# which gives a margin of safety for a COW filesystem like btrfs (where metadata
+# is always COWed).
+_require_scratch_size $((512 * 1024))
+_require_odirect
+_require_io_uring
+_require_dm_target flakey
+_require_xfs_io_command "falloc"
+
+cat >$fio_config <<EOF
+[test_io_uring_dio_dsync]
+ioengine=io_uring
+direct=1
+bs=64K
+sync=1
+filename=$test_file
+rw=randwrite
+time_based
+runtime=10
+EOF
+
+_require_fio $fio_config
+
+_scratch_mkfs >>$seqres.full 2>&1
+_require_metadata_journaling $SCRATCH_DEV
+_init_flakey
+_mount_flakey
+
+# We do 64K writes in the fio job.
+_require_congruent_file_oplen $SCRATCH_MNT $((64 * 1024))
+
+touch $test_file
+
+# On btrfs IOCB_NOWAIT writes can only be done on NOCOW files, so enable
+# nodatacow on the file if we are running on btrfs.
+if [ $FSTYP == "btrfs" ]; then
+       _require_chattr C
+       $CHATTR_PROG +C $test_file
+fi
+
+$XFS_IO_PROG -c "falloc 0 256M" $test_file
+
+# Persist everything, make sure the file exists after power failure.
+sync
+
+echo -e "Running fio with config:\n" >> $seqres.full
+cat $fio_config >> $seqres.full
+
+$FIO_PROG $fio_config --output=$fio_out
+
+echo -e "\nOutput from fio:\n" >> $seqres.full
+cat $fio_out >> $seqres.full
+
+digest_before=$(_md5_checksum $test_file)
+
+# Simulate a power failure and mount the filesystem to check that all the data
+# previously written are available.
+_flakey_drop_and_remount
+
+digest_after=$(_md5_checksum $test_file)
+
+if [ "$digest_after" != "$digest_before" ]; then
+       echo "Error: not all file data got persisted."
+       echo "Digest before power failure: $digest_before"
+       echo "Digest after power failure:  $digest_after"
+fi
+
+_unmount_flakey
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/generic/703.out b/tests/generic/703.out
new file mode 100644 (file)
index 0000000..fba6257
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 703
+Silence is golden
diff --git a/tests/generic/704 b/tests/generic/704
new file mode 100755 (executable)
index 0000000..6cc4bb4
--- /dev/null
@@ -0,0 +1,53 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Red Hat, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 704
+#
+# Make sure logical-sector sized O_DIRECT write is allowed
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.*
+       [ -d "$SCSI_DEBUG_MNT" ] && $UMOUNT_PROG $SCSI_DEBUG_MNT 2>/dev/null
+       _put_scsi_debug_dev
+}
+
+# Import common functions.
+. ./common/scsi_debug
+
+# real QA test starts here
+_supported_fs generic
+_fixed_by_kernel_commit 7c71ee78031c "xfs: allow logical-sector sized O_DIRECT"
+_require_scsi_debug
+# If TEST_DEV is block device, make sure current fs is a localfs which can be
+# written on scsi_debug device
+_require_test
+_require_block_device $TEST_DEV
+
+size=$(_small_fs_size_mb 256)
+echo "Get a device with 4096 physical sector size and 512 logical sector size"
+SCSI_DEBUG_DEV=`_get_scsi_debug_dev 4096 512 0 $size`
+blockdev --getpbsz --getss $SCSI_DEBUG_DEV
+
+echo "mkfs and mount"
+_mkfs_dev $SCSI_DEBUG_DEV || _fail "Can't make $FSTYP on scsi_debug device"
+SCSI_DEBUG_MNT="$TEST_DIR/scsi_debug_$seq"
+rm -rf $SCSI_DEBUG_MNT
+mkdir $SCSI_DEBUG_MNT
+run_check _mount $SCSI_DEBUG_DEV $SCSI_DEBUG_MNT
+
+echo "DIO read/write 512 bytes"
+# This dio write should succeed, even the physical sector size is 4096, but
+# the logical sector size is 512
+$XFS_IO_PROG -d -f -c "pwrite 0 512" $SCSI_DEBUG_MNT/testfile >> $seqres.full
+$XFS_IO_PROG -d -c "pread 0 512" $SCSI_DEBUG_MNT/testfile >> $seqres.full
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/704.out b/tests/generic/704.out
new file mode 100644 (file)
index 0000000..b7e8cd2
--- /dev/null
@@ -0,0 +1,6 @@
+QA output created by 704
+Get a device with 4096 physical sector size and 512 logical sector size
+4096
+512
+mkfs and mount
+DIO read/write 512 bytes
diff --git a/tests/generic/705 b/tests/generic/705
new file mode 100755 (executable)
index 0000000..7b6e557
--- /dev/null
@@ -0,0 +1,45 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Red Hat, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 705
+#
+# Test an issue in the truncate codepath where on-disk inode sizes are logged
+# prematurely via the free eofblocks path on file close.
+#
+. ./common/preamble
+_begin_fstest auto shutdown
+
+# real QA test starts here
+_supported_fs generic
+_require_scratch
+_require_scratch_shutdown
+_require_command "$FILEFRAG_PROG" filefrag
+_scratch_mkfs > $seqres.full 2>&1
+_scratch_mount
+
+echo "Create many small files with one extent at least"
+for ((i=0; i<10000; i++));do
+       $XFS_IO_PROG -f -c "pwrite 0 4k" $SCRATCH_MNT/file.$i >/dev/null 2>&1
+done
+
+echo "Shutdown the fs suddently"
+_scratch_shutdown
+
+echo "Cycle mount"
+_scratch_cycle_mount
+
+echo "Check file's (di_size > 0) extents"
+for f in $(find $SCRATCH_MNT -type f -size +0);do
+       # Check if the file has any extent
+       $FILEFRAG_PROG -v $f > $tmp.filefrag
+       if grep -Eq ': 0 extents found' $tmp.filefrag;then
+               echo " - $f get no extents, but its di_size > 0"
+               cat $tmp.filefrag
+               break
+       fi
+done
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/705.out b/tests/generic/705.out
new file mode 100644 (file)
index 0000000..ca2a6d4
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 705
+Create many small files with one extent at least
+Shutdown the fs suddently
+Cycle mount
+Check file's (di_size > 0) extents
diff --git a/tests/generic/706 b/tests/generic/706
new file mode 100755 (executable)
index 0000000..b3e7aa7
--- /dev/null
@@ -0,0 +1,36 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test 706
+#
+# Test that seeking for data on a 1 byte file works correctly, the returned
+# offset should be 0 if the start offset is 0.
+#
+. ./common/preamble
+_begin_fstest auto quick seek
+
+[ $FSTYP == "btrfs" ] &&
+       _fixed_by_kernel_commit 2f2e84ca6066 \
+       "btrfs: fix off-by-one in delalloc search during lseek"
+
+_supported_fs generic
+_require_test
+_require_seek_data_hole
+_require_test_program "seek_sanity_test"
+
+test_file=$TEST_DIR/seek_sanity_testfile.$seq
+
+_cleanup()
+{
+       cd /
+       rm -f $tmp.*
+       rm -f $test_file
+}
+
+_run_seek_sanity_test -s 22 -e 22 $test_file > $seqres.full 2>&1 ||
+       _fail "seek sanity check failed!"
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/generic/706.out b/tests/generic/706.out
new file mode 100644 (file)
index 0000000..577697c
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 706
+Silence is golden
diff --git a/tests/generic/707 b/tests/generic/707
new file mode 100755 (executable)
index 0000000..da9dc5b
--- /dev/null
@@ -0,0 +1,70 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Jan Kara, SUSE Linux.  All Rights Reserved.
+#
+# FS QA Test 707
+#
+# This is a test verifying whether the filesystem can gracefully handle
+# modifying of a directory while it is being moved, in particular the cases
+# where directory format changes
+#
+. ./common/preamble
+_begin_fstest auto
+
+_supported_fs generic
+_require_scratch
+
+_fixed_by_kernel_commit f950fd052913 \
+       "udf: Protect rename against modification of moved directory"
+_fixed_by_kernel_commit 0813299c586b \
+       "ext4: Fix possible corruption when moving a directory"
+
+_scratch_mkfs >>$seqres.full 2>&1
+_scratch_mount
+
+_cleanup()
+{
+       cd /
+       rm -f $tmp.*
+       if [ -n "$BGPID" ]; then
+               # Stop background process
+               kill -9 $BGPID &>/dev/null
+               wait
+       fi
+}
+
+# Loop multiple times trying to hit the race
+loops=$((100*TIME_FACTOR))
+files=500
+moves=500
+
+create_files()
+{
+       # We use slightly longer file name to make directory grow faster and
+       # hopefully convert between various types
+       for (( i = 0; i < $files; i++ )); do
+               touch somewhatlongerfilename$i
+       done
+}
+
+for (( i = 0; i <= $moves; i++ )); do
+       mkdir $SCRATCH_MNT/dir$i
+done
+
+for (( l = 0; l < $loops; l++ )); do
+       mkdir $SCRATCH_MNT/dir0/dir
+       pushd $SCRATCH_MNT/dir0/dir &>/dev/null
+       create_files &
+       BGPID=$!
+       popd &>/dev/null
+       for (( i = 0; i < $moves; i++ )); do
+               mv $SCRATCH_MNT/dir$i/dir $SCRATCH_MNT/dir$((i+1))/dir
+       done
+       wait
+       unset BGPID
+       rm -r $SCRATCH_MNT/dir$moves/dir
+done
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/generic/707.out b/tests/generic/707.out
new file mode 100644 (file)
index 0000000..8e57a1d
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 707
+Silence is golden
diff --git a/tests/generic/708 b/tests/generic/708
new file mode 100755 (executable)
index 0000000..6809a50
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Meta Platforms, Inc.  All Rights Reserved.
+#
+# FS QA Test 708
+#
+# Test iomap direct_io partial writes.
+#
+# Create a reasonably large file, then run a program which mmaps it,
+# touches the first page, then dio writes it to a second file. This
+# can result in a page fault reading from the mmapped dio write buffer and
+# thus the iomap direct_io partial write codepath.
+#
+. ./common/preamble
+_begin_fstest quick auto
+[ $FSTYP == "btrfs" ] && \
+       _fixed_by_kernel_commit b73a6fd1b1ef \
+               "btrfs: split partial dio bios before submit"
+
+# real QA test starts here
+_supported_fs generic
+_require_test
+_require_odirect
+_require_test_program dio-buf-fault
+src=$TEST_DIR/dio-buf-fault-$seq.src
+dst=$TEST_DIR/dio-buf-fault-$seq.dst
+
+rm -rf "$src" "$dst"
+
+echo "Silence is golden"
+
+$XFS_IO_PROG -fc "pwrite -q -S 0xcd 0 $((2 * 1024 * 1024))" $src
+$here/src/dio-buf-fault $src $dst > /dev/null || _fail "failed doing the dio copy"
+diff $src $dst
+
+# success, all done
+status=$?
+exit
diff --git a/tests/generic/708.out b/tests/generic/708.out
new file mode 100644 (file)
index 0000000..33c478a
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 708
+Silence is golden
diff --git a/tests/generic/709 b/tests/generic/709
new file mode 100755 (executable)
index 0000000..4bd591b
--- /dev/null
@@ -0,0 +1,55 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 709
+#
+# Can we use swapext to make the quota accounting incorrect?
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext quota
+
+# Import common functions.
+. ./common/filter
+. ./common/quota
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_user
+_require_nobody
+_require_quota
+_require_xfs_quota
+_require_scratch
+
+# Format filesystem and set up quota limits
+_scratch_mkfs > $seqres.full
+_qmount_option "usrquota,grpquota"
+_qmount >> $seqres.full
+
+# Set up initial files
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 256k -b 1m' $SCRATCH_MNT/a >> $seqres.full
+chown $qa_user $SCRATCH_MNT/a
+$XFS_IO_PROG -f -c 'pwrite -S 0x59 0 64k -b 64k' -c 'truncate 256k' $SCRATCH_MNT/b >> $seqres.full
+chown nobody $SCRATCH_MNT/b
+
+echo before swapext >> $seqres.full
+$XFS_QUOTA_PROG -x -c 'report -a' $SCRATCH_MNT >> $seqres.full
+stat $SCRATCH_MNT/* >> $seqres.full
+
+# Now try to swap the extents of the two files.  The command is allowed to
+# fail with -EINVAL (since that's what the first kernel fix does) or succeed
+# (because subsequent rewrites can handle quota).  Whatever the outcome, the
+# quota usage check at the end should never show a discrepancy.
+$XFS_IO_PROG -c "swapext $SCRATCH_MNT/b" $SCRATCH_MNT/a &> $tmp.swap
+cat $tmp.swap >> $seqres.full
+grep -v 'Invalid argument' $tmp.swap
+
+echo after swapext >> $seqres.full
+$XFS_QUOTA_PROG -x -c 'report -a' $SCRATCH_MNT >> $seqres.full
+stat $SCRATCH_MNT/* >> $seqres.full
+
+_check_quota_usage
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/709.out b/tests/generic/709.out
new file mode 100644 (file)
index 0000000..e2d83cf
--- /dev/null
@@ -0,0 +1,3 @@
+QA output created by 709
+Comparing user usage
+Comparing group usage
diff --git a/tests/generic/710 b/tests/generic/710
new file mode 100755 (executable)
index 0000000..c7fca05
--- /dev/null
@@ -0,0 +1,53 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 710
+#
+# Can we use swapext to exceed the quota enforcement?
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext quota
+
+# Import common functions.
+. ./common/filter
+. ./common/quota
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_user
+_require_nobody
+_require_quota
+_require_xfs_quota
+_require_scratch
+
+# Format filesystem and set up quota limits
+_scratch_mkfs > $seqres.full
+_qmount_option "usrquota,grpquota"
+_qmount >> $seqres.full
+
+# Set up initial files
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 256k -b 1m' $SCRATCH_MNT/a >> $seqres.full
+chown $qa_user $SCRATCH_MNT/a
+$XFS_IO_PROG -f -c 'pwrite -S 0x59 0 64k -b 64k' -c 'truncate 256k' $SCRATCH_MNT/b >> $seqres.full
+chown nobody $SCRATCH_MNT/b
+
+# Set up a quota limit
+$XFS_QUOTA_PROG -x -c "limit -u bhard=70k nobody" $SCRATCH_MNT
+
+echo before swapext >> $seqres.full
+$XFS_QUOTA_PROG -x -c 'report -a' $SCRATCH_MNT >> $seqres.full
+stat $SCRATCH_MNT/* >> $seqres.full
+
+# Now try to swapext
+$XFS_IO_PROG -c "swapext $SCRATCH_MNT/b" $SCRATCH_MNT/a
+
+echo after swapext >> $seqres.full
+$XFS_QUOTA_PROG -x -c 'report -a' $SCRATCH_MNT >> $seqres.full
+stat $SCRATCH_MNT/* >> $seqres.full
+
+_check_quota_usage
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/710.out b/tests/generic/710.out
new file mode 100644 (file)
index 0000000..a2aa981
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 710
+swapext: Disk quota exceeded
+Comparing user usage
+Comparing group usage
diff --git a/tests/generic/711 b/tests/generic/711
new file mode 100755 (executable)
index 0000000..f1318b3
--- /dev/null
@@ -0,0 +1,47 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 711
+#
+# Make sure that swapext won't touch a swap file.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       test -e "$dir/a" && swapoff $dir/a
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_test
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+
+# Set up a fragmented swapfile and a dummy donor file.
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 32m -b 1m' -c fsync $dir/a >> $seqres.full
+$here/src/punch-alternating $dir/a
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 32m -b 1m' -c fsync $dir/a >> $seqres.full
+$MKSWAP_PROG $dir/a >> $seqres.full
+
+$XFS_IO_PROG -f -c 'pwrite -S 0x59 0 32m -b 1m' $dir/b >> $seqres.full
+
+swapon $dir/a || _notrun 'failed to swapon'
+
+# Now try to swapext.  The old code would return EINVAL for swapfiles
+# even though everything else in the VFS returns ETXTBSY.
+$XFS_IO_PROG -c "swapext $dir/b" $dir/a 2>&1 | \
+       sed -e 's/swapext: Invalid argument/swapext: Text file busy/g'
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/711.out b/tests/generic/711.out
new file mode 100644 (file)
index 0000000..6c590f7
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 711
+swapext: Text file busy
diff --git a/tests/generic/712 b/tests/generic/712
new file mode 100755 (executable)
index 0000000..d4a7054
--- /dev/null
@@ -0,0 +1,58 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 712
+#
+# Make sure that swapext modifies ctime and not mtime of the file.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_require_test_program punch-alternating
+_require_xfs_io_command swapext '-v exchrange'
+_require_test
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+
+# Set up initial files
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 256k -b 1m' $dir/a >> $seqres.full
+$here/src/punch-alternating $dir/a
+$XFS_IO_PROG -f -c 'pwrite -S 0x59 0 256k -b 1m' $dir/b >> $seqres.full
+
+# Snapshot the 'a' file before we swap
+echo before >> $seqres.full
+md5sum $dir/a $dir/b >> $seqres.full
+old_mtime="$(echo $(stat -c '%y' $dir/a $dir/b))"
+old_ctime="$(echo $(stat -c '%z' $dir/a $dir/b))"
+stat -c '%y %Y %z %Z' $dir/a $dir/b >> $seqres.full
+
+# Now try to swapext
+$XFS_IO_PROG -c "swapext $dir/b" $dir/a
+
+# Snapshot the 'a' file after we swap
+echo after >> $seqres.full
+md5sum $dir/a $dir/b >> $seqres.full
+new_mtime="$(echo $(stat -c '%y' $dir/a $dir/b))"
+new_ctime="$(echo $(stat -c '%z' $dir/a $dir/b))"
+stat -c '%y %Y %z %Z' $dir/a $dir/b >> $seqres.full
+
+test "$new_mtime" = "$old_mtime" && echo "mtime: $new_mtime == $old_mtime"
+test "$new_ctime" = "$old_ctime" && echo "ctime: $new_ctime == $old_ctime"
+
+# success, all done
+echo Silence is golden.
+status=0
+exit
diff --git a/tests/generic/712.out b/tests/generic/712.out
new file mode 100644 (file)
index 0000000..33a76b2
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 712
+Silence is golden.
diff --git a/tests/generic/713 b/tests/generic/713
new file mode 100755 (executable)
index 0000000..9b742ee
--- /dev/null
@@ -0,0 +1,100 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 713
+#
+# Test swapext between ranges of two different files.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange -s 64k -l 64k'
+_require_xfs_io_command "falloc"
+_require_test
+
+filesnap() {
+       echo "$1"
+       if [ "$2" != "$3" ]; then
+               md5sum $2 $3 | _filter_test_dir
+       else
+               md5sum $2 | _filter_test_dir
+       fi
+}
+
+test_swapext_once() {
+       filesnap "$1: before swapext" $dir/$3 $dir/$4
+       $XFS_IO_PROG -c "swapext -v exchrange $2 $dir/$3" $dir/$4
+       filesnap "$1: after swapext" $dir/$3 $dir/$4
+       _test_cycle_mount
+       filesnap "$1: after cycling mount" $dir/$3 $dir/$4
+       echo
+}
+
+test_swapext_two() {
+       # swapext the same range of two files
+       test_swapext_once "$*: samerange" \
+               "-s $((blksz * 3)) -d $((blksz * 3)) -l $((blksz * 5))" b a
+
+       # swapext different ranges of two files
+       test_swapext_once "$*: diffrange" \
+               "-s $((blksz * 37)) -d $((blksz * 47)) -l $((blksz * 7))" b a
+
+       # swapext overlapping ranges of two files
+       test_swapext_once "$*: overlap" \
+               "-s $((blksz * 17)) -d $((blksz * 23)) -l $((blksz * 7))" b a
+}
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+nrblks=57
+_require_congruent_file_oplen $TEST_DIR $blksz
+
+# Set up some simple files for a first test.
+rm -rf $dir/a $dir/b
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+_pwrite_byte 0x59 0 $((blksz * nrblks)) $dir/b >> $seqres.full
+test_swapext_two "simple"
+
+# Make some files that don't end an aligned offset.
+rm -rf $dir/a $dir/b
+_pwrite_byte 0x58 0 $(( (blksz * nrblks) + 37)) $dir/a >> $seqres.full
+_pwrite_byte 0x59 0 $(( (blksz * nrblks) + 37)) $dir/b >> $seqres.full
+test_swapext_once "unalignedeof" "" a b
+
+# Set up some crazy rainbow files
+rm -rf $dir/a $dir/b
+_weave_file_rainbow $blksz $nrblks $dir/a >> $seqres.full
+_weave_file_rainbow $blksz $nrblks $dir/b >> $seqres.full
+test_swapext_two "rainbow"
+
+# Now set up a simple file for testing within the same file
+rm -rf $dir/c
+$XFS_IO_PROG -f -c "pwrite -S 0x58 0 $((blksz * nrblks))" \
+       -c "pwrite -S 0x59 $((blksz * nrblks)) $((blksz * nrblks))" \
+       $dir/c >> $seqres.full
+
+# swapext the same offset into the 'X' and 'Y' regions of the file
+test_swapext_once "single: sameXandY" \
+       "-s $((blksz * 3)) -d $((blksz * (nrblks + 3))) -l $((blksz * 5))" c c
+
+# swapext the same offset into the 'X' and 'Y' regions of the file
+test_swapext_once "single: overlap" \
+       "-s $((blksz * 13)) -d $((blksz * 17)) -l $((blksz * 5))" c c
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/713.out b/tests/generic/713.out
new file mode 100644 (file)
index 0000000..cb58e93
--- /dev/null
@@ -0,0 +1,86 @@
+QA output created by 713
+simple: samerange: before swapext
+db85d578204631f2b4eb1e73974253c2  TEST_DIR/test-713/b
+d0425612f15c6071022cf7127620f63d  TEST_DIR/test-713/a
+simple: samerange: after swapext
+20beef1c9ed7de02e4229c69bd43bd8f  TEST_DIR/test-713/b
+e7697fa99d08f7eb76fa3fb963fe916a  TEST_DIR/test-713/a
+simple: samerange: after cycling mount
+20beef1c9ed7de02e4229c69bd43bd8f  TEST_DIR/test-713/b
+e7697fa99d08f7eb76fa3fb963fe916a  TEST_DIR/test-713/a
+
+simple: diffrange: before swapext
+20beef1c9ed7de02e4229c69bd43bd8f  TEST_DIR/test-713/b
+e7697fa99d08f7eb76fa3fb963fe916a  TEST_DIR/test-713/a
+simple: diffrange: after swapext
+cd32ce54c295fcdf571ce7f8220fac56  TEST_DIR/test-713/b
+d9771c5bb6d9db00b9abe65a4410e1a6  TEST_DIR/test-713/a
+simple: diffrange: after cycling mount
+cd32ce54c295fcdf571ce7f8220fac56  TEST_DIR/test-713/b
+d9771c5bb6d9db00b9abe65a4410e1a6  TEST_DIR/test-713/a
+
+simple: overlap: before swapext
+cd32ce54c295fcdf571ce7f8220fac56  TEST_DIR/test-713/b
+d9771c5bb6d9db00b9abe65a4410e1a6  TEST_DIR/test-713/a
+simple: overlap: after swapext
+e0fff655f6a08fc2f03ee01e4767060c  TEST_DIR/test-713/b
+ec7d764c85e583e305028c9cba5b25b6  TEST_DIR/test-713/a
+simple: overlap: after cycling mount
+e0fff655f6a08fc2f03ee01e4767060c  TEST_DIR/test-713/b
+ec7d764c85e583e305028c9cba5b25b6  TEST_DIR/test-713/a
+
+unalignedeof: before swapext
+9f8c731a4f1946ffdda8c33e82417f2d  TEST_DIR/test-713/a
+7a5d2ba7508226751c835292e28cd227  TEST_DIR/test-713/b
+unalignedeof: after swapext
+7a5d2ba7508226751c835292e28cd227  TEST_DIR/test-713/a
+9f8c731a4f1946ffdda8c33e82417f2d  TEST_DIR/test-713/b
+unalignedeof: after cycling mount
+7a5d2ba7508226751c835292e28cd227  TEST_DIR/test-713/a
+9f8c731a4f1946ffdda8c33e82417f2d  TEST_DIR/test-713/b
+
+rainbow: samerange: before swapext
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-713/b
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-713/a
+rainbow: samerange: after swapext
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-713/b
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-713/a
+rainbow: samerange: after cycling mount
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-713/b
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-713/a
+
+rainbow: diffrange: before swapext
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-713/b
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-713/a
+rainbow: diffrange: after swapext
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-713/b
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-713/a
+rainbow: diffrange: after cycling mount
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-713/b
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-713/a
+
+rainbow: overlap: before swapext
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-713/b
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-713/a
+rainbow: overlap: after swapext
+6753bc585e3c71d53bfaae11d2ffee99  TEST_DIR/test-713/b
+39597abd4d9d0c9ceac22b77eb00c373  TEST_DIR/test-713/a
+rainbow: overlap: after cycling mount
+6753bc585e3c71d53bfaae11d2ffee99  TEST_DIR/test-713/b
+39597abd4d9d0c9ceac22b77eb00c373  TEST_DIR/test-713/a
+
+single: sameXandY: before swapext
+39e17753fa9e923a3b5928e13775e358  TEST_DIR/test-713/c
+single: sameXandY: after swapext
+8262c617070703fb0e2a28d8f05e3112  TEST_DIR/test-713/c
+single: sameXandY: after cycling mount
+8262c617070703fb0e2a28d8f05e3112  TEST_DIR/test-713/c
+
+single: overlap: before swapext
+8262c617070703fb0e2a28d8f05e3112  TEST_DIR/test-713/c
+swapext: Invalid argument
+single: overlap: after swapext
+8262c617070703fb0e2a28d8f05e3112  TEST_DIR/test-713/c
+single: overlap: after cycling mount
+8262c617070703fb0e2a28d8f05e3112  TEST_DIR/test-713/c
+
diff --git a/tests/generic/714 b/tests/generic/714
new file mode 100755 (executable)
index 0000000..b48a4b7
--- /dev/null
@@ -0,0 +1,116 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 714
+#
+# Test swapext between ranges of two different files, when one of the files
+# is shared.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_xfs_io_command "falloc"
+_require_test_reflink
+
+filesnap() {
+       echo "$1"
+       if [ "$2" != "$3" ]; then
+               md5sum $2 $3 | _filter_test_dir
+       else
+               md5sum $2 | _filter_test_dir
+       fi
+}
+
+test_swapext_once() {
+       filesnap "$1: before swapext" $dir/$3 $dir/$4
+       $XFS_IO_PROG -c "swapext -v exchrange $2 $dir/$3" $dir/$4
+       filesnap "$1: after swapext" $dir/$3 $dir/$4
+       _test_cycle_mount
+       filesnap "$1: after cycling mount" $dir/$3 $dir/$4
+       echo
+}
+
+test_swapext_two() {
+       # swapext the same range of two files
+       test_swapext_once "$*: samerange" \
+               "-s $((blksz * 3)) -d $((blksz * 3)) -l $((blksz * 5))" b a
+
+       # swapext different ranges of two files
+       test_swapext_once "$*: diffrange" \
+               "-s $((blksz * 37)) -d $((blksz * 47)) -l $((blksz * 7))" b a
+
+       # swapext overlapping ranges of two files
+       test_swapext_once "$*: overlap" \
+               "-s $((blksz * 17)) -d $((blksz * 23)) -l $((blksz * 7))" b a
+
+       # Now let's overwrite a entirely to make sure COW works
+       echo "overwrite A and B entirely"
+       md5sum $dir/sharea | _filter_test_dir
+       $XFS_IO_PROG -c "pwrite -S 0x60 0 $((blksz * nrblks))" $dir/a >> $seqres.full
+       $XFS_IO_PROG -c "pwrite -S 0x60 0 $((blksz * nrblks))" $dir/b >> $seqres.full
+       md5sum $dir/sharea | _filter_test_dir
+       _test_cycle_mount
+       md5sum $dir/sharea | _filter_test_dir
+       echo
+}
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+_require_congruent_file_oplen $TEST_DIR $blksz
+nrblks=57
+
+# Set up some simple files for a first test.
+rm -f $dir/a $dir/b $dir/sharea
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+_pwrite_byte 0x59 0 $((blksz * nrblks)) $dir/b >> $seqres.full
+_cp_reflink $dir/a $dir/sharea
+test_swapext_two "simple"
+
+# Set up some crazy rainbow files
+rm -f $dir/a $dir/b $dir/sharea
+_weave_file_rainbow $blksz $nrblks $dir/a >> $seqres.full
+_weave_file_rainbow $blksz $nrblks $dir/b >> $seqres.full
+_cp_reflink $dir/a $dir/sharea
+test_swapext_two "rainbow"
+
+# Now set up a simple file for testing within the same file
+rm -f $dir/c $dir/sharec
+$XFS_IO_PROG -f -c "pwrite -S 0x58 0 $((blksz * nrblks))" \
+       -c "pwrite -S 0x59 $((blksz * nrblks)) $((blksz * nrblks))" \
+       $dir/c >> $seqres.full
+_cp_reflink $dir/c $dir/sharec
+
+# swapext the same offset into the 'X' and 'Y' regions of the file
+test_swapext_once "single: sameXandY" \
+       "-s $((blksz * 3)) -d $((blksz * (nrblks + 3))) -l $((blksz * 5))" c c
+
+# swapext the same offset into the 'X' and 'Y' regions of the file
+test_swapext_once "single: overlap" \
+       "-s $((blksz * 13)) -d $((blksz * 17)) -l $((blksz * 5))" c c
+
+# Now let's overwrite a entirely to make sure COW works
+echo "overwrite C entirely"
+md5sum $dir/sharec | _filter_test_dir
+$XFS_IO_PROG -c "pwrite -S 0x60 0 $((blksz * nrblks))" $dir/c >> $seqres.full
+md5sum $dir/sharec | _filter_test_dir
+_test_cycle_mount
+md5sum $dir/sharec | _filter_test_dir
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/714.out b/tests/generic/714.out
new file mode 100644 (file)
index 0000000..bd45efc
--- /dev/null
@@ -0,0 +1,90 @@
+QA output created by 714
+simple: samerange: before swapext
+db85d578204631f2b4eb1e73974253c2  TEST_DIR/test-714/b
+d0425612f15c6071022cf7127620f63d  TEST_DIR/test-714/a
+simple: samerange: after swapext
+20beef1c9ed7de02e4229c69bd43bd8f  TEST_DIR/test-714/b
+e7697fa99d08f7eb76fa3fb963fe916a  TEST_DIR/test-714/a
+simple: samerange: after cycling mount
+20beef1c9ed7de02e4229c69bd43bd8f  TEST_DIR/test-714/b
+e7697fa99d08f7eb76fa3fb963fe916a  TEST_DIR/test-714/a
+
+simple: diffrange: before swapext
+20beef1c9ed7de02e4229c69bd43bd8f  TEST_DIR/test-714/b
+e7697fa99d08f7eb76fa3fb963fe916a  TEST_DIR/test-714/a
+simple: diffrange: after swapext
+cd32ce54c295fcdf571ce7f8220fac56  TEST_DIR/test-714/b
+d9771c5bb6d9db00b9abe65a4410e1a6  TEST_DIR/test-714/a
+simple: diffrange: after cycling mount
+cd32ce54c295fcdf571ce7f8220fac56  TEST_DIR/test-714/b
+d9771c5bb6d9db00b9abe65a4410e1a6  TEST_DIR/test-714/a
+
+simple: overlap: before swapext
+cd32ce54c295fcdf571ce7f8220fac56  TEST_DIR/test-714/b
+d9771c5bb6d9db00b9abe65a4410e1a6  TEST_DIR/test-714/a
+simple: overlap: after swapext
+e0fff655f6a08fc2f03ee01e4767060c  TEST_DIR/test-714/b
+ec7d764c85e583e305028c9cba5b25b6  TEST_DIR/test-714/a
+simple: overlap: after cycling mount
+e0fff655f6a08fc2f03ee01e4767060c  TEST_DIR/test-714/b
+ec7d764c85e583e305028c9cba5b25b6  TEST_DIR/test-714/a
+
+overwrite A and B entirely
+d0425612f15c6071022cf7127620f63d  TEST_DIR/test-714/sharea
+d0425612f15c6071022cf7127620f63d  TEST_DIR/test-714/sharea
+d0425612f15c6071022cf7127620f63d  TEST_DIR/test-714/sharea
+
+rainbow: samerange: before swapext
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/b
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/a
+rainbow: samerange: after swapext
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/b
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/a
+rainbow: samerange: after cycling mount
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/b
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/a
+
+rainbow: diffrange: before swapext
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/b
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/a
+rainbow: diffrange: after swapext
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/b
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/a
+rainbow: diffrange: after cycling mount
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/b
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/a
+
+rainbow: overlap: before swapext
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/b
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/a
+rainbow: overlap: after swapext
+6753bc585e3c71d53bfaae11d2ffee99  TEST_DIR/test-714/b
+39597abd4d9d0c9ceac22b77eb00c373  TEST_DIR/test-714/a
+rainbow: overlap: after cycling mount
+6753bc585e3c71d53bfaae11d2ffee99  TEST_DIR/test-714/b
+39597abd4d9d0c9ceac22b77eb00c373  TEST_DIR/test-714/a
+
+overwrite A and B entirely
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/sharea
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/sharea
+48b41ee1970eb71064b77181f42634cf  TEST_DIR/test-714/sharea
+
+single: sameXandY: before swapext
+39e17753fa9e923a3b5928e13775e358  TEST_DIR/test-714/c
+single: sameXandY: after swapext
+8262c617070703fb0e2a28d8f05e3112  TEST_DIR/test-714/c
+single: sameXandY: after cycling mount
+8262c617070703fb0e2a28d8f05e3112  TEST_DIR/test-714/c
+
+single: overlap: before swapext
+8262c617070703fb0e2a28d8f05e3112  TEST_DIR/test-714/c
+swapext: Invalid argument
+single: overlap: after swapext
+8262c617070703fb0e2a28d8f05e3112  TEST_DIR/test-714/c
+single: overlap: after cycling mount
+8262c617070703fb0e2a28d8f05e3112  TEST_DIR/test-714/c
+
+overwrite C entirely
+39e17753fa9e923a3b5928e13775e358  TEST_DIR/test-714/sharec
+39e17753fa9e923a3b5928e13775e358  TEST_DIR/test-714/sharec
+39e17753fa9e923a3b5928e13775e358  TEST_DIR/test-714/sharec
diff --git a/tests/generic/715 b/tests/generic/715
new file mode 100755 (executable)
index 0000000..595953d
--- /dev/null
@@ -0,0 +1,76 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 715
+#
+# Test swapext between two files of unlike size.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange -s 64k -l 64k'
+_require_test
+
+filesnap() {
+       echo "$1"
+       if [ "$2" != "$3" ]; then
+               md5sum $2 $3 | _filter_test_dir
+       else
+               md5sum $2 | _filter_test_dir
+       fi
+}
+
+test_swapext_once() {
+       local tag=$1
+       local a_len=$2
+       local b_len=$3
+       local a_off=$4
+       local b_off=$5
+       local len=$6
+
+       # len is either a block count or -e to swap to EOF
+       if [ "$len" != "-e" ]; then
+               len="-l $((blksz * len))"
+       fi
+
+       rm -f $dir/a $dir/b
+       _pwrite_byte 0x58 0 $((blksz * a_len)) $dir/a >> $seqres.full
+       _pwrite_byte 0x59 0 $((blksz * b_len)) $dir/b >> $seqres.full
+       filesnap "$tag: before swapext" $dir/a $dir/b
+
+       cmd="swapext -v exchrange -s $((blksz * a_off)) -d $((blksz * b_off)) $len $dir/a"
+       echo "$cmd" >> $seqres.full
+       $XFS_IO_PROG -c "$cmd" $dir/b
+       filesnap "$tag: after swapext" $dir/a $dir/b
+
+       _test_cycle_mount
+       filesnap "$tag: after cycling mount" $dir/a $dir/b
+       echo
+}
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+
+test_swapext_once "last 5 blocks" 27 37 22 32 5
+
+test_swapext_once "whole file to eof" 27 37 0 0 -e
+
+test_swapext_once "blocks 30-40" 27 37 30 30 10
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/715.out b/tests/generic/715.out
new file mode 100644 (file)
index 0000000..b4a6565
--- /dev/null
@@ -0,0 +1,32 @@
+QA output created by 715
+last 5 blocks: before swapext
+207ea56e0ccbf50d38fd3a2d842aa170  TEST_DIR/test-715/a
+eb58941d31f5be1e4e22df8c536dd490  TEST_DIR/test-715/b
+last 5 blocks: after swapext
+3f34470fe9feb8513d5f3a8538f2c5f3  TEST_DIR/test-715/a
+c3daca7dd9218371cd0dc64f11e4b0bf  TEST_DIR/test-715/b
+last 5 blocks: after cycling mount
+3f34470fe9feb8513d5f3a8538f2c5f3  TEST_DIR/test-715/a
+c3daca7dd9218371cd0dc64f11e4b0bf  TEST_DIR/test-715/b
+
+whole file to eof: before swapext
+207ea56e0ccbf50d38fd3a2d842aa170  TEST_DIR/test-715/a
+eb58941d31f5be1e4e22df8c536dd490  TEST_DIR/test-715/b
+whole file to eof: after swapext
+eb58941d31f5be1e4e22df8c536dd490  TEST_DIR/test-715/a
+207ea56e0ccbf50d38fd3a2d842aa170  TEST_DIR/test-715/b
+whole file to eof: after cycling mount
+eb58941d31f5be1e4e22df8c536dd490  TEST_DIR/test-715/a
+207ea56e0ccbf50d38fd3a2d842aa170  TEST_DIR/test-715/b
+
+blocks 30-40: before swapext
+207ea56e0ccbf50d38fd3a2d842aa170  TEST_DIR/test-715/a
+eb58941d31f5be1e4e22df8c536dd490  TEST_DIR/test-715/b
+swapext: Invalid argument
+blocks 30-40: after swapext
+207ea56e0ccbf50d38fd3a2d842aa170  TEST_DIR/test-715/a
+eb58941d31f5be1e4e22df8c536dd490  TEST_DIR/test-715/b
+blocks 30-40: after cycling mount
+207ea56e0ccbf50d38fd3a2d842aa170  TEST_DIR/test-715/a
+eb58941d31f5be1e4e22df8c536dd490  TEST_DIR/test-715/b
+
diff --git a/tests/generic/716 b/tests/generic/716
new file mode 100755 (executable)
index 0000000..25976ab
--- /dev/null
@@ -0,0 +1,122 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 716
+#
+# Test atomic file updates when (a) the length is the same; (b) the length
+# is different; and (c) someone modifies the original file and we need to
+# cancel the update.  The file contents are cloned into the staging file,
+# and some of the contents are updated.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_xfs_io_command startupdate
+_require_test_reflink
+_require_test
+
+filesnap() {
+       echo "$1"
+       md5sum $2 | _filter_test_dir
+}
+
+mkfile() {
+       rm -f $dir/a
+       _pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+       sync
+}
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+nrblks=64
+
+# Use the atomic file update staging prototype in xfs_io to update a file.
+mkfile
+filesnap "before commit" $dir/a
+
+$XFS_IO_PROG \
+       -c 'startupdate' \
+       -c 'pwrite -S 0x60 44k 55k -b 1m' \
+       -c 'commitupdate -q' \
+       "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after commit" $dir/a
+echo
+
+# Use the atomic file updates to replace a file with a shorter file.
+mkfile
+filesnap "before shorten commit" $dir/a
+
+$XFS_IO_PROG \
+       -c 'startupdate' \
+       -c 'truncate 55k' \
+       -c 'pwrite -S 0x60 0 55k' \
+       -c 'commitupdate -q' \
+       "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after shorten commit" $dir/a
+echo
+
+# Use the atomic file updates to replace a file with a longer file.
+mkfile
+filesnap "before lengthen commit" $dir/a
+
+$XFS_IO_PROG \
+       -c 'startupdate' \
+       -c "pwrite -S 0x60 0 $(( (blksz * nrblks) + 37373 ))" \
+       -c 'commitupdate -q' \
+       "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after lengthen commit" $dir/a
+echo
+
+# Use the atomic file update staging prototype in xfs_io to cancel updating a
+# file.
+mkfile
+filesnap "before cancel" $dir/a
+
+$XFS_IO_PROG \
+       -c 'startupdate' \
+       -c 'pwrite -S 0x60 44k 55k -b 1m' \
+       -c 'cancelupdate' \
+       "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after cancel" $dir/a
+echo
+
+# Now try the update but with the A file open separately so that we clobber
+# mtime and fail the update.
+mkfile
+filesnap "before fail commit" $dir/a
+
+$XFS_IO_PROG \
+       -c "open $dir/a" \
+       -c 'startupdate' \
+       -c 'pwrite -S 0x58 44k 55k -b 1m' \
+       -c 'file 0' \
+       -c 'close' \
+       -c 'pwrite -S 0x61 22k 11k -b 1m' \
+       -c 'commitupdate -q' \
+       "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after fail commit" $dir/a
+echo
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/716.out b/tests/generic/716.out
new file mode 100644 (file)
index 0000000..acfc963
--- /dev/null
@@ -0,0 +1,48 @@
+QA output created by 716
+before commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-716/a
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-716/a'.
+after commit
+bedbd22b58a680219a1225353f6195fa  TEST_DIR/test-716/a
+
+before shorten commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-716/a
+wrote 56320/56320 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-716/a'.
+after shorten commit
+52353039d89c5f2b76b9003464e5276a  TEST_DIR/test-716/a
+
+before lengthen commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-716/a
+wrote 4231677/4231677 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-716/a'.
+after lengthen commit
+1839e7c6bf616160dc51b12179db2642  TEST_DIR/test-716/a
+
+before cancel
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-716/a
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Cancelled updates to 'TEST_DIR/test-716/a'.
+after cancel
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-716/a
+
+before fail commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-716/a
+committing update: Device or resource busy
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[000] TEST_DIR/test-716/a (xfs,non-sync,non-direct,read-write)
+ 001  TEST_DIR/test-716/a (fileupdate) (xfs,non-sync,non-direct,read-write)
+[000] TEST_DIR/test-716/a (fileupdate) (xfs,non-sync,non-direct,read-write)
+wrote 11264/11264 bytes at offset 22528
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+after fail commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-716/a
+
diff --git a/tests/generic/717 b/tests/generic/717
new file mode 100755 (executable)
index 0000000..2c45e71
--- /dev/null
@@ -0,0 +1,101 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 717
+#
+# Try invalid parameters to see if they fail.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_xfs_io_command startupdate
+_require_test
+_require_scratch
+_require_chattr i
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+nrblks=64
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/b >> $seqres.full
+
+echo Immutable files
+$XFS_IO_PROG -c 'chattr +i' -c "swapext $dir/b" $dir/a
+$CHATTR_PROG -i $dir/a
+
+echo Readonly files
+$XFS_IO_PROG -r -c "swapext $dir/b" $dir/a
+
+echo Directories
+$XFS_IO_PROG -c "swapext $dir/b" $dir
+
+echo Unaligned ranges
+$XFS_IO_PROG -c "swapext -s 37 -d 61 -l 17 $dir/b" $dir/a
+
+echo file1 range entirely beyond EOF
+$XFS_IO_PROG -c "swapext -s $(( blksz * (nrblks + 500) )) -d 0 -l $blksz $dir/b" $dir/a
+
+echo file2 range entirely beyond EOF
+$XFS_IO_PROG -c "swapext -d $(( blksz * (nrblks + 500) )) -s 0 -l $blksz $dir/b" $dir/a
+
+echo Both ranges entirely beyond EOF
+$XFS_IO_PROG -c "swapext -d $(( blksz * (nrblks + 500) )) -s $(( blksz * (nrblks + 500) )) -l $blksz $dir/b" $dir/a
+
+echo file1 range crossing EOF
+$XFS_IO_PROG -c "swapext -s $(( blksz * (nrblks - 1) )) -d 0 -l $((2 * blksz)) $dir/b" $dir/a
+
+echo file2 range crossing EOF
+$XFS_IO_PROG -c "swapext -d $(( blksz * (nrblks  - 1) )) -s 0 -l $((2 * blksz)) $dir/b" $dir/a
+
+echo Both ranges crossing EOF
+$XFS_IO_PROG -c "swapext -d $(( blksz * (nrblks - 1) )) -s $(( blksz * (nrblks - 1) )) -l $((blksz * 2)) $dir/b" $dir/a
+
+echo file1 unaligned EOF to file2 nowhere near EOF
+_pwrite_byte 0x58 $((blksz * nrblks)) 37 $dir/a >> $seqres.full
+_pwrite_byte 0x59 $((blksz * nrblks)) 37 $dir/b >> $seqres.full
+$XFS_IO_PROG -c "swapext -d 0 -s $(( blksz * nrblks )) -l 37 $dir/b" $dir/a
+
+echo file2 unaligned EOF to file1 nowhere near EOF
+$XFS_IO_PROG -c "swapext -s 0 -d $(( blksz * nrblks )) -l 37 $dir/b" $dir/a
+
+echo Files of unequal length
+_pwrite_byte 0x58 $((blksz * nrblks)) $((blksz * 2)) $dir/a >> $seqres.full
+_pwrite_byte 0x59 $((blksz * nrblks)) $blksz $dir/b >> $seqres.full
+$XFS_IO_PROG -c "swapext $dir/b" $dir/a
+
+echo Files on different filesystems
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $SCRATCH_MNT/c >> $seqres.full
+$XFS_IO_PROG -c "swapext $SCRATCH_MNT/c" $dir/a
+
+echo Files on different mounts
+mkdir -p $SCRATCH_MNT/xyz
+mount --bind $dir $SCRATCH_MNT/xyz --bind
+_pwrite_byte 0x60 0 $((blksz * (nrblks + 2))) $dir/c >> $seqres.full
+$XFS_IO_PROG -c "swapext $SCRATCH_MNT/xyz/c" $dir/a
+umount $SCRATCH_MNT/xyz
+
+echo Swapping a file with itself
+$XFS_IO_PROG -c "swapext $dir/a" $dir/a
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/717.out b/tests/generic/717.out
new file mode 100644 (file)
index 0000000..7a7ab30
--- /dev/null
@@ -0,0 +1,33 @@
+QA output created by 717
+Immutable files
+swapext: Operation not permitted
+Readonly files
+swapext: Bad file descriptor
+Directories
+swapext: Is a directory
+Unaligned ranges
+swapext: Invalid argument
+file1 range entirely beyond EOF
+swapext: Invalid argument
+file2 range entirely beyond EOF
+swapext: Invalid argument
+Both ranges entirely beyond EOF
+swapext: Invalid argument
+file1 range crossing EOF
+swapext: Invalid argument
+file2 range crossing EOF
+swapext: Invalid argument
+Both ranges crossing EOF
+swapext: Invalid argument
+file1 unaligned EOF to file2 nowhere near EOF
+swapext: Invalid argument
+file2 unaligned EOF to file1 nowhere near EOF
+swapext: Invalid argument
+Files of unequal length
+swapext: Bad address
+Files on different filesystems
+swapext: Invalid cross-device link
+Files on different mounts
+swapext: Invalid cross-device link
+Swapping a file with itself
+swapext: Invalid argument
diff --git a/tests/generic/718 b/tests/generic/718
new file mode 100755 (executable)
index 0000000..f53d184
--- /dev/null
@@ -0,0 +1,48 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 718
+#
+# Make sure swapext honors RLIMIT_FSIZE.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_test
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+nrblks=64
+
+# Create some 4M files to test swapext
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+_pwrite_byte 0x59 0 $((blksz * nrblks)) $dir/b >> $seqres.full
+sync
+md5sum $dir/a $dir/b | _filter_test_dir
+
+# Set FSIZE to twice the blocksize (IOWs, 128k)
+ulimit -f $(( (blksz * 2) / 512))
+ulimit -a >> $seqres.full
+
+# Now try to swapext
+$XFS_IO_PROG -c "swapext $dir/b" $dir/a
+md5sum $dir/a $dir/b | _filter_test_dir
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/718.out b/tests/generic/718.out
new file mode 100644 (file)
index 0000000..f3f26f7
--- /dev/null
@@ -0,0 +1,6 @@
+QA output created by 718
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-718/a
+901e136269b8d283d311697b7c6dc1f2  TEST_DIR/test-718/b
+swapext: Invalid argument
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-718/a
+901e136269b8d283d311697b7c6dc1f2  TEST_DIR/test-718/b
diff --git a/tests/generic/719 b/tests/generic/719
new file mode 100755 (executable)
index 0000000..fe0b9d0
--- /dev/null
@@ -0,0 +1,105 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 719
+#
+# Test atomic file replacement when (a) the length is the same; (b) the length
+# is different; and (c) someone modifies the original file and we need to
+# cancel the update.  The staging file is created empty, which implies that the
+# caller wants a full file replacement.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_xfs_io_command startupdate '-e'
+_require_test
+
+filesnap() {
+       echo "$1"
+       md5sum $2 | _filter_test_dir
+}
+
+mkfile() {
+       rm -f $dir/a
+       _pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+       sync
+}
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+nrblks=64
+
+# Use the atomic file update staging prototype in xfs_io to update a file.
+mkfile
+filesnap "before commit" $dir/a
+
+$XFS_IO_PROG \
+       -c 'startupdate -e' \
+       -c "pwrite -S 0x60 0 $((blksz * nrblks))" \
+       -c 'commitupdate -q' \
+       "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after commit" $dir/a
+echo
+
+# Use the atomic file updates to replace a file with a shorter file.
+mkfile
+filesnap "before shorten commit" $dir/a
+
+$XFS_IO_PROG \
+       -c 'startupdate -e' \
+       -c 'pwrite -S 0x60 0 55k' \
+       -c 'commitupdate -q' \
+       "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after shorten commit" $dir/a
+echo
+
+# Use the atomic file updates to replace a file with a longer file.
+mkfile
+filesnap "before lengthen commit" $dir/a
+
+$XFS_IO_PROG \
+       -c 'startupdate -e' \
+       -c "pwrite -S 0x60 0 $(( (blksz * nrblks) + 37373 ))" \
+       -c 'commitupdate -q' \
+       "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after lengthen commit" $dir/a
+echo
+
+# Now try the update but with the A file open separately so that we clobber
+# mtime and fail the update.
+mkfile
+filesnap "before fail commit" $dir/a
+
+$XFS_IO_PROG \
+       -c "open $dir/a" \
+       -c 'startupdate -e ' \
+       -c 'pwrite -S 0x58 44k 55k -b 1m' \
+       -c 'file 0' \
+       -c 'close' \
+       -c 'pwrite -S 0x61 22k 11k -b 1m' \
+       -c 'commitupdate -q' \
+       "$dir/a" 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after fail commit" $dir/a
+echo
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/719.out b/tests/generic/719.out
new file mode 100644 (file)
index 0000000..6f5da04
--- /dev/null
@@ -0,0 +1,40 @@
+QA output created by 719
+before commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-719/a
+wrote 4194304/4194304 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-719/a'.
+after commit
+0558063c531ca7c7864fc5a4923f7144  TEST_DIR/test-719/a
+
+before shorten commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-719/a
+wrote 56320/56320 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-719/a'.
+after shorten commit
+52353039d89c5f2b76b9003464e5276a  TEST_DIR/test-719/a
+
+before lengthen commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-719/a
+wrote 4231677/4231677 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-719/a'.
+after lengthen commit
+1839e7c6bf616160dc51b12179db2642  TEST_DIR/test-719/a
+
+before fail commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-719/a
+committing update: Device or resource busy
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[000] TEST_DIR/test-719/a (xfs,non-sync,non-direct,read-write)
+ 001  TEST_DIR/test-719/a (fileupdate) (xfs,non-sync,non-direct,read-write)
+[000] TEST_DIR/test-719/a (fileupdate) (xfs,non-sync,non-direct,read-write)
+wrote 11264/11264 bytes at offset 22528
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+after fail commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-719/a
+
diff --git a/tests/generic/720 b/tests/generic/720
new file mode 100755 (executable)
index 0000000..4db69c6
--- /dev/null
@@ -0,0 +1,58 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 720
+#
+# Stress testing with a lot of extents.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange'
+_require_test_program punch-alternating
+_require_test
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=$(_get_file_block_size $TEST_DIR)
+nrblks=$((LOAD_FACTOR * 100000))
+
+_require_fs_space $TEST_DIR $(( (2 * blksz * nrblks) / 1024 ))
+
+# Create some big swiss cheese files to test swapext with a lot of extents
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+$here/src/punch-alternating $dir/a
+_pwrite_byte 0x59 0 $((blksz * nrblks)) $dir/b >> $seqres.full
+$here/src/punch-alternating -o 1 $dir/b
+filefrag -v $dir/a $dir/b >> $seqres.full
+
+# Now try to swapext
+md5_a="$(md5sum < $dir/a)"
+md5_b="$(md5sum < $dir/b)"
+date >> $seqres.full
+$XFS_IO_PROG -c "swapext $dir/b" $dir/a
+date >> $seqres.full
+
+echo "md5_a=$md5_a" >> $seqres.full
+echo "md5_b=$md5_b" >> $seqres.full
+md5sum $dir/a $dir/b >> $seqres.full
+
+test "$(md5sum < $dir/b)" = "$md5_a" || echo "file b does not match former a"
+test "$(md5sum < $dir/a)" = "$md5_b" || echo "file a does not match former b"
+
+echo "Silence is golden!"
+# success, all done
+status=0
+exit
diff --git a/tests/generic/720.out b/tests/generic/720.out
new file mode 100644 (file)
index 0000000..09dbdbb
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 720
+Silence is golden!
diff --git a/tests/generic/721 b/tests/generic/721
new file mode 100755 (executable)
index 0000000..0beb089
--- /dev/null
@@ -0,0 +1,126 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 721
+#
+# Test non-root atomic file updates when (a) the file contents are cloned into
+# the staging file; and (b) when the staging file is created empty.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_xfs_io_command startupdate
+_require_test_reflink
+_require_test
+_require_user
+
+filesnap() {
+       echo "$1"
+       md5sum $2 | _filter_test_dir
+}
+
+mkfile() {
+       rm -f $dir/a
+       _pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+       chown $qa_user $dir/a $dir/
+       sync
+}
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+nrblks=64
+
+# Use the atomic file update staging prototype in xfs_io to update a file.
+mkfile
+filesnap "before commit" $dir/a
+
+cmd="$XFS_IO_PROG \
+       -c 'startupdate' \
+       -c 'pwrite -S 0x60 44k 55k -b 1m' \
+       -c 'commitupdate -q' \
+       \"$dir/a\""
+su -s /bin/bash -c "$cmd" $qa_user 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after commit" $dir/a
+echo
+
+# Use the atomic file updates to replace a file with a shorter file.
+mkfile
+filesnap "before shorten commit" $dir/a
+
+cmd="$XFS_IO_PROG \
+       -c 'startupdate' \
+       -c 'truncate 55k' \
+       -c 'pwrite -S 0x60 0 55k' \
+       -c 'commitupdate -q' \
+       \"$dir/a\""
+su -s /bin/bash -c "$cmd" $qa_user 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after shorten commit" $dir/a
+echo
+
+# Use the atomic file updates to replace a file with a longer file.
+mkfile
+filesnap "before lengthen commit" $dir/a
+
+cmd="$XFS_IO_PROG \
+       -c 'startupdate' \
+       -c \"pwrite -S 0x60 0 $(( (blksz * nrblks) + 37373 ))\" \
+       -c 'commitupdate -q' \
+       \"$dir/a\""
+su -s /bin/bash -c "$cmd" $qa_user 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after lengthen commit" $dir/a
+echo
+
+# Use the atomic file update staging prototype in xfs_io to cancel updating a
+# file.
+mkfile
+filesnap "before cancel" $dir/a
+
+cmd="$XFS_IO_PROG \
+       -c 'startupdate' \
+       -c 'pwrite -S 0x60 44k 55k -b 1m' \
+       -c 'cancelupdate' \
+       \"$dir/a\""
+su -s /bin/bash -c "$cmd" $qa_user 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after cancel" $dir/a
+echo
+
+# Now try the update but with the A file open separately so that we clobber
+# mtime and fail the update.
+mkfile
+filesnap "before fail commit" $dir/a
+
+cmd="$XFS_IO_PROG \
+       -c \"open $dir/a\" \
+       -c 'startupdate' \
+       -c 'pwrite -S 0x58 44k 55k -b 1m' \
+       -c 'file 0' \
+       -c 'close' \
+       -c 'pwrite -S 0x61 22k 11k -b 1m' \
+       -c 'commitupdate -q' \
+       \"$dir/a\""
+su -s /bin/bash -c "$cmd" $qa_user 2>&1 | _filter_xfs_io | _filter_test_dir
+
+filesnap "after fail commit" $dir/a
+echo
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/721.out b/tests/generic/721.out
new file mode 100644 (file)
index 0000000..d067212
--- /dev/null
@@ -0,0 +1,48 @@
+QA output created by 721
+before commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-721/a
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-721/a'.
+after commit
+bedbd22b58a680219a1225353f6195fa  TEST_DIR/test-721/a
+
+before shorten commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-721/a
+wrote 56320/56320 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-721/a'.
+after shorten commit
+52353039d89c5f2b76b9003464e5276a  TEST_DIR/test-721/a
+
+before lengthen commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-721/a
+wrote 4231677/4231677 bytes at offset 0
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Committed updates to 'TEST_DIR/test-721/a'.
+after lengthen commit
+1839e7c6bf616160dc51b12179db2642  TEST_DIR/test-721/a
+
+before cancel
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-721/a
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+Cancelled updates to 'TEST_DIR/test-721/a'.
+after cancel
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-721/a
+
+before fail commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-721/a
+committing update: Device or resource busy
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 56320/56320 bytes at offset 45056
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[000] TEST_DIR/test-721/a (xfs,non-sync,non-direct,read-write)
+ 001  TEST_DIR/test-721/a (fileupdate) (xfs,non-sync,non-direct,read-write)
+[000] TEST_DIR/test-721/a (fileupdate) (xfs,non-sync,non-direct,read-write)
+wrote 11264/11264 bytes at offset 22528
+XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+after fail commit
+d712f003e9d467e063cda1baf319b928  TEST_DIR/test-721/a
+
diff --git a/tests/generic/722 b/tests/generic/722
new file mode 100755 (executable)
index 0000000..40eab9b
--- /dev/null
@@ -0,0 +1,63 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 722
+#
+# Test swapext with the fsync flag flushes everything to disk before the call
+# returns.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_test_program "punch-alternating"
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_scratch
+_require_scratch_shutdown
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+_pwrite_byte 0x58 0 2m $SCRATCH_MNT/a >> $seqres.full
+_pwrite_byte 0x59 0 2m $SCRATCH_MNT/b >> $seqres.full
+$here/src/punch-alternating $SCRATCH_MNT/a
+$here/src/punch-alternating $SCRATCH_MNT/b
+
+old_a=$(md5sum $SCRATCH_MNT/a | awk '{print $1}')
+old_b=$(md5sum $SCRATCH_MNT/b | awk '{print $1}')
+echo "md5 a: $old_a md5 b: $old_b" >> $seqres.full
+
+od -tx1 -Ad -c $SCRATCH_MNT/a > /tmp/a0
+od -tx1 -Ad -c $SCRATCH_MNT/b > /tmp/b0
+
+echo swap >> $seqres.full
+$XFS_IO_PROG -c "swapext -v exchrange -a -e -f -u $SCRATCH_MNT/a" $SCRATCH_MNT/b
+_scratch_shutdown
+_scratch_cycle_mount
+
+new_a=$(md5sum $SCRATCH_MNT/a | awk '{print $1}')
+new_b=$(md5sum $SCRATCH_MNT/b | awk '{print $1}')
+echo "md5 a: $new_a md5 b: $new_b" >> $seqres.full
+
+test $old_a = $new_b || echo "scratch file B doesn't match old file A"
+test $old_b = $new_a || echo "scratch file A doesn't match old file B"
+
+od -tx1 -Ad -c $SCRATCH_MNT/a > /tmp/a1
+od -tx1 -Ad -c $SCRATCH_MNT/b > /tmp/b1
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/generic/722.out b/tests/generic/722.out
new file mode 100644 (file)
index 0000000..869bd20
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 722
+Silence is golden
diff --git a/tests/generic/723 b/tests/generic/723
new file mode 100755 (executable)
index 0000000..b452de0
--- /dev/null
@@ -0,0 +1,70 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 723
+#
+# Test swapext with the dry run flag doesn't change anything.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_test_program "punch-alternating"
+_require_xfs_io_command swapext '-v exchrange'
+_require_scratch
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+_pwrite_byte 0x58 0 1m $SCRATCH_MNT/a >> $seqres.full
+_pwrite_byte 0x59 0 2m $SCRATCH_MNT/b >> $seqres.full
+$XFS_IO_PROG -c 'truncate 2m' $SCRATCH_MNT/a
+$here/src/punch-alternating $SCRATCH_MNT/a
+$here/src/punch-alternating $SCRATCH_MNT/b
+
+old_a=$(md5sum $SCRATCH_MNT/a | awk '{print $1}')
+old_b=$(md5sum $SCRATCH_MNT/b | awk '{print $1}')
+echo "md5 a: $old_a md5 b: $old_b" >> $seqres.full
+
+# Test swapext with the -n option, which will do all the input parameter
+# checking and return 0 without changing anything.
+echo dry run swap >> $seqres.full
+$XFS_IO_PROG -c "swapext -v exchrange -n -f -u $SCRATCH_MNT/a" $SCRATCH_MNT/b
+_scratch_cycle_mount
+
+new_a=$(md5sum $SCRATCH_MNT/a | awk '{print $1}')
+new_b=$(md5sum $SCRATCH_MNT/b | awk '{print $1}')
+echo "md5 a: $new_a md5 b: $new_b" >> $seqres.full
+
+test $old_a = $new_a || echo "scratch file A should not have swapped"
+test $old_b = $new_b || echo "scratch file B should not have swapped"
+
+# Do it again, but without the -n option, to prove that we can actually
+# swap the file contents.
+echo actual swap >> $seqres.full
+$XFS_IO_PROG -c "swapext -v exchrange -f -u $SCRATCH_MNT/a" $SCRATCH_MNT/b
+_scratch_cycle_mount
+
+new_a=$(md5sum $SCRATCH_MNT/a | awk '{print $1}')
+new_b=$(md5sum $SCRATCH_MNT/b | awk '{print $1}')
+echo "md5 a: $new_a md5 b: $new_b" >> $seqres.full
+
+test $old_a = $new_b || echo "scratch file A should have swapped"
+test $old_b = $new_a || echo "scratch file B should have swapped"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/generic/723.out b/tests/generic/723.out
new file mode 100644 (file)
index 0000000..4036d5f
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 723
+Silence is golden
diff --git a/tests/generic/724 b/tests/generic/724
new file mode 100755 (executable)
index 0000000..12324fb
--- /dev/null
@@ -0,0 +1,53 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 724
+#
+# Test scatter-gather atomic file writes.  We create a temporary file, write
+# sparsely to it, then use XFS_EXCH_RANGE_FILE1_WRITTEN flag to swap
+# atomicallly only the ranges that we wrote.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_scratch
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 65536
+
+# Create original file
+_pwrite_byte 0x58 0 1m $SCRATCH_MNT/a >> $seqres.full
+
+# Create the donor file
+$XFS_IO_PROG -f -c 'truncate 1m' $SCRATCH_MNT/b
+_pwrite_byte 0x59 64k 64k $SCRATCH_MNT/b >> $seqres.full
+_pwrite_byte 0x57 768k 64k $SCRATCH_MNT/b >> $seqres.full
+
+md5sum $SCRATCH_MNT/a | _filter_scratch
+md5sum $SCRATCH_MNT/b | _filter_scratch
+
+# Test swapext.  -h means skip holes in /b, and -e means operate to EOF
+echo swap | tee -a $seqres.full
+$XFS_IO_PROG -c "swapext -v exchrange -f -u -h -e -a $SCRATCH_MNT/b" $SCRATCH_MNT/a
+_scratch_cycle_mount
+
+md5sum $SCRATCH_MNT/a | _filter_scratch
+md5sum $SCRATCH_MNT/b | _filter_scratch
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/724.out b/tests/generic/724.out
new file mode 100644 (file)
index 0000000..d182cc9
--- /dev/null
@@ -0,0 +1,6 @@
+QA output created by 724
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
+c9fb827e2e3e579dc2a733ddad486d1d  SCRATCH_MNT/b
+swap
+e9cbfe8489a68efaa5fcf40cf3106118  SCRATCH_MNT/a
+faf8ed02a5b0638096a817abcc6c2127  SCRATCH_MNT/b
diff --git a/tests/generic/725 b/tests/generic/725
new file mode 100755 (executable)
index 0000000..bf60127
--- /dev/null
@@ -0,0 +1,56 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 725
+#
+# Test scatter-gather atomic file commits.  Use the startupdate command to
+# create a temporary file, write sparsely to it, then commitupdate -h to
+# perform the scattered update.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_xfs_io_command startupdate '-e'
+_require_scratch
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 65536
+
+# Create original file
+_pwrite_byte 0x58 0 1m $SCRATCH_MNT/a >> $seqres.full
+sync
+md5sum $SCRATCH_MNT/a | _filter_scratch
+
+# Test atomic scatter-gather file commits.
+echo commit | tee -a $seqres.full
+$XFS_IO_PROG \
+       -c 'bmap -elpv' \
+       -c 'startupdate -e' \
+       -c 'truncate 1m' \
+       -c 'pwrite -S 0x59 64k 64k' \
+       -c 'pwrite -S 0x57 768k 64k' \
+       -c 'bmap -elpv' \
+       -c 'commitupdate -h -k' \
+       -c 'bmap -elpv' \
+       $SCRATCH_MNT/a >> $seqres.full
+_scratch_cycle_mount
+
+md5sum $SCRATCH_MNT/a | _filter_scratch
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/725.out b/tests/generic/725.out
new file mode 100644 (file)
index 0000000..2e2086e
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 725
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
+commit
+e9cbfe8489a68efaa5fcf40cf3106118  SCRATCH_MNT/a
diff --git a/tests/generic/726 b/tests/generic/726
new file mode 100755 (executable)
index 0000000..4cf18bd
--- /dev/null
@@ -0,0 +1,115 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 726
+#
+# Functional test for dropping suid and sgid bits as part of an atomic file
+# commit.
+#
+. ./common/preamble
+_begin_fstest auto fiexchange swapext quick
+
+# Override the default cleanup function.
+# _cleanup()
+# {
+#      cd /
+#      rm -r -f $tmp.*
+# }
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs generic
+_require_user
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_xfs_io_command startupdate
+_require_scratch
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 1048576
+chmod a+rw $SCRATCH_MNT/
+
+setup_testfile() {
+       rm -f $SCRATCH_MNT/a
+       _pwrite_byte 0x58 0 1m $SCRATCH_MNT/a >> $seqres.full
+       sync
+}
+
+commit_and_check() {
+       local user="$1"
+
+       md5sum $SCRATCH_MNT/a | _filter_scratch
+       stat -c '%a %A %n' $SCRATCH_MNT/a | _filter_scratch
+
+       local cmd="$XFS_IO_PROG -c 'startupdate' -c 'pwrite -S 0x57 0 1m' -c 'commitupdate' $SCRATCH_MNT/a"
+       if [ -n "$user" ]; then
+               su - "$user" -c "$cmd" >> $seqres.full
+       else
+               $SHELL -c "$cmd" >> $seqres.full
+       fi
+
+       _scratch_cycle_mount
+       md5sum $SCRATCH_MNT/a | _filter_scratch
+       stat -c '%a %A %n' $SCRATCH_MNT/a | _filter_scratch
+
+       # Blank line in output
+       echo
+}
+
+# Commit to a non-exec file by an unprivileged user clears suid but leaves
+# sgid.
+echo "Test 1 - qa_user, non-exec file"
+setup_testfile
+chmod a+rws $SCRATCH_MNT/a
+commit_and_check "$qa_user"
+
+# Commit to a group-exec file by an unprivileged user clears suid and sgid.
+echo "Test 2 - qa_user, group-exec file"
+setup_testfile
+chmod g+x,a+rws $SCRATCH_MNT/a
+commit_and_check "$qa_user"
+
+# Commit to a user-exec file by an unprivileged user clears suid but not sgid.
+echo "Test 3 - qa_user, user-exec file"
+setup_testfile
+chmod u+x,a+rws,g-x $SCRATCH_MNT/a
+commit_and_check "$qa_user"
+
+# Commit to a all-exec file by an unprivileged user clears suid and sgid.
+echo "Test 4 - qa_user, all-exec file"
+setup_testfile
+chmod a+rwxs $SCRATCH_MNT/a
+commit_and_check "$qa_user"
+
+# Commit to a non-exec file by root leaves suid and sgid.
+echo "Test 5 - root, non-exec file"
+setup_testfile
+chmod a+rws $SCRATCH_MNT/a
+commit_and_check
+
+# Commit to a group-exec file by root leaves suid and sgid.
+echo "Test 6 - root, group-exec file"
+setup_testfile
+chmod g+x,a+rws $SCRATCH_MNT/a
+commit_and_check
+
+# Commit to a user-exec file by root leaves suid and sgid.
+echo "Test 7 - root, user-exec file"
+setup_testfile
+chmod u+x,a+rws,g-x $SCRATCH_MNT/a
+commit_and_check
+
+# Commit to a all-exec file by root leaves suid and sgid.
+echo "Test 8 - root, all-exec file"
+setup_testfile
+chmod a+rwxs $SCRATCH_MNT/a
+commit_and_check
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/726.out b/tests/generic/726.out
new file mode 100644 (file)
index 0000000..d7aefa8
--- /dev/null
@@ -0,0 +1,49 @@
+QA output created by 726
+Test 1 - qa_user, non-exec file
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
+6666 -rwSrwSrw- SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
+666 -rw-rw-rw- SCRATCH_MNT/a
+
+Test 2 - qa_user, group-exec file
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
+6676 -rwSrwsrw- SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
+676 -rw-rwxrw- SCRATCH_MNT/a
+
+Test 3 - qa_user, user-exec file
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
+6766 -rwsrwSrw- SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
+766 -rwxrw-rw- SCRATCH_MNT/a
+
+Test 4 - qa_user, all-exec file
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
+6777 -rwsrwsrwx SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
+777 -rwxrwxrwx SCRATCH_MNT/a
+
+Test 5 - root, non-exec file
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
+6666 -rwSrwSrw- SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
+6666 -rwSrwSrw- SCRATCH_MNT/a
+
+Test 6 - root, group-exec file
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
+6676 -rwSrwsrw- SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
+6676 -rwSrwsrw- SCRATCH_MNT/a
+
+Test 7 - root, user-exec file
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
+6766 -rwsrwSrw- SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
+6766 -rwsrwSrw- SCRATCH_MNT/a
+
+Test 8 - root, all-exec file
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
+6777 -rwsrwsrwx SCRATCH_MNT/a
+3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
+6777 -rwsrwsrwx SCRATCH_MNT/a
+
diff --git a/tests/generic/727 b/tests/generic/727
new file mode 100755 (executable)
index 0000000..af9612c
--- /dev/null
@@ -0,0 +1,85 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 727
+#
+# Functional test for dropping capability bits as part of an atomic file
+# commit.
+#
+. ./common/preamble
+_begin_fstest auto fiexchange swapext quick
+
+# Override the default cleanup function.
+# _cleanup()
+# {
+#      cd /
+#      rm -r -f $tmp.*
+# }
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs generic
+_require_user
+_require_command "$GETCAP_PROG" getcap
+_require_command "$SETCAP_PROG" setcap
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_xfs_io_command startupdate
+_require_scratch
+_require_attrs security
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 1048576
+chmod a+rw $SCRATCH_MNT/
+
+setup_testfile() {
+       rm -f $SCRATCH_MNT/a $SCRATCH_MNT/b
+       _pwrite_byte 0x58 0 1m $SCRATCH_MNT/a >> $seqres.full
+       _pwrite_byte 0x57 0 1m $SCRATCH_MNT/b >> $seqres.full
+       chmod a+rw $SCRATCH_MNT/a $SCRATCH_MNT/b
+       $SETCAP_PROG cap_setgid,cap_setuid+ep $SCRATCH_MNT/a
+       sync
+}
+
+commit_and_check() {
+       local user="$1"
+
+       md5sum $SCRATCH_MNT/a | _filter_scratch
+       stat -c '%a %A %n' $SCRATCH_MNT/a | _filter_scratch
+       _getcap -v $SCRATCH_MNT/a | _filter_scratch
+
+       local cmd="$XFS_IO_PROG -c 'startupdate' -c 'pwrite -S 0x57 0 1m' -c 'commitupdate' $SCRATCH_MNT/a"
+       if [ -n "$user" ]; then
+               su - "$user" -c "$cmd" >> $seqres.full
+       else
+               $SHELL -c "$cmd" >> $seqres.full
+       fi
+
+       _scratch_cycle_mount
+       md5sum $SCRATCH_MNT/a | _filter_scratch
+       stat -c '%a %A %n' $SCRATCH_MNT/a | _filter_scratch
+       _getcap -v $SCRATCH_MNT/a | _filter_scratch
+
+       # Blank line in output
+       echo
+}
+
+# Commit by an unprivileged user clears capability bits.
+echo "Test 1 - qa_user"
+setup_testfile
+commit_and_check "$qa_user"
+
+# Commit by root leaves capability bits.
+echo "Test 2 - root"
+setup_testfile
+commit_and_check
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/727.out b/tests/generic/727.out
new file mode 100755 (executable)
index 0000000..2c27bfd
--- /dev/null
@@ -0,0 +1,17 @@
+QA output created by 727
+Test 1 - qa_user
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
+666 -rw-rw-rw- SCRATCH_MNT/a
+SCRATCH_MNT/a cap_setgid,cap_setuid=ep
+3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
+666 -rw-rw-rw- SCRATCH_MNT/a
+SCRATCH_MNT/a
+
+Test 2 - root
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
+666 -rw-rw-rw- SCRATCH_MNT/a
+SCRATCH_MNT/a cap_setgid,cap_setuid=ep
+3784de23efab7a2074c9ec66901e39e5  SCRATCH_MNT/a
+666 -rw-rw-rw- SCRATCH_MNT/a
+SCRATCH_MNT/a
+
diff --git a/tests/generic/728 b/tests/generic/728
new file mode 100755 (executable)
index 0000000..fe3486b
--- /dev/null
@@ -0,0 +1,43 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Netapp Inc., All Rights Reserved.
+#
+# FS QA Test 728
+#
+# Test a bug where the NFS client wasn't sending a post-op GETATTR to the
+# server after setting an xattr, resulting in `stat` reporting a stale ctime.
+#
+. ./common/preamble
+_begin_fstest auto quick attr
+
+# Import common functions
+. ./common/attr
+
+# real QA test starts here
+_supported_fs generic
+_require_test
+_require_attrs
+
+rm -rf $TEST_DIR/testfile
+touch $TEST_DIR/testfile
+
+check_xattr_op()
+{
+       what=$1
+       shift 1
+
+       before_ctime=$(stat -c %z $TEST_DIR/testfile)
+       # maximum known ctime granularity is 2s (e.g. FAT)
+       sleep 2
+       $SETFATTR_PROG $* $TEST_DIR/testfile
+       after_ctime=$(stat -c %z $TEST_DIR/testfile)
+
+       test "$before_ctime" != "$after_ctime" || echo "Expected ctime to change after $what."
+}
+
+check_xattr_op setxattr -n user.foobar -v 123
+check_xattr_op removexattr -x user.foobar
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/generic/728.out b/tests/generic/728.out
new file mode 100644 (file)
index 0000000..ab39f45
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 728
+Silence is golden
diff --git a/tests/generic/729 b/tests/generic/729
new file mode 100755 (executable)
index 0000000..66242ed
--- /dev/null
@@ -0,0 +1,42 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Red Hat, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 729
+#
+# Trigger page faults in the same file during read and write
+#
+# This is generic/647 with an additional test that writes a memory-mapped page
+# onto itself using direct I/O.
+#
+# The kernel will invalidate the page cache before carrying out the write, so
+# filesystems that fault in the page and then carry out the direct I/O write
+# with page faults disabled will never make any progress.
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -f $tmp.*
+       rm -f $TEST_DIR/mmap-rw-fault.tmp
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+
+_supported_fs generic
+_require_test
+_require_odirect
+_require_test_program mmap-rw-fault
+
+echo "Silence is golden"
+
+$here/src/mmap-rw-fault -2 $TEST_DIR/mmap-rw-fault.tmp
+
+status=$?
+exit
diff --git a/tests/generic/729.out b/tests/generic/729.out
new file mode 100644 (file)
index 0000000..0f175ae
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 729
+Silence is golden
diff --git a/tests/generic/730 b/tests/generic/730
new file mode 100755 (executable)
index 0000000..988c47e
--- /dev/null
@@ -0,0 +1,58 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Red Hat Inc. All Rights Reserved.
+# Copyright (c) 2023 Christoph Hellwig
+#
+# Test proper file system shut down when the block device is removed underneath
+# and there is dirty data.
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+_cleanup()
+{
+       cd /
+       $UMOUNT_PROG $SCSI_DEBUG_MNT >>$seqres.full 2>&1
+       _put_scsi_debug_dev
+       rm -f $tmp.*
+}
+
+. ./common/filter
+. ./common/scsi_debug
+
+_supported_fs generic
+
+# We don't actually use the test device, but we need a block based fs
+_require_test
+_require_block_device $TEST_DEV
+_require_scsi_debug
+
+size=$(_small_fs_size_mb 256)
+SCSI_DEBUG_DEV=`_get_scsi_debug_dev 512 512 0 $size`
+test -b "$SCSI_DEBUG_DEV" || _notrun "Failed to initialize scsi debug device"
+echo "SCSI debug device $SCSI_DEBUG_DEV" >>$seqres.full
+
+run_check _mkfs_dev $SCSI_DEBUG_DEV
+
+SCSI_DEBUG_MNT="$TEST_DIR/scsi_debug_$seq"
+rm -rf $SCSI_DEBUG_MNT
+mkdir $SCSI_DEBUG_MNT
+run_check _mount $SCSI_DEBUG_DEV $SCSI_DEBUG_MNT
+
+# create a test file
+$XFS_IO_PROG -f -c "pwrite 0 1M" $SCSI_DEBUG_MNT/testfile >>$seqres.full
+
+# open a file descriptor for reading the file
+exec 3< $SCSI_DEBUG_MNT/testfile
+
+# delete the scsi debug device while it still has dirty data
+echo 1 > /sys/block/$(_short_dev $SCSI_DEBUG_DEV)/device/delete
+
+# try to read from the file, which should give us -EIO
+cat <&3 > /dev/null
+
+# close the file descriptor to not block unmount
+exec 3<&-
+
+status=0
+exit
diff --git a/tests/generic/730.out b/tests/generic/730.out
new file mode 100644 (file)
index 0000000..79e96db
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 730
+cat: -: Input/output error
diff --git a/tests/generic/731 b/tests/generic/731
new file mode 100755 (executable)
index 0000000..b279e3f
--- /dev/null
@@ -0,0 +1,59 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Red Hat Inc. All Rights Reserved.
+# Copyright (c) 2023 Christoph Hellwig
+#
+# Test proper file system shut down when the block device is removed underneath
+# and it has no dirty data.
+#
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+_cleanup()
+{
+       cd /
+       $UMOUNT_PROG $SCSI_DEBUG_MNT >>$seqres.full 2>&1
+       _put_scsi_debug_dev
+       rm -f $tmp.*
+}
+
+. ./common/filter
+. ./common/scsi_debug
+
+# We don't actually use the test device, but we need a block based fs
+_require_test
+_require_block_device $TEST_DEV
+_supported_fs generic
+_require_scsi_debug
+
+size=$(_small_fs_size_mb 256)
+SCSI_DEBUG_DEV=`_get_scsi_debug_dev 512 512 0 $size`
+test -b "$SCSI_DEBUG_DEV" || _notrun "Failed to initialize scsi debug device"
+echo "SCSI debug device $SCSI_DEBUG_DEV" >>$seqres.full
+
+run_check _mkfs_dev $SCSI_DEBUG_DEV
+
+SCSI_DEBUG_MNT="$TEST_DIR/scsi_debug_$seq"
+rm -rf $SCSI_DEBUG_MNT
+mkdir $SCSI_DEBUG_MNT
+run_check _mount $SCSI_DEBUG_DEV $SCSI_DEBUG_MNT
+
+# create a test file
+$XFS_IO_PROG -f -c "pwrite 0 1M" -c "fsync" $SCSI_DEBUG_MNT/testfile >>$seqres.full
+
+# open a file descriptor for reading the file
+exec 3< $SCSI_DEBUG_MNT/testfile
+
+# drop all caches and delete the scsi debug device
+echo 3 > /proc/sys/vm/drop_caches
+echo 1 > /sys/block/`_short_dev $SCSI_DEBUG_DEV`/device/delete
+
+# try to read from the file, which should give us -EIO
+cat <&3 > /dev/null
+
+# close the file descriptor to not block unmount
+exec 3<&-
+
+status=0
+exit
diff --git a/tests/generic/731.out b/tests/generic/731.out
new file mode 100644 (file)
index 0000000..102c052
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 731
+cat: -: Input/output error
diff --git a/tests/generic/732 b/tests/generic/732
new file mode 100755 (executable)
index 0000000..5b5087d
--- /dev/null
@@ -0,0 +1,56 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Red Hat, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 732
+#
+# Mount the same export to different mount points and move (rename)
+# files among those mount points.
+# This simple test recently unveils an ancient nfsd bug that is fixed
+# by fdd2630a739819 ("nfsd: fix change_info in NFSv4 RENAME replies").
+#
+. ./common/preamble
+_begin_fstest auto quick rename
+
+# Override the default cleanup function.
+_cleanup()
+{
+       $UMOUNT_PROG $testdir1 2>/dev/null
+       $UMOUNT_PROG $testdir2 2>/dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+
+# real QA test starts here
+_supported_fs ^nfs
+
+_require_test
+_require_scratch
+
+echo "Silence is golden"
+
+_scratch_mkfs >> $seqres.full
+testdir1=$TEST_DIR/mountpoint1-$seq
+testdir2=$TEST_DIR/mountpoint2-$seq
+rm -rf $testdir1 $testdir2
+mkdir -p $testdir1 $testdir2
+
+# Don't share the data and attribute caches among mount points for NFS.
+# This caching behavior is necessary to reproduce this issue as we're
+# checking the alignment of each mount point's own unique cache.
+[ "$FSTYP" = "nfs" ] && MOUNT_OPTIONS="-o nosharecache"
+
+SCRATCH_MNT=$testdir1 _scratch_mount
+SCRATCH_MNT=$testdir2 _scratch_mount
+rm -rf $testdir1/{A,B}
+mkdir $testdir1/{A,B}
+touch $testdir1/A/f
+mv $testdir1/A/f $testdir1/B/
+cat $testdir2/B/f
+mv $testdir2/B/f $testdir2/A/
+cat $testdir1/A/f
+mv $testdir1/A/f $testdir1/B/
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/732.out b/tests/generic/732.out
new file mode 100644 (file)
index 0000000..451f82c
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 732
+Silence is golden
diff --git a/tests/generic/733 b/tests/generic/733
new file mode 100755 (executable)
index 0000000..d88d92a
--- /dev/null
@@ -0,0 +1,79 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023, Oracle and/or its affiliates.  All Rights Reserved.
+#
+# FS QA Test No. 733
+#
+# Race file reads with a very slow reflink operation to see if the reads
+# actually complete while the reflink is ongoing.  This is a functionality
+# test for XFS commit 14a537983b22 "xfs: allow read IO and FICLONE to run
+# concurrently".
+#
+. ./common/preamble
+_begin_fstest auto clone punch
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+. ./common/reflink
+
+# real QA test starts here
+_supported_fs generic
+_require_scratch_reflink
+_require_cp_reflink
+_require_xfs_io_command "fpunch"
+_require_test_program "punch-alternating"
+_require_test_program "t_reflink_read_race"
+_require_command "$TIMEOUT_PROG" timeout
+
+[ "$FSTYP" = "xfs" ] && _fixed_by_kernel_commit 14a537983b22 \
+        "xfs: allow read IO and FICLONE to run concurrently"
+
+rm -f "$seqres.full"
+
+echo "Format and mount"
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount >> "$seqres.full" 2>&1
+
+testdir="$SCRATCH_MNT/test-$seq"
+mkdir "$testdir"
+
+calc_space() {
+       blocks_needed=$(( 2 ** (fnr + 1) ))
+       space_needed=$((blocks_needed * blksz * 5 / 4))
+}
+
+# Figure out the number of blocks that we need to get the reflink runtime above
+# 1 seconds
+echo "Create a many-block file"
+for ((fnr = 1; fnr < 40; fnr++)); do
+       free_blocks=$(stat -f -c '%a' "$testdir")
+       blksz=$(_get_file_block_size "$testdir")
+       space_avail=$((free_blocks * blksz))
+       calc_space
+       test $space_needed -gt $space_avail && \
+               _notrun "Insufficient space for stress test; would only create $blocks_needed extents."
+
+       off=$(( (2 ** fnr) * blksz))
+       $XFS_IO_PROG -f -c "pwrite -S 0x61 -b 4194304 $off $off" "$testdir/file1" >> "$seqres.full"
+       "$here/src/punch-alternating" "$testdir/file1" >> "$seqres.full"
+
+       $TIMEOUT_PROG 1s cp --reflink=always "$testdir/file1" "$testdir/garbage" || break
+done
+echo "fnr=$fnr" >> $seqres.full
+
+echo "Reflink the big file"
+$here/src/t_reflink_read_race "$testdir/file1" "$testdir/file2" \
+       "$testdir/outcome" &>> $seqres.full
+
+if [ ! -e "$testdir/outcome" ]; then
+       echo "Could not set up program"
+elif grep -q "finished read early" "$testdir/outcome"; then
+       echo "test completed successfully"
+else
+       cat "$testdir/outcome"
+fi
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/733.out b/tests/generic/733.out
new file mode 100644 (file)
index 0000000..d4f5a7c
--- /dev/null
@@ -0,0 +1,6 @@
+QA output created by 733
+Format and mount
+Create a many-block file
+Reflink the big file
+Terminated
+test completed successfully
diff --git a/tests/generic/734 b/tests/generic/734
new file mode 100755 (executable)
index 0000000..93c2ad9
--- /dev/null
@@ -0,0 +1,88 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 734
+#
+# This is a regression test for the kernel commit noted below.  The stale
+# memory exposure can be exploited by creating a file with shared blocks,
+# evicting the page cache for that file, and then funshareing at least one
+# memory page's worth of data.  iomap will mark the page uptodate and dirty
+# without ever reading the ondisk contents.
+#
+. ./common/preamble
+_begin_fstest auto quick unshare clone
+
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $testdir
+}
+
+# real QA test starts here
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+_fixed_by_git_commit kernel 35d30c9cf127 \
+       "iomap: don't skip reading in !uptodate folios when unsharing a range"
+
+# real QA test starts here
+_supported_fs generic
+_require_test_reflink
+_require_cp_reflink
+_require_xfs_io_command "funshare"
+
+testdir=$TEST_DIR/test-$seq
+rm -rf $testdir
+mkdir $testdir
+
+# Create a file that is at least four pages in size and aligned to the
+# file allocation unit size so that we don't trigger any unnecessary zeroing.
+pagesz=$(_get_page_size)
+alloc_unit=$(_get_file_block_size $TEST_DIR)
+filesz=$(( ( (4 * pagesz) + alloc_unit - 1) / alloc_unit * alloc_unit))
+
+echo "Create the original file and a clone"
+_pwrite_byte 0x61 0 $filesz $testdir/file2.chk >> $seqres.full
+_pwrite_byte 0x61 0 $filesz $testdir/file1 >> $seqres.full
+_cp_reflink $testdir/file1 $testdir/file2
+_cp_reflink $testdir/file1 $testdir/file3
+
+_test_cycle_mount
+
+cat $testdir/file3 > /dev/null
+
+echo "Funshare at least one pagecache page"
+$XFS_IO_PROG -c "funshare 0 $filesz" $testdir/file2
+$XFS_IO_PROG -c "funshare 0 $filesz" $testdir/file3
+_pwrite_byte 0x61 0 $filesz $testdir/file2.chk >> $seqres.full
+
+echo "Check contents"
+
+# file2 wasn't cached when it was unshared, but it should match
+if ! cmp -s $testdir/file2.chk $testdir/file2; then
+       echo "file2.chk does not match file2"
+
+       echo "file2.chk contents" >> $seqres.full
+       od -tx1 -Ad -c $testdir/file2.chk >> $seqres.full
+       echo "file2 contents" >> $seqres.full
+       od -tx1 -Ad -c $testdir/file2 >> $seqres.full
+       echo "end bad contents" >> $seqres.full
+fi
+
+# file3 was cached when it was unshared, and it should match
+if ! cmp -s $testdir/file2.chk $testdir/file3; then
+       echo "file2.chk does not match file3"
+
+       echo "file2.chk contents" >> $seqres.full
+       od -tx1 -Ad -c $testdir/file2.chk >> $seqres.full
+       echo "file3 contents" >> $seqres.full
+       od -tx1 -Ad -c $testdir/file3 >> $seqres.full
+       echo "end bad contents" >> $seqres.full
+fi
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/734.out b/tests/generic/734.out
new file mode 100644 (file)
index 0000000..174c3f8
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 734
+Create the original file and a clone
+Funshare at least one pagecache page
+Check contents
diff --git a/tests/generic/735 b/tests/generic/735
new file mode 100755 (executable)
index 0000000..0ba111a
--- /dev/null
@@ -0,0 +1,58 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 HUAWEI.  All Rights Reserved.
+#
+# FS QA Test No. 735
+#
+# Append writes to a file with logical block numbers close to 0xffffffff
+# and observe if a kernel crash is caused by ext4_lblk_t overflow triggering
+# BUG_ON at ext4_mb_new_inode_pa(). This is a regression test for
+# commit bc056e7163ac ("ext4: fix BUG in ext4_mb_new_inode_pa() due to overflow")
+# commit 2dcf5fde6dff ("ext4: prevent the normalized size from exceeding EXT_MAX_BLOCKS")
+
+. ./common/preamble
+. ./common/populate
+_begin_fstest auto quick insert prealloc
+
+# real QA test starts here
+if [[ "$FSTYP" =~ ext[0-9]+ ]]; then
+       _fixed_by_kernel_commit bc056e7163ac "ext4: fix BUG in ext4_mb_new_inode_pa() due to overflow"
+       _fixed_by_kernel_commit 2dcf5fde6dff "ext4: prevent the normalized size from exceeding EXT_MAX_BLOCKS"
+fi
+
+_require_odirect
+_require_xfs_io_command "falloc"
+_require_xfs_io_command "finsert"
+
+dev_size=$((80 * 1024 * 1024))
+_scratch_mkfs_sized $dev_size >>$seqres.full 2>&1 || _fail "mkfs failed"
+
+_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 1048576     # finsert at 1M
+file_blksz="$(_get_file_block_size ${SCRATCH_MNT})"
+
+# Reserve 1M space
+$XFS_IO_PROG -f -c "falloc 0 1M" "${SCRATCH_MNT}/tmp" >> $seqres.full
+
+# Create a file with logical block numbers close to 0xffffffff
+$XFS_IO_PROG -f -c "falloc 0 10M" "${SCRATCH_MNT}/file" >> $seqres.full
+max_pos=$(( 0xffffffff * file_blksz ))
+finsert_len=$(( max_pos - ((10 + 2) << 20) ))
+$XFS_IO_PROG -f -c "finsert 1M ${finsert_len}" "${SCRATCH_MNT}/file" >> $seqres.full
+
+# Filling up the free space ensures that the pre-allocated space is the reserved space.
+nr_free=$(stat -f -c '%f' ${SCRATCH_MNT})
+_fill_fs $((nr_free * file_blksz)) ${SCRATCH_MNT}/fill $file_blksz 0 >> $seqres.full 2>&1
+sync
+
+# Remove reserved space to gain free space for allocation
+rm -f ${SCRATCH_MNT}/tmp
+
+# Trying to allocate two blocks triggers BUG_ON.
+$XFS_IO_PROG -c "open -ad ${SCRATCH_MNT}/file" -c "pwrite -S 0xff 0 $((2 * file_blksz))" >> $seqres.full
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/735.out b/tests/generic/735.out
new file mode 100644 (file)
index 0000000..92df092
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 735
+Silence is golden
diff --git a/tests/generic/736 b/tests/generic/736
new file mode 100755 (executable)
index 0000000..d2432a8
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 SUSE Linux Products GmbH. All Rights Reserved.
+#
+# FS QA Test No. 736
+#
+# Test that on a fairly large directory if we keep renaming files while holding
+# the directory open and doing readdir(3) calls, we don't end up in an infinite
+# loop.
+#
+. ./common/preamble
+_begin_fstest auto quick dir rename
+
+_cleanup()
+{
+       cd /
+       rm -fr $tmp.*
+       rm -fr $target_dir
+}
+
+_supported_fs generic
+_require_test
+_require_test_program readdir-while-renames
+
+[ $FSTYP = "btrfs" ] && _fixed_by_kernel_commit 9b378f6ad48c \
+       "btrfs: fix infinite directory reads"
+
+target_dir="$TEST_DIR/test-$seq"
+rm -fr $target_dir
+mkdir $target_dir
+
+$here/src/readdir-while-renames $target_dir
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/generic/736.out b/tests/generic/736.out
new file mode 100644 (file)
index 0000000..8588bd4
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 736
+Silence is golden
diff --git a/tests/generic/737 b/tests/generic/737
new file mode 100755 (executable)
index 0000000..4563b1b
--- /dev/null
@@ -0,0 +1,54 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 IBM Corporation.  All Rights Reserved.
+#
+# FS QA Test No. 737
+#
+# Integrity test for O_SYNC with buff-io, dio, aio-dio with sudden shutdown.
+# Based on a testcase reported by Gao Xiang <hsiangkao@linux.alibaba.com>
+#
+
+. ./common/preamble
+_begin_fstest auto quick shutdown aio
+
+# real QA test starts here
+_supported_fs generic
+_require_scratch
+_require_scratch_shutdown
+_require_aiodio aio-dio-write-verify
+
+[[ "$FSTYP" =~ ext[0-9]+ ]] && _fixed_by_kernel_commit 91562895f803 \
+       "ext4: properly sync file size update after O_SYNC direct IO"
+
+_scratch_mkfs > $seqres.full 2>&1
+_scratch_mount
+
+echo "T-1: Create a 1M file using buff-io & O_SYNC"
+$XFS_IO_PROG -fs -c "pwrite -S 0x5a 0 1M" $SCRATCH_MNT/testfile.t1 > /dev/null 2>&1
+echo "T-1: Shutdown the fs suddenly"
+_scratch_shutdown
+echo "T-1: Cycle mount"
+_scratch_cycle_mount
+echo "T-1: File contents after cycle mount"
+_hexdump $SCRATCH_MNT/testfile.t1
+
+echo "T-2: Create a 1M file using O_DIRECT & O_SYNC"
+$XFS_IO_PROG -fsd -c "pwrite -S 0x5a 0 1M" $SCRATCH_MNT/testfile.t2 > /dev/null 2>&1
+echo "T-2: Shutdown the fs suddenly"
+_scratch_shutdown
+echo "T-2: Cycle mount"
+_scratch_cycle_mount
+echo "T-2: File contents after cycle mount"
+_hexdump $SCRATCH_MNT/testfile.t2
+
+echo "T-3: Create a 1M file using AIO-DIO & O_SYNC"
+$AIO_TEST -a size=1048576 -S -N $SCRATCH_MNT/testfile.t3 > /dev/null 2>&1
+echo "T-3: Shutdown the fs suddenly"
+_scratch_shutdown
+echo "T-3: Cycle mount"
+_scratch_cycle_mount
+echo "T-3: File contents after cycle mount"
+_hexdump $SCRATCH_MNT/testfile.t3
+
+status=0
+exit
diff --git a/tests/generic/737.out b/tests/generic/737.out
new file mode 100644 (file)
index 0000000..efe6ff1
--- /dev/null
@@ -0,0 +1,22 @@
+QA output created by 737
+T-1: Create a 1M file using buff-io & O_SYNC
+T-1: Shutdown the fs suddenly
+T-1: Cycle mount
+T-1: File contents after cycle mount
+000000 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a  >ZZZZZZZZZZZZZZZZ<
+*
+100000
+T-2: Create a 1M file using O_DIRECT & O_SYNC
+T-2: Shutdown the fs suddenly
+T-2: Cycle mount
+T-2: File contents after cycle mount
+000000 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a  >ZZZZZZZZZZZZZZZZ<
+*
+100000
+T-3: Create a 1M file using AIO-DIO & O_SYNC
+T-3: Shutdown the fs suddenly
+T-3: Cycle mount
+T-3: File contents after cycle mount
+000000 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a 5a  >ZZZZZZZZZZZZZZZZ<
+*
+100000
diff --git a/tests/generic/738 b/tests/generic/738
new file mode 100755 (executable)
index 0000000..2b37b1e
--- /dev/null
@@ -0,0 +1,52 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# FS QA Test 738
+#
+# Test possible deadlock of umount and reclaim memory
+# when there are EOF blocks in files.
+#
+. ./common/preamble
+_begin_fstest auto quick freeze
+
+_cleanup()
+{
+       xfs_freeze -u $SCRATCH_MNT 2>/dev/null
+       wait
+       cd /
+       rm -r -f $tmp.*
+}
+
+_supported_fs generic
+_require_scratch
+_require_freeze
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+create_eof_block_file()
+{
+       # Create several EOF blocks in the new file
+       for j in $(seq 1 5); do
+               cat $SCRATCH_MNT/testfile >> $1
+       done
+}
+
+$XFS_IO_PROG -fc "pwrite 0 64k" $SCRATCH_MNT/testfile >> $seqres.full
+# Create enough files to make sure there is enough cache
+for i in $(seq 0 1024); do
+       create_eof_block_file $SCRATCH_MNT/$i
+done
+sync
+xfs_freeze -f $SCRATCH_MNT
+
+# This will hang if bug reproduces
+echo 3 > /proc/sys/vm/drop_caches &
+
+# Wait a while before exiting and unfreezing.
+sleep 3
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/generic/738.out b/tests/generic/738.out
new file mode 100644 (file)
index 0000000..5719515
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 738
+Silence is golden
diff --git a/tests/generic/739 b/tests/generic/739
new file mode 100755 (executable)
index 0000000..0941dd3
--- /dev/null
@@ -0,0 +1,31 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2023 Google LLC
+#
+# FS QA Test No. 739
+#
+# Verify the on-disk format of encrypted files that use a crypto data unit size
+# that differs from the filesystem block size.  This tests the functionality
+# that was introduced in Linux 6.7 by kernel commit 5b1188847180
+# ("fscrypt: support crypto data unit size less than filesystem block size").
+#
+. ./common/preamble
+_begin_fstest auto quick encrypt
+
+. ./common/filter
+. ./common/encrypt
+
+_supported_fs generic
+_wants_kernel_commit 5b1188847180 \
+       "fscrypt: support crypto data unit size less than filesystem block size"
+
+# For now, just test 512-byte and 1024-byte data units.  Filesystems accept
+# power-of-2 sizes between 512 and the filesystem block size, inclusively.
+# Testing 512 and 1024 ensures this test will run for any FS block size >= 1024
+# (provided that the filesystem supports sub-block data units at all).
+_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-CTS-CBC v2 log2_dusize=9
+_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-CTS-CBC v2 log2_dusize=10
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/739.out b/tests/generic/739.out
new file mode 100644 (file)
index 0000000..0066605
--- /dev/null
@@ -0,0 +1,11 @@
+QA output created by 739
+
+Verifying ciphertext with parameters:
+       contents_encryption_mode: AES-256-XTS
+       filenames_encryption_mode: AES-256-CTS-CBC
+       options: v2 log2_dusize=9
+
+Verifying ciphertext with parameters:
+       contents_encryption_mode: AES-256-XTS
+       filenames_encryption_mode: AES-256-CTS-CBC
+       options: v2 log2_dusize=10
diff --git a/tests/generic/741 b/tests/generic/741
new file mode 100755 (executable)
index 0000000..f8f9a7b
--- /dev/null
@@ -0,0 +1,60 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle.  All Rights Reserved.
+#
+# FS QA Test 741
+#
+# Attempt to mount both the DM physical device and the DM flakey device.
+# Verify the returned error message.
+#
+. ./common/preamble
+_begin_fstest auto quick volume tempfsid
+
+# Override the default cleanup function.
+_cleanup()
+{
+       umount $extra_mnt &> /dev/null
+       rm -rf $extra_mnt
+       _unmount_flakey
+       _cleanup_flakey
+       cd /
+       rm -r -f $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/dmflakey
+
+# real QA test starts here
+_supported_fs generic
+_require_test
+_require_scratch
+_require_dm_target flakey
+
+[ "$FSTYP" = "btrfs" ] && _fixed_by_kernel_commit XXXXXXXXXXXX \
+                       "btrfs: return accurate error code on open failure"
+
+_scratch_mkfs >> $seqres.full
+_init_flakey
+_mount_flakey
+
+extra_mnt=$TEST_DIR/extra_mnt
+rm -rf $extra_mnt
+mkdir -p $extra_mnt
+
+# Mount must fail because the physical device has a dm created on it.
+# Filters alter the return code of the mount.
+_mount $SCRATCH_DEV $extra_mnt 2>&1 | \
+                       _filter_testdir_and_scratch | _filter_error_mount
+
+# Try again with flakey unmounted, must fail.
+_unmount_flakey
+_mount $SCRATCH_DEV $extra_mnt 2>&1 | \
+                       _filter_testdir_and_scratch | _filter_error_mount
+
+# Removing dm should make mount successful.
+_cleanup_flakey
+_scratch_mount
+
+status=0
+exit
diff --git a/tests/generic/741.out b/tests/generic/741.out
new file mode 100644 (file)
index 0000000..b694f5f
--- /dev/null
@@ -0,0 +1,3 @@
+QA output created by 741
+mount: TEST_DIR/extra_mnt: SCRATCH_DEV already mounted or mount point busy
+mount: TEST_DIR/extra_mnt: SCRATCH_DEV already mounted or mount point busy
diff --git a/tests/generic/742 b/tests/generic/742
new file mode 100755 (executable)
index 0000000..43ebdbc
--- /dev/null
@@ -0,0 +1,50 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Meta Platforms, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 742
+#
+# Test fiemap into an mmaped buffer of the same file
+#
+# Create a reasonably large file, then run a program which mmaps it and uses
+# that as a buffer for an fiemap call.  This is a regression test for btrfs
+# where we used to hold a lock for the duration of the fiemap call which would
+# result in a deadlock if we page faulted.
+#
+. ./common/preamble
+_begin_fstest quick auto fiemap
+[ $FSTYP == "btrfs" ] && \
+       _fixed_by_kernel_commit b0ad381fa769 \
+               "btrfs: fix deadlock with fiemap and extent locking"
+
+_cleanup()
+{
+       rm -f $dst
+       cd /
+       rm -r -f $tmp.*
+}
+
+# real QA test starts here
+_supported_fs generic
+_require_test
+_require_test_program "fiemap-fault"
+_require_test_program "punch-alternating"
+_require_xfs_io_command "fpunch"
+
+dst=$TEST_DIR/$seq/fiemap-fault
+
+mkdir -p $TEST_DIR/$seq
+
+echo "Silence is golden"
+
+# Generate a file with lots of extents
+blksz=$(_get_file_block_size $TEST_DIR)
+$XFS_IO_PROG -f -c "pwrite -q 0 $((blksz * 10000))" $dst
+$here/src/punch-alternating $dst
+
+# Now run the reproducer
+$here/src/fiemap-fault $dst
+
+# success, all done
+status=$?
+exit
diff --git a/tests/generic/742.out b/tests/generic/742.out
new file mode 100644 (file)
index 0000000..ef4f23c
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 742
+Silence is golden
diff --git a/tests/generic/743 b/tests/generic/743
new file mode 100755 (executable)
index 0000000..ad37d32
--- /dev/null
@@ -0,0 +1,65 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 743
+#
+# This is a regression test for a kernel hang that I saw when creating a memory
+# mapping, injecting EIO errors on the block device, and invoking
+# MADV_POPULATE_READ on the mapping to fault in the pages.
+#
+. ./common/preamble
+_begin_fstest auto rw eio
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -f $tmp.*
+       _dmerror_unmount
+       _dmerror_cleanup
+}
+
+# Import common functions.
+. ./common/dmerror
+
+_fixed_by_kernel_commit XXXXXXXXXXXX \
+       "mm/madvise: make MADV_POPULATE_(READ|WRITE) handle VM_FAULT_RETRY properly"
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs generic
+_require_xfs_io_command madvise -R
+_require_scratch
+_require_dm_target error
+_require_command "$TIMEOUT_PROG" "timeout"
+
+_scratch_mkfs >> $seqres.full 2>&1
+_dmerror_init
+
+filesz=2m
+
+# Create a file that we'll read, then cycle mount to zap pagecache
+_dmerror_mount
+$XFS_IO_PROG -f -c "pwrite -S 0x58 0 $filesz" "$SCRATCH_MNT/a" >> $seqres.full
+_dmerror_unmount
+_dmerror_mount
+
+# Try to read the file data in a regular fashion just to prove that it works.
+echo read with no errors
+$TIMEOUT_PROG -s KILL 10s $XFS_IO_PROG -c "mmap -r 0 $filesz" -c "madvise -R 0 $filesz" "$SCRATCH_MNT/a"
+_dmerror_unmount
+_dmerror_mount
+
+# Load file metadata and induce EIO errors on read.  Try to provoke the kernel;
+# kill the process after 10s so we can clean up.
+stat "$SCRATCH_MNT/a" >> $seqres.full
+echo read with IO errors
+_dmerror_load_error_table
+$TIMEOUT_PROG -s KILL 10s $XFS_IO_PROG -c "mmap -r 0 $filesz" -c "madvise -R 0 $filesz" "$SCRATCH_MNT/a"
+_dmerror_load_working_table
+
+# success, all done
+status=0
+exit
diff --git a/tests/generic/743.out b/tests/generic/743.out
new file mode 100644 (file)
index 0000000..f34158c
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 743
+read with no errors
+read with IO errors
+madvise: Bad address
index b464b22b4c70e5c2950f3a6a366a3099e9e5410f..d6ed3e55d7df2769c27db792e0514b8af3678e5b 100644 (file)
@@ -14,7 +14,7 @@ default: $(DIRT)
 
 include $(BUILDRULES)
 
-install:
+install: default
        $(INSTALL) -m 755 -d $(TARGET_DIR)
        $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
        $(INSTALL) -m 644 group.list $(TARGET_DIR)
index 128d2a3a79e7e659be6b4f651a52758769f1763d..51590b5ce518da1a2e88b137d3c622ec4bea0647 100644 (file)
@@ -14,7 +14,7 @@ default: $(DIRT)
 
 include $(BUILDRULES)
 
-install:
+install: default
        $(INSTALL) -m 755 -d $(TARGET_DIR)
        $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
        $(INSTALL) -m 644 group.list $(TARGET_DIR)
index 260ad31bdc9f87aba262c8864ad77eaa30b69958..17e01bf38b94ecd3d088d59e1920ab77cc187f66 100644 (file)
@@ -14,7 +14,7 @@ default: $(DIRT)
 
 include $(BUILDRULES)
 
-install:
+install: default
        $(INSTALL) -m 755 -d $(TARGET_DIR)
        $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
        $(INSTALL) -m 644 group.list $(TARGET_DIR)
index 54ce8a8f868d59b4dddc5899216e434e5599bec6..f03f628a796a2b5e962fa6ef6f9e7b5dab5747f5 100755 (executable)
@@ -7,7 +7,7 @@
 # Test file copy up on overlayfs by changing mode bits.
 #
 . ./common/preamble
-_begin_fstest attr auto copyup quick
+_begin_fstest attr auto copyup quick perms
 
 # Import common functions.
 . ./common/filter
index d14f22f26475def2e126c3842cb27acba01ce09a..7bab4fbef4cf7000fe9543cf77fb6c6a2458cebc 100755 (executable)
@@ -11,7 +11,7 @@
 # d0e13f5 ovl: fix uid/gid when creating over whiteout
 #
 . ./common/preamble
-_begin_fstest auto quick whiteout
+_begin_fstest auto quick whiteout perms
 
 # Import common functions.
 . ./common/filter
index 20812d8816f0e6afaa6ab12ac05d229df28a4153..09a950baf8e6ae86791bb4d9bfe975c0da637fc2 100755 (executable)
@@ -37,7 +37,7 @@ $SETFATTR_PROG -n "trusted.overlay.opaque" -v "y" $upperdir/testdir
 # $upperdir overlaid on top of $lowerdir, so that "trusted.overlay.opaque"
 # xattr should be honored and should not be listed
 # mount readonly, because there's no upper and workdir
-$MOUNT_PROG -t overlay -o ro -o lowerdir=$upperdir:$lowerdir $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT
+_overlay_scratch_mount_opts -o ro -o lowerdir=$upperdir:$lowerdir
 
 # Dump trusted.overlay xattr, we should not see the "opaque" xattr
 _getfattr -d -m overlay $SCRATCH_MNT/testdir
index fd23e373f055d08d42152329f34463af7e61066d..f0c05579725dd651dbb1af4976958d3aa0120149 100755 (executable)
@@ -7,7 +7,7 @@
 # SGID bit inheritance over whiteout.
 #
 . ./common/preamble
-_begin_fstest auto quick whiteout
+_begin_fstest auto quick whiteout perms
 
 # Import common functions.
 . ./common/filter
index 98a33aec23f7f27b19e16511fb541dc17a77cae4..9f82da344b8a793c0596d82ce757f4e34929623d 100755 (executable)
@@ -16,18 +16,13 @@ _begin_fstest auto quick copyup perms
 
 # real QA test starts here
 
-require_unshare() {
-       unshare -f -r "$@" true &>/dev/null || \
-               _notrun "unshare $@: not supported"
-}
-
 # Modify as appropriate.
 _supported_fs overlay
 _fixed_by_kernel_commit 3fe6e52f0626 \
        "ovl: override creds with the ones from the superblock mounter"
 
 _require_scratch
-require_unshare -m -p -U
+_require_unshare
 
 # Remove all files from previous tests
 _scratch_mkfs
index 95d5aa223cdbbaf6562ce30994463b4e1220e2d3..740c47c1d2dd2de59c849a583215ae514a06ce81 100755 (executable)
@@ -14,7 +14,7 @@
 #     Miklos Szeredi <mszeredi@redhat.com>
 #
 . ./common/preamble
-_begin_fstest auto quick attr
+_begin_fstest auto quick attr perms
 
 # Import common functions.
 . ./common/filter
index 77030d20013e9ece77f899f819f9b60145054756..25c70bc870c0931cc6a3c6d56c368f13e057b677 100755 (executable)
@@ -52,26 +52,42 @@ touch $SCRATCH_MNT/testf1
 # getfattr    ok         no attr     ok    ok
 #
 $SETFATTR_PROG -n "trusted.overlayfsrz" -v "n" \
-  $SCRATCH_MNT/testf0 2>&1 | _filter_scratch
+  $SCRATCH_MNT/testf0 2>&1 | tee -a $seqres.full | _filter_scratch
 
 _getfattr --absolute-names -n "trusted.overlayfsrz" \
-  $SCRATCH_MNT/testf0 2>&1 | _filter_scratch
+  $SCRATCH_MNT/testf0 2>&1 | tee -a $seqres.full | _filter_scratch
 
-# {s,g}etfattr of "trusted.overlay.xxx" should fail.
+# {s,g}etfattr of "trusted.overlay.xxx" fail on older kernels
 # The errno returned varies among kernel versions,
-#            v4.3/7   v4.8-rc1    v4.8       v4.10
-# setfattr  not perm  not perm   not perm   not supp
-# getfattr  no attr   no attr    not perm   not supp
+#            v4.3/7   v4.8-rc1    v4.8       v4.10     v6.7
+# setfattr  not perm  not perm   not perm   not supp  ok
+# getfattr  no attr   no attr    not perm   not supp  ok
 #
-# Consider "Operation not {supported,permitted}" pass.
+# Consider "Operation not {supported,permitted}" pass for old kernels.
 #
-$SETFATTR_PROG -n "trusted.overlay.fsz" -v "n" \
-  $SCRATCH_MNT/testf1 2>&1 | _filter_scratch | \
-  sed -e 's/permitted/supported/g'
+if _check_scratch_overlay_xattr_escapes $SCRATCH_MNT/testf0; then
+       setexp=""
+       getexp="No such attribute"
+else
+       setexp="Operation not supported"
+       getexp="Operation not supported"
+fi
 
-_getfattr --absolute-names -n "trusted.overlay.fsz" \
-  $SCRATCH_MNT/testf1 2>&1 | _filter_scratch | \
-  sed -e 's/permitted/supported/g'
+getres=$(_getfattr --absolute-names -n "trusted.overlay.fsz" \
+  $SCRATCH_MNT/testf1 2>&1 | tee -a $seqres.full | _filter_scratch | \
+  sed 's/permitted/supported/')
+
+[[ "$getres" =~ "$getexp" ]] || echo unexpected getattr result: $getres
+
+setres=$($SETFATTR_PROG -n "trusted.overlay.fsz" -v "n" \
+  $SCRATCH_MNT/testf1 2>&1 | tee -a $seqres.full |_filter_scratch | \
+  sed -e 's/permitted/supported/g')
+
+if [ "$setexp" ]; then
+       [[ "$setres" =~ "$expres" ]] || echo unexpected setattr result: $setres
+else
+       [[ "$setres" == "" ]] || echo unexpected setattr result: $setres
+fi
 
 # success, all done
 status=0
index c4572d6708f10deebba7170ff63b2327c202bbe5..5303000931031a9a0f08f212767d684579d3ed80 100644 (file)
@@ -2,5 +2,3 @@ QA output created by 026
 # file: SCRATCH_MNT/testf0
 trusted.overlayfsrz="n"
 
-setfattr: SCRATCH_MNT/testf1: Operation not supported
-SCRATCH_MNT/testf1: trusted.overlay.fsz: Operation not supported
index 8cd76979808deb9e664bca6fe6e17842887deea6..f4c981ad5bdb87c9d43139ffa88a31f43b9ba070 100755 (executable)
@@ -42,8 +42,7 @@ mkdir -p $lowerdir1 $lowerdir2 $upperdir $workdir
 
 # Mount overlay with lower layers only.
 # Verify that overlay is mounted read-only and that it cannot be remounted rw.
-$MOUNT_PROG -t overlay -o"lowerdir=$lowerdir2:$lowerdir1" \
-                       $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT
+_overlay_scratch_mount_opts -o"lowerdir=$lowerdir2:$lowerdir1"
 touch $SCRATCH_MNT/foo 2>&1 | _filter_scratch
 $MOUNT_PROG -o remount,rw $SCRATCH_MNT 2>&1 | _filter_ro_mount
 $UMOUNT_PROG $SCRATCH_MNT
index da8c645b31e692446fdb81162cc50426aa7723ec..6abe2e018b56977f6aaf618001d33910185e4ce6 100755 (executable)
@@ -133,7 +133,7 @@ unmount_dirs
 
 # Check encode/decode/read of lower file handles on lower layers only r/o overlay.
 # For non-upper overlay mount, nfs_export requires disabling redirect_dir.
-$MOUNT_PROG -t overlay $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT \
+_overlay_scratch_mount_opts \
                        -o ro,redirect_dir=nofollow,nfs_export=on,lowerdir=$middle:$lower
 test_file_handles $SCRATCH_MNT/lowertestdir -rp
 test_file_handles $SCRATCH_MNT/lowertestdir/subdir -rp
@@ -144,7 +144,7 @@ unmount_dirs
 # Overlay lookup cannot follow the redirect from $upper/lowertestdir.new to
 # $lower/lowertestdir. Instead, we mount an overlay subtree rooted at these
 # directories.
-$MOUNT_PROG -t overlay $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT \
+_overlay_scratch_mount_opts \
                -o ro,redirect_dir=nofollow,nfs_export=on,lowerdir=$upper/lowertestdir.new:$lower/lowertestdir
 test_file_handles $SCRATCH_MNT -r
 test_file_handles $SCRATCH_MNT/subdir -rp
index dfa29d01fdff2baa27b048eea5108b595632611e..cf94f930208b050fe8650657f78496af3cedf205 100755 (executable)
@@ -162,7 +162,7 @@ unmount_dirs
 
 # Check encode/decode/read of lower file handles on lower layers only r/o overlay.
 # For non-upper overlay mount, nfs_export requires disabling redirect_dir.
-$MOUNT_PROG -t overlay $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT \
+_overlay_scratch_mount_opts \
                        -o ro,redirect_dir=nofollow,nfs_export=on,lowerdir=$middle:$lower
 test_file_handles $SCRATCH_MNT/lowertestdir -rp
 test_file_handles $SCRATCH_MNT/lowertestdir/subdir -rp
@@ -173,7 +173,7 @@ unmount_dirs
 # Overlay lookup cannot follow the redirect from $upper/lowertestdir.new to
 # $lower/lowertestdir. Instead, we mount an overlay subtree rooted at these
 # directories.
-$MOUNT_PROG -t overlay $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT \
+_overlay_scratch_mount_opts \
                -o ro,redirect_dir=nofollow,nfs_export=on,lowerdir=$upper/lowertestdir.new:$lower/lowertestdir
 test_file_handles $SCRATCH_MNT -r
 test_file_handles $SCRATCH_MNT/subdir -rp
index 733245db08ec3632eedaca715c24f02ff15520cb..f37755da17004339c19b3ad785b2a370ce5d9224 100755 (executable)
@@ -7,7 +7,7 @@
 # Test metadata only copy up functionality.
 #
 . ./common/preamble
-_begin_fstest auto quick metacopy
+_begin_fstest auto quick metacopy redirect prealloc
 
 # Import common functions.
 . ./common/filter
@@ -123,6 +123,13 @@ mount_overlay()
        _overlay_scratch_mount_dirs "$_lowerdir" $upperdir $workdir -o redirect_dir=on,index=on,metacopy=on
 }
 
+mount_ro_overlay()
+{
+       local _lowerdir=$1
+
+       _overlay_scratch_mount_dirs "$_lowerdir" "-" "-" -o ro,redirect_dir=follow,metacopy=on
+}
+
 umount_overlay()
 {
        $UMOUNT_PROG $SCRATCH_MNT
@@ -146,7 +153,8 @@ test_common()
        check_file_size_contents $SCRATCH_MNT/$_target $_size "$_data"
        check_file_blocks $SCRATCH_MNT/$_target $_blocks
 
-       # Make sure copied up file is a metacopy file.
+       # Trigger metadata copy up and check existence of metacopy xattr.
+       chmod 400 $SCRATCH_MNT/$_target
        umount_overlay
        check_metacopy $upperdir/$_target "y"
        check_file_size_contents $upperdir/$_target $_size ""
@@ -165,7 +173,7 @@ test_common()
 create_basic_files()
 {
        _scratch_mkfs
-       mkdir -p $lowerdir $lowerdir2 $upperdir $workdir $workdir2
+       mkdir -p $lowerdir/subdir $lowerdir2 $upperdir $workdir $workdir2
        mkdir -p $upperdir/$udirname
        echo "$lowerdata" > $lowerdir/$lowername
        chmod 600 $lowerdir/$lowername
@@ -184,12 +192,19 @@ create_lower_link()
 
 prepare_midlayer()
 {
+       local _redirect=$1
+
        _scratch_mkfs
        create_basic_files
+       [ -n "$_redirect" ] && mv "$lowerdir/$lowername" "$lowerdir/$_redirect"
        # Create midlayer
        _overlay_scratch_mount_dirs $lowerdir $lowerdir2 $workdir2 -o redirect_dir=on,index=on,metacopy=on
-       # Trigger a metacopy
-       chmod 400 $SCRATCH_MNT/$lowername
+       # Trigger a metacopy with or without redirect
+       if [ -n "$_redirect" ]; then
+               mv "$SCRATCH_MNT/$_redirect" "$SCRATCH_MNT/$lowername"
+       else
+               chmod 400 $SCRATCH_MNT/$lowername
+       fi
        umount_overlay
 }
 
@@ -254,6 +269,24 @@ mount_overlay $lowerdir
 mv $SCRATCH_MNT/$lowerlink $SCRATCH_MNT/$ufile
 test_common $lowerdir $ufile $lowersize $lowerblocks "$lowerdata" "/$lowerlink"
 
+echo -e "\n== Check follow to lowerdata without redirect =="
+prepare_midlayer
+mount_ro_overlay "$lowerdir2:$lowerdir"
+test_common "$lowerdir2:$lowerdir" $lowername $lowersize $lowerblocks \
+               "$lowerdata"
+
+echo -e "\n== Check follow to lowerdata with relative redirect =="
+prepare_midlayer "$lowername.renamed"
+mount_ro_overlay "$lowerdir2:$lowerdir"
+test_common "$lowerdir2:$lowerdir" "$lowername" $lowersize $lowerblocks \
+               "$lowerdata"
+
+echo -e "\n== Check follow to lowerdata with absolute redirect =="
+prepare_midlayer "/subdir/$lowername"
+mount_ro_overlay "$lowerdir2:$lowerdir"
+test_common "$lowerdir2:$lowerdir" "$lowername" $lowersize $lowerblocks \
+               "$lowerdata"
+
 # success, all done
 status=0
 exit
index a4790d31b9737fda09edf911c6440832c30f8afc..f4ce0244a91c04ac75493d3f3f9fcbb56631b689 100644 (file)
@@ -40,3 +40,21 @@ check properties of metadata copied up file
 Unmount and Mount again
 check properties of metadata copied up file
 check properties of data copied up file
+
+== Check follow to lowerdata without redirect ==
+check properties of metadata copied up file
+Unmount and Mount again
+check properties of metadata copied up file
+check properties of data copied up file
+
+== Check follow to lowerdata with relative redirect ==
+check properties of metadata copied up file
+Unmount and Mount again
+check properties of metadata copied up file
+check properties of data copied up file
+
+== Check follow to lowerdata with absolute redirect ==
+check properties of metadata copied up file
+Unmount and Mount again
+check properties of metadata copied up file
+check properties of data copied up file
index 04e13e46a8b57b991514226320df48d5506e9311..a4e9560a3505cc06604aabe2bc0334375d6c4eaa 100755 (executable)
@@ -65,7 +65,7 @@ create_test_files $lowertestdir
 $MOUNT_PROG --bind $lowertestdir $lowertestdir
 
 # For non-upper overlay mount, nfs_export requires disabling redirect_dir.
-$MOUNT_PROG -t overlay $OVL_BASE_SCRATCH_MNT $SCRATCH_MNT \
+_overlay_scratch_mount_opts \
        -o ro,redirect_dir=nofollow,nfs_export=on,lowerdir=$lower:$lower2
 
 # Decode an overlay directory file handle, whose underlying lower dir dentry
index 163e74f1c64014a63ff7e8a74db34d0ce09805ba..5b9f7b186540dd5161ea1029ac210fc82a43eb6e 100755 (executable)
@@ -8,7 +8,7 @@
 # Test overlayfs copy-up function for variant sparse files.
 #
 . ./common/preamble
-_begin_fstest auto quick copyup
+_begin_fstest auto quick copyup fiemap
 
 # Import common functions..
 . ./common/filter
diff --git a/tests/overlay/079 b/tests/overlay/079
new file mode 100755 (executable)
index 0000000..f28fc31
--- /dev/null
@@ -0,0 +1,331 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FS QA Test No. 079
+#
+# Test data-only layers functionality.
+#
+. ./common/preamble
+_begin_fstest auto quick metacopy redirect prealloc
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+_supported_fs overlay
+# We use non-default scratch underlying overlay dirs, we need to check
+# them explicity after test.
+_require_scratch_nocheck
+_require_scratch_overlay_features redirect_dir metacopy
+_require_scratch_overlay_lowerdata_layers
+_require_xfs_io_command "falloc"
+
+# remove all files from previous tests
+_scratch_mkfs
+
+# File size on lower
+dataname="datafile"
+sharedname="shared"
+datacontent="data"
+dataname2="datafile2"
+datacontent2="data2"
+datasize="4096"
+
+# Number of blocks allocated by filesystem on lower. Will be queried later.
+datarblocks=""
+datarblocksize=""
+estimated_datablocks=""
+
+udirname="pureupper"
+ufile="upperfile"
+
+# Check metacopy xattr
+check_metacopy()
+{
+       local target=$1 exist=$2
+       local out_f target_f
+       local msg
+
+       out_f=$(_getfattr --absolute-names --only-values -n \
+               $OVL_XATTR_METACOPY $target 2>&1 | _filter_scratch)
+
+       if [ "$exist" == "y" ];then
+               [ "$out_f" == "" ] && return
+               echo "Metacopy xattr does not exist on ${target}. stdout=$out_f"
+               return
+       fi
+
+       if [ "$out_f" == "" ];then
+               echo "Metacopy xattr exists on ${target} unexpectedly."
+               return
+       fi
+
+       target_f=`echo $target | _filter_scratch`
+       msg="$target_f: trusted.overlay.metacopy: No such attribute"
+
+       [ "$out_f" == "$msg" ] && return
+
+       echo "Error while checking xattr on ${target}. stdout=$out"
+}
+
+# Check redirect xattr
+check_redirect()
+{
+       local target=$1
+       local expect=$2
+
+       value=$(_getfattr --absolute-names --only-values -n \
+               $OVL_XATTR_REDIRECT $target)
+
+       [[ "$value" == "$expect" ]] || echo "Redirect xattr incorrect. Expected=\"$expect\", actual=\"$value\""
+}
+
+# Check size
+check_file_size()
+{
+       local target=$1 expected_size=$2 actual_size
+
+       actual_size=$(_get_filesize $target)
+
+       [ "$actual_size" == "$expected_size" ] || echo "Expected file size $expected_size but actual size is $actual_size"
+}
+
+check_file_blocks()
+{
+       local target=$1 expected_blocks=$2 nr_blocks
+
+       nr_blocks=$(stat -c "%b" $target)
+
+       [ "$nr_blocks" == "$expected_blocks" ] || echo "Expected $expected_blocks blocks but actual number of blocks is ${nr_blocks}."
+}
+
+check_file_contents()
+{
+       local target=$1 expected=$2
+       local actual target_f
+
+       target_f=`echo $target | _filter_scratch`
+
+       read actual<$target
+
+       [ "$actual" == "$expected" ] || echo "Expected file $target_f contents to be \"$expected\" but actual contents are \"$actual\""
+}
+
+check_no_file_contents()
+{
+       local target=$1
+       local actual target_f out_f
+
+       target_f=`echo $target | _filter_scratch`
+       out_f=`cat $target 2>&1 | _filter_scratch`
+       msg="cat: $target_f: No such file or directory"
+
+       [ "$out_f" == "$msg" ] && return
+
+       echo "$target_f unexpectedly has content"
+}
+
+
+check_file_size_contents()
+{
+       local target=$1 expected_size=$2 expected_content=$3
+
+       check_file_size $target $expected_size
+       check_file_contents $target $expected_content
+}
+
+mount_overlay()
+{
+       local _lowerdir=$1 _datadir2=$2 _datadir=$3
+
+       _overlay_scratch_mount_opts \
+               -o"lowerdir=$_lowerdir::$_datadir2::$_datadir" \
+               -o"upperdir=$upperdir,workdir=$workdir" \
+               -o redirect_dir=on,metacopy=on
+}
+
+mount_ro_overlay()
+{
+       local _lowerdir=$1 _datadir2=$2 _datadir=$3
+
+       _overlay_scratch_mount_opts \
+               -o"lowerdir=$_lowerdir::$_datadir2::$_datadir" \
+               -o redirect_dir=follow,metacopy=on
+}
+
+umount_overlay()
+{
+       $UMOUNT_PROG $SCRATCH_MNT
+}
+
+test_no_access()
+{
+       local _target=$1
+
+       mount_ro_overlay "$lowerdir" "$datadir2" "$datadir"
+
+       stat $SCRATCH_MNT/$_target >> $seqres.full 2>&1 || \
+               echo "No access to lowerdata layer $_target"
+
+       echo "Unmount and Mount rw"
+       umount_overlay
+       mount_overlay "$lowerdir" "$datadir2" "$datadir"
+       stat $SCRATCH_MNT/$_target >> $seqres.full 2>&1 || \
+               echo "No access to lowerdata layer $_target"
+       umount_overlay
+}
+
+test_common()
+{
+       local _lowerdir=$1 _datadir2=$2 _datadir=$3
+       local _target=$4 _size=$5 _blocks=$6 _data="$7"
+       local _redirect=$8
+
+       echo "Mount ro"
+       mount_ro_overlay $_lowerdir $_datadir2 $_datadir
+
+       # Check redirect xattr to lowerdata
+       [ -n "$_redirect" ] && check_redirect $lowerdir/$_target "$_redirect"
+
+       echo "check properties of metadata copied up file $_target"
+       check_file_size_contents $SCRATCH_MNT/$_target $_size "$_data"
+       check_file_blocks $SCRATCH_MNT/$_target $_blocks
+
+       # Do a mount cycle and check size and contents again.
+       echo "Unmount and Mount rw"
+       umount_overlay
+       mount_overlay $_lowerdir $_datadir2 $_datadir
+       echo "check properties of metadata copied up file $_target"
+       check_file_size_contents $SCRATCH_MNT/$_target $_size "$_data"
+       check_file_blocks $SCRATCH_MNT/$_target $_blocks
+
+       # Trigger metadata copy up and check existence of metacopy xattr.
+       chmod 400 $SCRATCH_MNT/$_target
+       umount_overlay
+       check_metacopy $upperdir/$_target "y"
+       check_file_size_contents $upperdir/$_target $_size ""
+
+       # Trigger data copy up and check absence of metacopy xattr.
+       mount_overlay $_lowerdir $_datadir2 $_datadir
+       $XFS_IO_PROG -c "falloc 0 1" $SCRATCH_MNT/$_target >> $seqres.full
+       echo "check properties of data copied up file $_target"
+       check_file_size_contents $SCRATCH_MNT/$_target $_size "$_data"
+       umount_overlay
+       check_metacopy $upperdir/$_target "n"
+       check_file_size_contents $upperdir/$_target $_size "$_data"
+}
+
+test_lazy()
+{
+       local _target=$1
+
+       mount_overlay "$lowerdir" "$datadir2" "$datadir"
+
+       # Metadata should be valid
+       check_file_size $SCRATCH_MNT/$_target $datasize
+       check_file_blocks $SCRATCH_MNT/$_target $estimated_datablocks
+
+       # But have no content
+       check_no_file_contents $SCRATCH_MNT/$_target
+
+       umount_overlay
+}
+
+create_basic_files()
+{
+       _scratch_mkfs
+       mkdir -p $datadir/subdir $datadir2/subdir $lowerdir $lowerdir2 $upperdir $workdir $workdir2
+       mkdir -p $upperdir/$udirname
+       echo "$datacontent" > $datadir/$dataname
+       chmod 600 $datadir/$dataname
+       echo "$datacontent2" > $datadir2/$dataname2
+       chmod 600 $datadir2/$dataname2
+
+       echo "$datacontent" > $datadir/$sharedname
+       echo "$datacontent2" > $datadir2/$sharedname
+       chmod 600 $datadir/$sharedname  $datadir2/$sharedname
+
+       # Create files of size datasize.
+       for f in $datadir/$dataname $datadir2/$dataname2 $datadir/$sharedname $datadir2/$sharedname; do
+               $XFS_IO_PROG -c "falloc 0 $datasize" $f
+               $XFS_IO_PROG -c "fsync" $f
+       done
+
+       # Query number of block
+       datablocks=$(stat -c "%b" $datadir/$dataname)
+
+       # For lazy lookup file the block count is estimated based on size and block size
+       datablocksize=$(stat -c "%B" $datadir/$dataname)
+       estimated_datablocks=$(( ($datasize + $datablocksize - 1)/$datablocksize ))
+}
+
+prepare_midlayer()
+{
+       local _redirect=$1
+
+       _scratch_mkfs
+       create_basic_files
+       if [ -n "$_redirect" ]; then
+               mv "$datadir/$dataname" "$datadir/$_redirect"
+               mv "$datadir2/$dataname2" "$datadir2/$_redirect.2"
+               mv "$datadir/$sharedname" "$datadir/$_redirect.shared"
+               mv "$datadir2/$sharedname" "$datadir2/$_redirect.shared"
+       fi
+       # Create midlayer
+       _overlay_scratch_mount_dirs $datadir2:$datadir $lowerdir $workdir2 -o redirect_dir=on,index=on,metacopy=on
+       # Trigger a metacopy with or without redirect
+       if [ -n "$_redirect" ]; then
+               mv "$SCRATCH_MNT/$_redirect" "$SCRATCH_MNT/$dataname"
+               mv "$SCRATCH_MNT/$_redirect.2" "$SCRATCH_MNT/$dataname2"
+               mv "$SCRATCH_MNT/$_redirect.shared" "$SCRATCH_MNT/$sharedname"
+       else
+               chmod 400 $SCRATCH_MNT/$dataname
+               chmod 400 $SCRATCH_MNT/$dataname2
+               chmod 400 $SCRATCH_MNT/$sharedname
+       fi
+       umount_overlay
+}
+
+# Create test directories
+datadir=$OVL_BASE_SCRATCH_MNT/data
+datadir2=$OVL_BASE_SCRATCH_MNT/data2
+lowerdir=$OVL_BASE_SCRATCH_MNT/lower
+upperdir=$OVL_BASE_SCRATCH_MNT/upper
+workdir=$OVL_BASE_SCRATCH_MNT/workdir
+workdir2=$OVL_BASE_SCRATCH_MNT/workdir2
+
+echo -e "\n== Check no follow to lowerdata layer without redirect =="
+prepare_midlayer
+test_no_access "$dataname"
+test_no_access "$dataname2"
+test_no_access "$sharedname"
+
+echo -e "\n== Check no follow to lowerdata layer with relative redirect =="
+prepare_midlayer "$dataname.renamed"
+test_no_access "$dataname"
+test_no_access "$dataname2"
+test_no_access "$sharedname"
+
+echo -e "\n== Check follow to lowerdata layer with absolute redirect =="
+prepare_midlayer "/subdir/$dataname"
+test_common "$lowerdir" "$datadir2" "$datadir" "$dataname" $datasize $datablocks \
+               "$datacontent" "/subdir/$dataname"
+test_common "$lowerdir" "$datadir2" "$datadir" "$dataname2" $datasize $datablocks \
+               "$datacontent2" "/subdir/$dataname.2"
+# Shared file should be picked from upper datadir
+test_common "$lowerdir" "$datadir2" "$datadir" "$sharedname" $datasize $datablocks \
+               "$datacontent2" "/subdir/$dataname.shared"
+
+echo -e "\n== Check lazy follow to lowerdata layer =="
+
+prepare_midlayer "/subdir/$dataname"
+rm $datadir/subdir/$dataname
+test_lazy $dataname
+
+
+# success, all done
+status=0
+exit
diff --git a/tests/overlay/079.out b/tests/overlay/079.out
new file mode 100644 (file)
index 0000000..4807cdb
--- /dev/null
@@ -0,0 +1,42 @@
+QA output created by 079
+
+== Check no follow to lowerdata layer without redirect ==
+No access to lowerdata layer datafile
+Unmount and Mount rw
+No access to lowerdata layer datafile
+No access to lowerdata layer datafile2
+Unmount and Mount rw
+No access to lowerdata layer datafile2
+No access to lowerdata layer shared
+Unmount and Mount rw
+No access to lowerdata layer shared
+
+== Check no follow to lowerdata layer with relative redirect ==
+No access to lowerdata layer datafile
+Unmount and Mount rw
+No access to lowerdata layer datafile
+No access to lowerdata layer datafile2
+Unmount and Mount rw
+No access to lowerdata layer datafile2
+No access to lowerdata layer shared
+Unmount and Mount rw
+No access to lowerdata layer shared
+
+== Check follow to lowerdata layer with absolute redirect ==
+Mount ro
+check properties of metadata copied up file datafile
+Unmount and Mount rw
+check properties of metadata copied up file datafile
+check properties of data copied up file datafile
+Mount ro
+check properties of metadata copied up file datafile2
+Unmount and Mount rw
+check properties of metadata copied up file datafile2
+check properties of data copied up file datafile2
+Mount ro
+check properties of metadata copied up file shared
+Unmount and Mount rw
+check properties of metadata copied up file shared
+check properties of data copied up file shared
+
+== Check lazy follow to lowerdata layer ==
diff --git a/tests/overlay/080 b/tests/overlay/080
new file mode 100755 (executable)
index 0000000..0b5dca0
--- /dev/null
@@ -0,0 +1,326 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 Red Hat, Inc. All Rights Reserved.
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FS QA Test No. 080
+#
+# Test fs-verity functionallity
+#
+. ./common/preamble
+_begin_fstest auto quick metacopy redirect verity
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+. ./common/verity
+
+# real QA test starts here
+_supported_fs overlay
+# We use non-default scratch underlying overlay dirs, we need to check
+# them explicity after test.
+_require_scratch_nocheck
+_require_scratch_overlay_features redirect_dir metacopy
+_require_scratch_overlay_lowerdata_layers
+_require_scratch_overlay_verity
+
+# remove all files from previous tests
+_scratch_mkfs
+
+verityname="verityfile"
+noverityname="noverityfile"
+wrongverityname="wrongverityfile"
+missingverityname="missingverityfile"
+lowerdata="data1"
+lowerdata2="data2"
+lowerdata3="data3"
+lowerdata4="data4"
+lowersize="5"
+
+# Create test directories
+lowerdir=$OVL_BASE_SCRATCH_MNT/lower
+lowerdir2=$OVL_BASE_SCRATCH_MNT/lower2
+upperdir=$OVL_BASE_SCRATCH_MNT/upper
+workdir=$OVL_BASE_SCRATCH_MNT/workdir
+workdir2=$OVL_BASE_SCRATCH_MNT/workdir2
+
+# Check metacopy xattr
+check_metacopy()
+{
+       local target=$1 exist=$2 dataonlybase=$3
+       local out_f target_f
+       local msg
+
+       out_f=$( { _getfattr --absolute-names --only-values -n \
+               $OVL_XATTR_METACOPY $target 2>&3 | od -A n -t x1 -w256 ; } 3>&1 | _filter_scratch)
+        has_version0=`echo $out_f | awk 'NR==1{print $1 == 0}'`
+
+       if [ "$exist" == "y" ];then
+               [ "$out_f" == "" -o "$has_version0" == "1" ] && return
+               echo "Metacopy xattr does not exist on ${target}. stdout=$out_f"
+               return
+       fi
+
+       if [ "$out_f" == ""  -o "$has_version0" == "1" ];then
+               echo "Metacopy xattr exists on ${target} unexpectedly."
+               return
+       fi
+
+       target_f=`echo $target | _filter_scratch`
+       msg="$target_f: trusted.overlay.metacopy: No such attribute"
+
+       [ "$out_f" == "$msg" ] && return
+
+       echo "Error while checking xattr on ${target}. stdout=$out"
+}
+
+# Check verity set in metacopy
+check_verity()
+{
+       local target=$1 exist=$2
+       local out_f target_f
+       local msg
+
+       out_f=$( { _getfattr --absolute-names --only-values -n $OVL_XATTR_METACOPY $target 2>&3 | od -A n -t x1 -w256 ; } 3>&1 | _filter_scratch)
+
+       target_f=`echo $target | _filter_scratch`
+       msg="$target_f: trusted.overlay.metacopy: No such attribute"
+       has_digest=`echo $out_f | awk 'NR==1{print $4 == 1}'`
+
+       if [ "$exist" == "y" ]; then
+               [ "$out_f" == "$msg" -o "$has_digest" == "0" ] && echo "No verity on ${target}. stdout=$out_f"
+               return
+       fi
+
+       [ "$out_f" == "$msg" -o "$has_digest" == "0" ] && return
+       echo "Verity xattr exists on ${target} unexpectedly. stdout=$out_f"
+}
+
+# Check redirect xattr
+check_redirect()
+{
+       local target=$1
+       local expect=$2
+
+       value=$(_getfattr --absolute-names --only-values -n \
+               $OVL_XATTR_REDIRECT $target)
+
+       [[ "$value" == "$expect" ]] || echo "Redirect xattr incorrect. Expected=\"$expect\", actual=\"$value\""
+}
+
+# Check size
+check_file_size()
+{
+       local target=$1 expected_size=$2 actual_size
+
+       actual_size=$(_get_filesize $target)
+
+       [ "$actual_size" == "$expected_size" ] || echo "Expected file size of $target $expected_size but actual size is $actual_size"
+}
+
+check_file_contents()
+{
+       local target=$1 expected=$2
+       local actual target_f
+
+       target_f=`echo $target | _filter_scratch`
+
+       read actual<$target
+
+       [ "$actual" == "$expected" ] || echo "Expected file $target_f contents to be \"$expected\" but actual contents are \"$actual\""
+}
+
+check_file_size_contents()
+{
+       local target=$1 expected_size=$2 expected_content=$3
+
+       check_file_size $target $expected_size
+       check_file_contents $target $expected_content
+}
+
+check_io_error()
+{
+       local target=$1
+       local actual target_f out_f
+
+       target_f=`echo $target | _filter_scratch`
+       out_f=`cat $target 2>&1 | _filter_scratch`
+       msg="cat: $target_f: Input/output error"
+
+       [ "$out_f" == "$msg" ] && return
+
+       echo "$target_f unexpectedly has no I/O error"
+}
+
+create_basic_files()
+{
+       local subdir=$1
+
+       _scratch_mkfs
+       mkdir -p $lowerdir $lowerdir2 $upperdir $workdir $workdir2
+
+       if [ "$subdir" != "" ]; then
+           mkdir $lowerdir/$subdir
+       fi
+
+       echo -n "$lowerdata" > $lowerdir/$subdir$verityname
+       echo -n "$lowerdata2" > $lowerdir/$subdir$noverityname
+       echo -n "$lowerdata3" > $lowerdir/$subdir$wrongverityname
+       echo -n "$lowerdata4" > $lowerdir/$subdir$missingverityname
+
+       for f in $verityname $noverityname $wrongverityname $missingverityname; do
+               chmod 600 $lowerdir/$subdir$f
+
+               if [ "$f" != "$noverityname" ]; then
+                       _fsv_enable $lowerdir/$subdir$f
+               fi
+        done
+}
+
+prepare_midlayer()
+{
+       local dataonlybase=$1
+
+       subdir=""
+       if [ "$dataonlybase" == "y" ]; then
+           subdir="base/"
+       fi
+
+       create_basic_files "$subdir"
+       # Create midlayer
+       _overlay_scratch_mount_dirs $lowerdir $lowerdir2 $workdir2 -o redirect_dir=on,index=on,verity=on,metacopy=on
+       for f in $verityname $noverityname $wrongverityname $missingverityname; do
+               if [ "$dataonlybase" == "y" ]; then
+                   mv $SCRATCH_MNT/base/$f $SCRATCH_MNT/$f
+               else
+                   chmod 400 $SCRATCH_MNT/$f
+               fi
+       done
+       umount_overlay
+
+       if [ "$dataonlybase" == "y" ]; then
+           rm -rf $lowerdir2/base
+       fi
+
+       for f in $verityname $noverityname $wrongverityname $missingverityname; do
+               # Ensure we have right metacopy and verity xattrs
+               check_metacopy $lowerdir2/$f "y"
+
+               if [ "$f" == "$noverityname" ]; then
+                   check_verity $lowerdir2/$f "n"
+               else
+                   check_verity $lowerdir2/$f "y"
+               fi
+
+               if [ "$dataonlybase" == "y" ]; then
+                       check_redirect $lowerdir2/$f "/base/$f"
+               fi
+
+               check_file_size_contents $lowerdir2/$f $lowersize ""
+       done
+
+       # Fixup missing and wrong verity in lowerdir
+       rm -f $lowerdir/$subdir$wrongverityname $lowerdir/$subdir$missingverityname
+       echo -n "changed" > $lowerdir/$subdir$wrongverityname
+       _fsv_enable $lowerdir/$subdir$wrongverityname
+       echo "$lowerdata4" > $lowerdir/$subdir$missingverityname
+}
+
+test_common()
+{
+       local dataonlybase=$1
+       local verity=$2
+
+       if [ $dataonlybase == "y" ]; then
+               mount_overlay "$lowerdir2::$lowerdir" $verity
+       else
+               mount_overlay "$lowerdir2:$lowerdir" $verity
+       fi
+
+       check_file_size_contents $SCRATCH_MNT/$verityname $lowersize "$lowerdata"
+
+       if [ "$verity" == "require" ]; then
+               check_io_error $SCRATCH_MNT/$noverityname
+       else
+               check_file_size_contents $SCRATCH_MNT/$noverityname $lowersize "$lowerdata2"
+       fi
+
+       if [ "$verity" == "off" ]; then
+               check_file_size_contents $SCRATCH_MNT/$wrongverityname $lowersize "changed"
+               check_file_size_contents $SCRATCH_MNT/$missingverityname $lowersize "$lowerdata4"
+       else
+               check_io_error $SCRATCH_MNT/$missingverityname
+               check_io_error $SCRATCH_MNT/$wrongverityname
+       fi
+
+       umount_overlay
+}
+
+mount_overlay()
+{
+       local _lowerdir=$1
+       local _verity=$2
+
+       _overlay_scratch_mount_dirs "$_lowerdir" $upperdir $workdir -o redirect_dir=on,index=on,metacopy=on,verity=$_verity
+}
+
+umount_overlay()
+{
+       $UMOUNT_PROG $SCRATCH_MNT
+}
+
+
+echo -e "\n== Check fsverity validation =="
+
+prepare_midlayer "n"
+test_common "n" "off"
+prepare_midlayer "n"
+test_common "n" "on"
+
+# Now with data-only layers
+prepare_midlayer "y"
+test_common "y" "off"
+prepare_midlayer "y"
+test_common "y" "on"
+
+echo -e "\n== Check fsverity require =="
+
+prepare_midlayer "n"
+test_common "n" "require"
+
+# Now with data-only layers
+prepare_midlayer "y"
+test_common "y" "require"
+
+echo -e "\n== Check fsverity copy-up =="
+
+# Ensure Second level metacopy sets verity xattr
+prepare_midlayer "n"
+mount_overlay "$lowerdir2:$lowerdir" "on"
+chmod 200 $SCRATCH_MNT/$verityname
+umount_overlay
+check_metacopy $upperdir/$verityname "y"
+check_verity $upperdir/$verityname "y"
+
+# Ensure data copy up remove verity xattr
+create_basic_files ""
+mount_overlay "$lowerdir" "on"
+echo foo >> $SCRATCH_MNT/$verityname
+umount_overlay
+check_metacopy $upperdir/$verityname "n"
+check_verity $upperdir/$verityname "n"
+
+# Ensure metacopy is only used if verity is enabled in lower for verity=require
+create_basic_files ""
+mount_overlay "$lowerdir" "require"
+chmod 200 $SCRATCH_MNT/$verityname
+chmod 200 $SCRATCH_MNT/$noverityname
+umount_overlay
+check_metacopy $upperdir/$verityname "y"
+check_verity $upperdir/$verityname "y"
+check_metacopy $upperdir/$noverityname "n"
+check_verity $upperdir/$noverityname "n"
+
+# success, all done
+status=0
+exit
diff --git a/tests/overlay/080.out b/tests/overlay/080.out
new file mode 100644 (file)
index 0000000..7d1f1d1
--- /dev/null
@@ -0,0 +1,7 @@
+QA output created by 080
+
+== Check fsverity validation ==
+
+== Check fsverity require ==
+
+== Check fsverity copy-up ==
diff --git a/tests/overlay/081 b/tests/overlay/081
new file mode 100755 (executable)
index 0000000..481e993
--- /dev/null
@@ -0,0 +1,128 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FSQA Test No. 081
+#
+# Test persistent (and optionally unique) overlayfs fsid
+# with mount options uuid=null/auto/on introduced in kernel v6.6
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+_supported_fs overlay
+
+_scratch_mkfs >>$seqres.full 2>&1
+
+# Create overlay layer with pre-packaged merge dir
+upperdir=$OVL_BASE_SCRATCH_MNT/$OVL_UPPER
+workdir=$OVL_BASE_SCRATCH_MNT/$OVL_WORK
+lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER
+mkdir -p $upperdir/test_dir
+mkdir -p $lowerdir/test_dir
+test_dir=$SCRATCH_MNT/test_dir/
+
+# Record base fs fsid
+upper_fsid=$(stat -f -c '%i' $upperdir)
+lower_fsid=$(stat -f -c '%i' $lowerdir)
+
+# Sanity tests
+[[ -n "$upper_fsid" ]] || \
+       echo "invalid upper fs fsid"
+[[ "$lower_fsid" == "$upper_fsid" ]] || \
+       echo "lower fs and upper fs fsid differ"
+
+# Test legacy behavior - ovl fsid inherited from upper fs
+_overlay_scratch_mount_dirs $lowerdir $upperdir $workdir -o uuid=null 2>/dev/null || \
+       _notrun "Overlayfs does not support unique fsid feature"
+
+# Lookup of test_dir marks upper root as "impure", so following (uuid=auto) mounts
+# will NOT behave as first time mount of a new overlayfs
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+[[ "$ovl_fsid" == "$upper_fsid" ]] || \
+       echo "Overlayfs (uuid=null) and upper fs fsid differ"
+
+# Keep base fs mounted in case it has a volatile fsid (e.g. tmpfs)
+$UMOUNT_PROG $SCRATCH_MNT
+
+# Test legacy behavior is preserved by default for existing "impure" overlayfs
+_scratch_mount
+
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+[[ "$ovl_fsid" == "$upper_fsid" ]] || \
+       echo "Overlayfs (after uuid=null) and upper fs fsid differ"
+
+$UMOUNT_PROG $SCRATCH_MNT
+
+# Test unique fsid on explicit opt-in for existing "impure" overlayfs
+_scratch_mount -o uuid=on
+
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+ovl_unique_fsid=$ovl_fsid
+[[ "$ovl_fsid" != "$upper_fsid" ]] || \
+       echo "Overlayfs (uuid=on) and upper fs fsid are the same"
+
+$UMOUNT_PROG $SCRATCH_MNT
+
+# Test unique fsid is persistent by default after it was created
+_scratch_mount
+
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+[[ "$ovl_fsid" == "$ovl_unique_fsid" ]] || \
+       echo "Overlayfs (after uuid=on) unique fsid is not persistent"
+
+$UMOUNT_PROG $SCRATCH_MNT
+
+# Test ignore existing persistent fsid on explicit opt-out
+_scratch_mount -o uuid=null
+
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+[[ "$ovl_fsid" == "$upper_fsid" ]] || \
+       echo "Overlayfs (uuid=null) and upper fs fsid differ"
+
+$UMOUNT_PROG $SCRATCH_MNT
+
+# Test fallback to uuid=null with non-upper ovelray
+_overlay_scratch_mount_dirs "$upperdir:$lowerdir" "-" "-" -o ro,uuid=on
+
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+[[ "$ovl_fsid" == "$lower_fsid" ]] || \
+       echo "Overlayfs (no upper) and lower fs fsid differ"
+
+# Re-create fresh overlay layers, so following (uuid=auto) mounts
+# will behave as first time mount of a new overlayfs
+_scratch_unmount
+_scratch_mkfs >>$seqres.full 2>&1
+mkdir -p $upperdir/test_dir
+mkdir -p $lowerdir/test_dir
+
+# Record new base fs fsid
+upper_fsid=$(stat -f -c '%i' $upperdir)
+
+# Test unique fsid by default for first time mount of new overlayfs
+_scratch_mount
+
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+ovl_unique_fsid=$ovl_fsid
+[[ "$ovl_fsid" != "$upper_fsid" ]] || \
+       echo "Overlayfs (new) and upper fs fsid are the same"
+
+$UMOUNT_PROG $SCRATCH_MNT
+
+# Test unique fsid is persistent by default after it was created
+_scratch_mount -o uuid=on
+
+ovl_fsid=$(stat -f -c '%i' $test_dir)
+[[ "$ovl_fsid" == "$ovl_unique_fsid" ]] || \
+       echo "Overlayfs (uuid=on) unique fsid is not persistent"
+
+$UMOUNT_PROG $SCRATCH_MNT
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/overlay/081.out b/tests/overlay/081.out
new file mode 100644 (file)
index 0000000..663a886
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 081
+Silence is golden
diff --git a/tests/overlay/082 b/tests/overlay/082
new file mode 100755 (executable)
index 0000000..7409917
--- /dev/null
@@ -0,0 +1,67 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FS QA Test 082
+#
+# Test for a regression in copy up of symlink that has the noatime inode
+# attribute.
+#
+# kernel commit 72db82115d2b ("ovl: copy up sync/noatime fileattr flags")
+# from v5.15 introduced the regression.
+#
+. ./common/preamble
+_begin_fstest auto quick copyup symlink atime
+
+# real QA test starts here
+_supported_fs overlay
+_fixed_by_kernel_commit ab048302026d \
+       "ovl: fix failed copyup of fileattr on a symlink"
+
+_require_scratch
+_require_chattr A
+
+# remove all files from previous runs
+_scratch_mkfs
+
+# prepare lower test dir with NOATIME flag
+lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER
+mkdir -p $lowerdir/testdir
+$CHATTR_PROG +A $lowerdir/testdir >> $seqres.full 2>&1 ||
+       echo "failed to set No_Atime flag on $lowerdir/testdir"
+
+# The NOATIME is inherited to children symlink in ext4/fs2fs
+# (and on tmpfs on recent kernels).
+# The overlayfs test will not fail unless base fs is
+# one of those filesystems.
+#
+# The problem with this inheritence is that the NOATIME flag is inherited
+# to a symlink and the flag does take effect, but there is no way to query
+# the flag (lsattr) or change it (chattr) on a symlink, so overlayfs will
+# fail when trying to copy up NOATIME flag from lower to upper symlink.
+#
+touch $lowerdir/testdir/foo
+ln -sf foo $lowerdir/testdir/lnk
+$LSATTR_PROG -l $lowerdir/testdir/foo >> $seqres.full
+
+before=$(stat -c %x $lowerdir/testdir/lnk)
+echo "symlink atime before readlink: $before" >> $seqres.full 2>&1
+sleep 2s
+cat $lowerdir/testdir/lnk
+after=$(stat -c %x $lowerdir/testdir/lnk)
+echo "symlink atime after readlink: $after" >> $seqres.full 2>&1
+
+[ "$before" == "$after" ] || \
+       _notrun "base fs $OVL_BASE_FSTYP does not inherit No_Atime flag on symlink"
+
+# mounting overlay
+_scratch_mount
+
+# moving symlink will try to copy up lower symlink flags
+# and fails with error ENXIO, if the bug is reproduced
+mv $SCRATCH_MNT/testdir/lnk $SCRATCH_MNT/
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/overlay/082.out b/tests/overlay/082.out
new file mode 100644 (file)
index 0000000..2977f14
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 082
+Silence is golden
diff --git a/tests/overlay/083 b/tests/overlay/083
new file mode 100755 (executable)
index 0000000..df82d1f
--- /dev/null
@@ -0,0 +1,76 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FS QA Test 083
+#
+# Test regressions in parsing and display of special chars in mount options.
+#
+# The following kernel commits from v6.5 introduced regressions:
+#  b36a5780cb44 ("ovl: modify layer parameter parsing")
+#  1784fbc2ed9c ("ovl: port to new mount api")
+#
+
+. ./common/preamble
+_begin_fstest auto quick mount
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_supported_fs overlay
+_fixed_by_kernel_commit 32db51070850 \
+       "ovl: fix regression in showing lowerdir mount option"
+_fixed_by_kernel_commit c34706acf40b \
+       "ovl: fix regression in parsing of mount options with escaped comma"
+
+# _overlay_check_* helpers do not handle special chars well
+_require_scratch_nocheck
+
+# Remove all files from previous tests
+_scratch_mkfs
+
+# Create lowerdirs with special characters
+lowerdir_spaces="$OVL_BASE_SCRATCH_MNT/lower1 with  spaces"
+lowerdir_colons="$OVL_BASE_SCRATCH_MNT/lower2:with::colons"
+lowerdir_commas="$OVL_BASE_SCRATCH_MNT/lower3,with,,commas"
+lowerdir_colons_esc="$OVL_BASE_SCRATCH_MNT/lower2\:with\:\:colons"
+lowerdir_commas_esc="$OVL_BASE_SCRATCH_MNT/lower3\,with\,\,commas"
+upperdir=$OVL_BASE_SCRATCH_MNT/$OVL_UPPER
+workdir=$OVL_BASE_SCRATCH_MNT/$OVL_WORK
+mkdir -p "$lowerdir_spaces" "$lowerdir_colons" "$lowerdir_commas"
+
+# _overlay_mount_* helpers do not handle special chars well, so execute mount directly.
+# if escaped colons are not parsed correctly, mount will fail.
+$MOUNT_PROG -t overlay ovl_esc_test $SCRATCH_MNT \
+       -o"upperdir=$upperdir,workdir=$workdir" \
+       -o"lowerdir=$lowerdir_colons_esc:$lowerdir_spaces" \
+       2>&1 | tee -a $seqres.full
+
+# if spaces are not escaped when showing mount options,
+# mount command will not show the word 'spaces' after the spaces
+$MOUNT_PROG -t overlay | grep ovl_esc_test  | tee -a $seqres.full | grep -v spaces && \
+       echo "ERROR: escaped spaces truncated from lowerdir mount option"
+
+# Re-create the upper/work dirs to mount them with a different lower
+# This is required in case index feature is enabled
+$UMOUNT_PROG $SCRATCH_MNT
+rm -rf "$upperdir" "$workdir"
+mkdir -p "$upperdir" "$workdir"
+
+# Kernel commit c34706acf40b fixes parsing of mount options with escaped comma
+# when the mount options string is provided via data argument to mount(2) syscall.
+# With libmount >= 2.39, libmount itself will try to split the comma separated
+# options list provided to mount(8) commnad line and call fsconfig(2) for each
+# mount option seperately.  Since libmount does not obay to overlayfs escaped
+# commas format, it will call fsconfig(2) with the wrong path (i.e. ".../lower3")
+# and this test will fail, but the failure would indicate a libmount issue, not
+# a kernel issue.  Therefore, force libmount to use mount(2) syscall, so we only
+# test the kernel fix.
+LIBMOUNT_FORCE_MOUNT2=always $MOUNT_PROG -t overlay $OVL_BASE_SCRATCH_DEV $SCRATCH_MNT \
+       -o"upperdir=$upperdir,workdir=$workdir,lowerdir=$lowerdir_commas_esc" 2>> $seqres.full || \
+       echo "ERROR: incorrect parsing of escaped comma in lowerdir mount option"
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/overlay/083.out b/tests/overlay/083.out
new file mode 100644 (file)
index 0000000..0beba30
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 083
+Silence is golden
diff --git a/tests/overlay/084 b/tests/overlay/084
new file mode 100755 (executable)
index 0000000..778396a
--- /dev/null
@@ -0,0 +1,177 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 Red Hat, Inc. All Rights Reserved.
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FS QA Test No. 084
+#
+# Test advanded nesting functionallity
+#
+. ./common/preamble
+_begin_fstest auto quick nested
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       # Unmount nested mounts if things fail
+       $UMOUNT_PROG $OVL_BASE_SCRATCH_MNT/nested  2>/dev/null
+       rm -rf $tmp
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+_supported_fs overlay
+# This test does not run on kernels prior ro v6.7 and now it will also make sure
+# that the following on-disk format change was backported to v6.7 based kernels
+_fixed_by_kernel_commit 420332b94119 \
+       "ovl: mark xwhiteouts directory with overlay.opaque='x'"
+
+# We use non-default scratch underlying overlay dirs, we need to check
+# them explicity after test.
+_require_scratch_nocheck
+_require_scratch_overlay_xattr_escapes
+
+# remove all files from previous tests
+_scratch_mkfs
+
+lowerdir=$OVL_BASE_SCRATCH_MNT/lower
+middir=$OVL_BASE_SCRATCH_MNT/mid
+upperdir=$OVL_BASE_SCRATCH_MNT/upper
+workdir=$OVL_BASE_SCRATCH_MNT/workdir
+nesteddir=$OVL_BASE_SCRATCH_MNT/nested
+
+umount_overlay()
+{
+       $UMOUNT_PROG $SCRATCH_MNT
+}
+
+test_escape()
+{
+       local prefix=$1
+
+       echo -e "\n== Check xattr escape $prefix =="
+
+       # index feature would require nfs_export on $nesteddir mount
+       local extra_options="-o index=off"
+       if [ "$prefix" == "user" ]; then
+            extra_options+=",userxattr"
+       fi
+
+       _scratch_mkfs
+       mkdir -p $lowerdir $middir $upperdir $workdir $nesteddir
+
+       _overlay_scratch_mount_dirs $lowerdir $middir $workdir $extra_options
+
+       mkdir -p $SCRATCH_MNT/layer1/dir/ $SCRATCH_MNT/layer2/dir
+
+       touch $SCRATCH_MNT/layer1/dir/file
+
+       # Make layer2/dir an opaque file
+       # Only one of these will be escaped, but both should succeed
+       setfattr -n user.overlay.opaque -v "y" $SCRATCH_MNT/layer2/dir
+       setfattr -n trusted.overlay.opaque -v "y" $SCRATCH_MNT/layer2/dir
+
+       getfattr -m "overlay\\." --absolute-names -d $SCRATCH_MNT/layer2/dir | _filter_scratch
+
+       umount_overlay
+
+       getfattr -m "overlay\\." --absolute-names -d $middir/layer2/dir | _filter_scratch
+
+       # Remount as lower and try again
+       _overlay_scratch_mount_dirs $middir:$lowerdir $upperdir $workdir $extra_options
+
+       getfattr -m "overlay\\." --absolute-names -d $SCRATCH_MNT/layer2/dir | _filter_scratch
+
+       # Recursively mount and ensure the opaque dir is working with both trusted and user xattrs
+       echo "nested xattr mount with trusted.overlay"
+       _overlay_mount_dirs $SCRATCH_MNT/layer2:$SCRATCH_MNT/layer1 - - overlayfs $nesteddir
+       stat $nesteddir/dir/file  2>&1 | _filter_scratch
+       $UMOUNT_PROG $nesteddir
+
+       echo "nested xattr mount with user.overlay"
+       _overlay_mount_dirs $SCRATCH_MNT/layer2:$SCRATCH_MNT/layer1 - - -o userxattr overlayfs $nesteddir
+       stat $nesteddir/dir/file  2>&1 | _filter_scratch
+       $UMOUNT_PROG $nesteddir
+
+       # Also ensure propagate the escaped xattr when we copy-up layer2/dir
+       echo "copy-up of escaped xattrs"
+       touch $SCRATCH_MNT/layer2/dir/other_file
+       getfattr -m "$prefix.overlay\\.overlay" --absolute-names -d $upperdir/layer2/dir | _filter_scratch
+
+       umount_overlay
+}
+
+test_escape trusted
+test_escape user
+
+do_test_xwhiteout()
+{
+       local prefix=$1
+       local basedir=$2
+
+       local extra_options=""
+       if [ "$prefix" == "user" ]; then
+            extra_options="-o userxattr"
+       fi
+
+       mkdir -p $basedir/lower $basedir/upper $basedir/work
+       touch $basedir/lower/regular $basedir/lower/hidden  $basedir/upper/hidden
+       # overlay.opaque="x" means directory has xwhiteout children
+       setfattr -n $prefix.overlay.opaque -v "x" $basedir/upper
+       setfattr -n $prefix.overlay.whiteout -v "y" $basedir/upper/hidden
+
+       # Test the hidden is invisible
+       _overlay_scratch_mount_dirs $basedir/upper:$basedir/lower - - $extra_options
+       ls $SCRATCH_MNT
+       stat $SCRATCH_MNT/hidden 2>&1 | _filter_scratch
+       umount_overlay
+}
+
+# Validate that xwhiteouts work like whiteouts
+test_xwhiteout()
+{
+       local prefix=$1
+
+       echo -e "\n== Check xwhiteout $prefix =="
+
+       _scratch_mkfs
+
+       do_test_xwhiteout $prefix $OVL_BASE_SCRATCH_MNT
+}
+
+test_xwhiteout trusted
+test_xwhiteout user
+
+# Validate that (escaped) xwhiteouts work inside a nested overlayfs mount
+test_escaped_xwhiteout()
+{
+       local prefix=$1
+
+       echo -e "\n== Check escaped xwhiteout $prefix =="
+
+       # index feature would require nfs_export on $nesteddir mount
+       local extra_options="-o index=off"
+       if [ "$prefix" == "user" ]; then
+            extra_options+=",userxattr"
+       fi
+
+       _scratch_mkfs
+       mkdir -p $lowerdir $upperdir $workdir $nesteddir
+
+       _overlay_mount_dirs $lowerdir $upperdir $workdir $extra_options overlayfs $nesteddir
+
+       do_test_xwhiteout $prefix $nesteddir
+
+       $UMOUNT_PROG $nesteddir
+}
+
+test_escaped_xwhiteout trusted
+test_escaped_xwhiteout user
+
+# success, all done
+status=0
+exit
diff --git a/tests/overlay/084.out b/tests/overlay/084.out
new file mode 100644 (file)
index 0000000..54b890d
--- /dev/null
@@ -0,0 +1,61 @@
+QA output created by 084
+
+== Check xattr escape trusted ==
+# file: SCRATCH_MNT/layer2/dir
+trusted.overlay.opaque="y"
+user.overlay.opaque="y"
+
+# file: SCRATCH_DEV/mid/layer2/dir
+trusted.overlay.overlay.opaque="y"
+user.overlay.opaque="y"
+
+# file: SCRATCH_MNT/layer2/dir
+trusted.overlay.opaque="y"
+user.overlay.opaque="y"
+
+nested xattr mount with trusted.overlay
+stat: cannot statx 'SCRATCH_DEV/nested/dir/file': No such file or directory
+nested xattr mount with user.overlay
+stat: cannot statx 'SCRATCH_DEV/nested/dir/file': No such file or directory
+copy-up of escaped xattrs
+# file: SCRATCH_DEV/upper/layer2/dir
+trusted.overlay.overlay.opaque="y"
+
+
+== Check xattr escape user ==
+# file: SCRATCH_MNT/layer2/dir
+trusted.overlay.opaque="y"
+user.overlay.opaque="y"
+
+# file: SCRATCH_DEV/mid/layer2/dir
+trusted.overlay.opaque="y"
+user.overlay.overlay.opaque="y"
+
+# file: SCRATCH_MNT/layer2/dir
+trusted.overlay.opaque="y"
+user.overlay.opaque="y"
+
+nested xattr mount with trusted.overlay
+stat: cannot statx 'SCRATCH_DEV/nested/dir/file': No such file or directory
+nested xattr mount with user.overlay
+stat: cannot statx 'SCRATCH_DEV/nested/dir/file': No such file or directory
+copy-up of escaped xattrs
+# file: SCRATCH_DEV/upper/layer2/dir
+user.overlay.overlay.opaque="y"
+
+
+== Check xwhiteout trusted ==
+regular
+stat: cannot statx 'SCRATCH_MNT/hidden': No such file or directory
+
+== Check xwhiteout user ==
+regular
+stat: cannot statx 'SCRATCH_MNT/hidden': No such file or directory
+
+== Check escaped xwhiteout trusted ==
+regular
+stat: cannot statx 'SCRATCH_MNT/hidden': No such file or directory
+
+== Check escaped xwhiteout user ==
+regular
+stat: cannot statx 'SCRATCH_MNT/hidden': No such file or directory
diff --git a/tests/overlay/085 b/tests/overlay/085
new file mode 100755 (executable)
index 0000000..0f4e4b0
--- /dev/null
@@ -0,0 +1,332 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FS QA Test No. 085
+#
+# Test data-only layers functionality.
+# This is a variant of test overlay/079 with lowerdir+,datadir+ mount options
+#
+. ./common/preamble
+_begin_fstest auto quick metacopy redirect prealloc
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+_supported_fs overlay
+# We use non-default scratch underlying overlay dirs, we need to check
+# them explicity after test.
+_require_scratch_nocheck
+_require_scratch_overlay_features redirect_dir metacopy
+_require_scratch_overlay_lowerdir_add_layers
+_require_xfs_io_command "falloc"
+
+# remove all files from previous tests
+_scratch_mkfs
+
+# File size on lower
+dataname="datafile"
+sharedname="shared"
+datacontent="data"
+dataname2="datafile2"
+datacontent2="data2"
+datasize="4096"
+
+# Number of blocks allocated by filesystem on lower. Will be queried later.
+datarblocks=""
+datarblocksize=""
+estimated_datablocks=""
+
+udirname="pureupper"
+ufile="upperfile"
+
+# Check metacopy xattr
+check_metacopy()
+{
+       local target=$1 exist=$2
+       local out_f target_f
+       local msg
+
+       out_f=$(_getfattr --absolute-names --only-values -n \
+               $OVL_XATTR_METACOPY $target 2>&1 | _filter_scratch)
+
+       if [ "$exist" == "y" ];then
+               [ "$out_f" == "" ] && return
+               echo "Metacopy xattr does not exist on ${target}. stdout=$out_f"
+               return
+       fi
+
+       if [ "$out_f" == "" ];then
+               echo "Metacopy xattr exists on ${target} unexpectedly."
+               return
+       fi
+
+       target_f=`echo $target | _filter_scratch`
+       msg="$target_f: trusted.overlay.metacopy: No such attribute"
+
+       [ "$out_f" == "$msg" ] && return
+
+       echo "Error while checking xattr on ${target}. stdout=$out"
+}
+
+# Check redirect xattr
+check_redirect()
+{
+       local target=$1
+       local expect=$2
+
+       value=$(_getfattr --absolute-names --only-values -n \
+               $OVL_XATTR_REDIRECT $target)
+
+       [[ "$value" == "$expect" ]] || echo "Redirect xattr incorrect. Expected=\"$expect\", actual=\"$value\""
+}
+
+# Check size
+check_file_size()
+{
+       local target=$1 expected_size=$2 actual_size
+
+       actual_size=$(_get_filesize $target)
+
+       [ "$actual_size" == "$expected_size" ] || echo "Expected file size $expected_size but actual size is $actual_size"
+}
+
+check_file_blocks()
+{
+       local target=$1 expected_blocks=$2 nr_blocks
+
+       nr_blocks=$(stat -c "%b" $target)
+
+       [ "$nr_blocks" == "$expected_blocks" ] || echo "Expected $expected_blocks blocks but actual number of blocks is ${nr_blocks}."
+}
+
+check_file_contents()
+{
+       local target=$1 expected=$2
+       local actual target_f
+
+       target_f=`echo $target | _filter_scratch`
+
+       read actual<$target
+
+       [ "$actual" == "$expected" ] || echo "Expected file $target_f contents to be \"$expected\" but actual contents are \"$actual\""
+}
+
+check_no_file_contents()
+{
+       local target=$1
+       local actual target_f out_f
+
+       target_f=`echo $target | _filter_scratch`
+       out_f=`cat $target 2>&1 | _filter_scratch`
+       msg="cat: $target_f: No such file or directory"
+
+       [ "$out_f" == "$msg" ] && return
+
+       echo "$target_f unexpectedly has content"
+}
+
+
+check_file_size_contents()
+{
+       local target=$1 expected_size=$2 expected_content=$3
+
+       check_file_size $target $expected_size
+       check_file_contents $target $expected_content
+}
+
+mount_overlay()
+{
+       local _lowerdir=$1 _datadir2=$2 _datadir=$3
+
+       _overlay_scratch_mount_opts \
+               -o"lowerdir+=$_lowerdir,datadir+=$_datadir2,datadir+=$_datadir" \
+               -o"upperdir=$upperdir,workdir=$workdir" \
+               -o redirect_dir=on,metacopy=on
+}
+
+mount_ro_overlay()
+{
+       local _lowerdir=$1 _datadir2=$2 _datadir=$3
+
+       _overlay_scratch_mount_opts \
+               -o"lowerdir+=$_lowerdir,datadir+=$_datadir2,datadir+=$_datadir" \
+               -o redirect_dir=follow,metacopy=on
+}
+
+umount_overlay()
+{
+       $UMOUNT_PROG $SCRATCH_MNT
+}
+
+test_no_access()
+{
+       local _target=$1
+
+       mount_ro_overlay "$lowerdir" "$datadir2" "$datadir"
+
+       stat $SCRATCH_MNT/$_target >> $seqres.full 2>&1 || \
+               echo "No access to lowerdata layer $_target"
+
+       echo "Unmount and Mount rw"
+       umount_overlay
+       mount_overlay "$lowerdir" "$datadir2" "$datadir"
+       stat $SCRATCH_MNT/$_target >> $seqres.full 2>&1 || \
+               echo "No access to lowerdata layer $_target"
+       umount_overlay
+}
+
+test_common()
+{
+       local _lowerdir=$1 _datadir2=$2 _datadir=$3
+       local _target=$4 _size=$5 _blocks=$6 _data="$7"
+       local _redirect=$8
+
+       echo "Mount ro"
+       mount_ro_overlay $_lowerdir $_datadir2 $_datadir
+
+       # Check redirect xattr to lowerdata
+       [ -n "$_redirect" ] && check_redirect $lowerdir/$_target "$_redirect"
+
+       echo "check properties of metadata copied up file $_target"
+       check_file_size_contents $SCRATCH_MNT/$_target $_size "$_data"
+       check_file_blocks $SCRATCH_MNT/$_target $_blocks
+
+       # Do a mount cycle and check size and contents again.
+       echo "Unmount and Mount rw"
+       umount_overlay
+       mount_overlay $_lowerdir $_datadir2 $_datadir
+       echo "check properties of metadata copied up file $_target"
+       check_file_size_contents $SCRATCH_MNT/$_target $_size "$_data"
+       check_file_blocks $SCRATCH_MNT/$_target $_blocks
+
+       # Trigger metadata copy up and check existence of metacopy xattr.
+       chmod 400 $SCRATCH_MNT/$_target
+       umount_overlay
+       check_metacopy $upperdir/$_target "y"
+       check_file_size_contents $upperdir/$_target $_size ""
+
+       # Trigger data copy up and check absence of metacopy xattr.
+       mount_overlay $_lowerdir $_datadir2 $_datadir
+       $XFS_IO_PROG -c "falloc 0 1" $SCRATCH_MNT/$_target >> $seqres.full
+       echo "check properties of data copied up file $_target"
+       check_file_size_contents $SCRATCH_MNT/$_target $_size "$_data"
+       umount_overlay
+       check_metacopy $upperdir/$_target "n"
+       check_file_size_contents $upperdir/$_target $_size "$_data"
+}
+
+test_lazy()
+{
+       local _target=$1
+
+       mount_overlay "$lowerdir" "$datadir2" "$datadir"
+
+       # Metadata should be valid
+       check_file_size $SCRATCH_MNT/$_target $datasize
+       check_file_blocks $SCRATCH_MNT/$_target $estimated_datablocks
+
+       # But have no content
+       check_no_file_contents $SCRATCH_MNT/$_target
+
+       umount_overlay
+}
+
+create_basic_files()
+{
+       _scratch_mkfs
+       mkdir -p $datadir/subdir $datadir2/subdir $lowerdir $lowerdir2 $upperdir $workdir $workdir2
+       mkdir -p $upperdir/$udirname
+       echo "$datacontent" > $datadir/$dataname
+       chmod 600 $datadir/$dataname
+       echo "$datacontent2" > $datadir2/$dataname2
+       chmod 600 $datadir2/$dataname2
+
+       echo "$datacontent" > $datadir/$sharedname
+       echo "$datacontent2" > $datadir2/$sharedname
+       chmod 600 $datadir/$sharedname  $datadir2/$sharedname
+
+       # Create files of size datasize.
+       for f in $datadir/$dataname $datadir2/$dataname2 $datadir/$sharedname $datadir2/$sharedname; do
+               $XFS_IO_PROG -c "falloc 0 $datasize" $f
+               $XFS_IO_PROG -c "fsync" $f
+       done
+
+       # Query number of block
+       datablocks=$(stat -c "%b" $datadir/$dataname)
+
+       # For lazy lookup file the block count is estimated based on size and block size
+       datablocksize=$(stat -c "%B" $datadir/$dataname)
+       estimated_datablocks=$(( ($datasize + $datablocksize - 1)/$datablocksize ))
+}
+
+prepare_midlayer()
+{
+       local _redirect=$1
+
+       _scratch_mkfs
+       create_basic_files
+       if [ -n "$_redirect" ]; then
+               mv "$datadir/$dataname" "$datadir/$_redirect"
+               mv "$datadir2/$dataname2" "$datadir2/$_redirect.2"
+               mv "$datadir/$sharedname" "$datadir/$_redirect.shared"
+               mv "$datadir2/$sharedname" "$datadir2/$_redirect.shared"
+       fi
+       # Create midlayer
+       _overlay_scratch_mount_dirs $datadir2:$datadir $lowerdir $workdir2 -o redirect_dir=on,index=on,metacopy=on
+       # Trigger a metacopy with or without redirect
+       if [ -n "$_redirect" ]; then
+               mv "$SCRATCH_MNT/$_redirect" "$SCRATCH_MNT/$dataname"
+               mv "$SCRATCH_MNT/$_redirect.2" "$SCRATCH_MNT/$dataname2"
+               mv "$SCRATCH_MNT/$_redirect.shared" "$SCRATCH_MNT/$sharedname"
+       else
+               chmod 400 $SCRATCH_MNT/$dataname
+               chmod 400 $SCRATCH_MNT/$dataname2
+               chmod 400 $SCRATCH_MNT/$sharedname
+       fi
+       umount_overlay
+}
+
+# Create test directories
+datadir=$OVL_BASE_SCRATCH_MNT/data
+datadir2=$OVL_BASE_SCRATCH_MNT/data2
+lowerdir=$OVL_BASE_SCRATCH_MNT/lower
+upperdir=$OVL_BASE_SCRATCH_MNT/upper
+workdir=$OVL_BASE_SCRATCH_MNT/workdir
+workdir2=$OVL_BASE_SCRATCH_MNT/workdir2
+
+echo -e "\n== Check no follow to lowerdata layer without redirect =="
+prepare_midlayer
+test_no_access "$dataname"
+test_no_access "$dataname2"
+test_no_access "$sharedname"
+
+echo -e "\n== Check no follow to lowerdata layer with relative redirect =="
+prepare_midlayer "$dataname.renamed"
+test_no_access "$dataname"
+test_no_access "$dataname2"
+test_no_access "$sharedname"
+
+echo -e "\n== Check follow to lowerdata layer with absolute redirect =="
+prepare_midlayer "/subdir/$dataname"
+test_common "$lowerdir" "$datadir2" "$datadir" "$dataname" $datasize $datablocks \
+               "$datacontent" "/subdir/$dataname"
+test_common "$lowerdir" "$datadir2" "$datadir" "$dataname2" $datasize $datablocks \
+               "$datacontent2" "/subdir/$dataname.2"
+# Shared file should be picked from upper datadir
+test_common "$lowerdir" "$datadir2" "$datadir" "$sharedname" $datasize $datablocks \
+               "$datacontent2" "/subdir/$dataname.shared"
+
+echo -e "\n== Check lazy follow to lowerdata layer =="
+
+prepare_midlayer "/subdir/$dataname"
+rm $datadir/subdir/$dataname
+test_lazy $dataname
+
+
+# success, all done
+status=0
+exit
diff --git a/tests/overlay/085.out b/tests/overlay/085.out
new file mode 100644 (file)
index 0000000..4b9b2d7
--- /dev/null
@@ -0,0 +1,42 @@
+QA output created by 085
+
+== Check no follow to lowerdata layer without redirect ==
+No access to lowerdata layer datafile
+Unmount and Mount rw
+No access to lowerdata layer datafile
+No access to lowerdata layer datafile2
+Unmount and Mount rw
+No access to lowerdata layer datafile2
+No access to lowerdata layer shared
+Unmount and Mount rw
+No access to lowerdata layer shared
+
+== Check no follow to lowerdata layer with relative redirect ==
+No access to lowerdata layer datafile
+Unmount and Mount rw
+No access to lowerdata layer datafile
+No access to lowerdata layer datafile2
+Unmount and Mount rw
+No access to lowerdata layer datafile2
+No access to lowerdata layer shared
+Unmount and Mount rw
+No access to lowerdata layer shared
+
+== Check follow to lowerdata layer with absolute redirect ==
+Mount ro
+check properties of metadata copied up file datafile
+Unmount and Mount rw
+check properties of metadata copied up file datafile
+check properties of data copied up file datafile
+Mount ro
+check properties of metadata copied up file datafile2
+Unmount and Mount rw
+check properties of metadata copied up file datafile2
+check properties of data copied up file datafile2
+Mount ro
+check properties of metadata copied up file shared
+Unmount and Mount rw
+check properties of metadata copied up file shared
+check properties of data copied up file shared
+
+== Check lazy follow to lowerdata layer ==
diff --git a/tests/overlay/086 b/tests/overlay/086
new file mode 100755 (executable)
index 0000000..b596051
--- /dev/null
@@ -0,0 +1,81 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2023 CTERA Networks. All Rights Reserved.
+#
+# FS QA Test 086
+#
+# Test lowerdir+,datadir+ mount option restrictions.
+#
+
+. ./common/preamble
+_begin_fstest auto quick mount
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_supported_fs overlay
+
+# _overlay_check_* helpers do not handle special chars well
+_require_scratch_nocheck
+_require_scratch_overlay_lowerdir_add_layers
+
+# Remove all files from previous tests
+_scratch_mkfs
+
+# Create lowerdirs with special characters
+lowerdir_spaces="$OVL_BASE_SCRATCH_MNT/lower1 with  spaces"
+lowerdir_colons="$OVL_BASE_SCRATCH_MNT/lower2:with::colons"
+lowerdir_colons_esc="$OVL_BASE_SCRATCH_MNT/lower2\:with\:\:colons"
+lowerdir=$OVL_BASE_SCRATCH_MNT/$OVL_LOWER
+upperdir=$OVL_BASE_SCRATCH_MNT/$OVL_UPPER
+workdir=$OVL_BASE_SCRATCH_MNT/$OVL_WORK
+mkdir -p "$lowerdir_spaces" "$lowerdir_colons"
+
+# _overlay_mount_* helpers do not handle lowerdir+,datadir+, so execute mount directly.
+
+# check illegal combinations and order of lowerdir,lowerdir+,datadir+
+$MOUNT_PROG -t overlay none $SCRATCH_MNT \
+       -o"lowerdir=$lowerdir,lowerdir+=$lowerdir_colons" \
+       2>> $seqres.full && \
+       echo "ERROR: invalid combination of lowerdir and lowerdir+ mount options"
+
+$UMOUNT_PROG $SCRATCH_MNT 2>/dev/null
+
+$MOUNT_PROG -t overlay none $SCRATCH_MNT \
+       -o"lowerdir=$lowerdir,datadir+=$lowerdir_colons" \
+       -o redirect_dir=follow,metacopy=on 2>> $seqres.full && \
+       echo "ERROR: invalid combination of lowerdir and datadir+ mount options"
+
+$UMOUNT_PROG $SCRATCH_MNT 2>/dev/null
+
+$MOUNT_PROG -t overlay none $SCRATCH_MNT \
+       -o"datadir+=$lowerdir,lowerdir+=$lowerdir_colons" \
+       -o redirect_dir=follow,metacopy=on 2>> $seqres.full && \
+       echo "ERROR: invalid order of lowerdir+ and datadir+ mount options"
+
+$UMOUNT_PROG $SCRATCH_MNT 2>/dev/null
+
+# mount is expected to fail with escaped colons.
+$MOUNT_PROG -t overlay none $SCRATCH_MNT \
+       -o"lowerdir+=$lowerdir_colons_esc" \
+       2>> $seqres.full && \
+       echo "ERROR: incorrect parsing of escaped colons in lowerdir+ mount option"
+
+$UMOUNT_PROG $SCRATCH_MNT 2>/dev/null
+
+# mount is expected to succeed without escaped colons.
+$MOUNT_PROG -t overlay ovl_esc_test $SCRATCH_MNT \
+       -o"lowerdir+=$lowerdir_colons,datadir+=$lowerdir_spaces" \
+       -o redirect_dir=follow,metacopy=on \
+       2>&1 | tee -a $seqres.full
+
+# if spaces are not escaped when showing mount options,
+# mount command will not show the word 'spaces' after the spaces
+$MOUNT_PROG -t overlay | grep ovl_esc_test | tee -a $seqres.full | \
+       grep -q 'datadir+'.*spaces || \
+       echo "ERROR: escaped spaces truncated from datadir+ mount option"
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/overlay/086.out b/tests/overlay/086.out
new file mode 100644 (file)
index 0000000..b34758f
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 086
+Silence is golden
index 2785b94d9485634bb231ccc27f7e3db91e6ab684..c71e78d615d6a0c49ecc2b6ee7255fa2a248af90 100644 (file)
@@ -14,7 +14,7 @@ default: $(DIRT)
 
 include $(BUILDRULES)
 
-install:
+install: default
        $(INSTALL) -m 755 -d $(TARGET_DIR)
        $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
        $(INSTALL) -m 644 group.list $(TARGET_DIR)
index 0c74ba393d2a2bdabfee8ea0c590441ae65d3b07..d72c04d95d9f96d7366dd7dae319fc801e6e6982 100644 (file)
@@ -14,7 +14,7 @@ default: $(DIRT)
 
 include $(BUILDRULES)
 
-install:
+install: default
        $(INSTALL) -m 755 -d $(TARGET_DIR)
        $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
        $(INSTALL) -m 644 group.list $(TARGET_DIR)
diff --git a/tests/selftest/001 b/tests/selftest/001
new file mode 100755 (executable)
index 0000000..f348033
--- /dev/null
@@ -0,0 +1,13 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# FS QA Test No. 001
+#
+# A test that should always pass
+#
+. ./common/preamble
+_begin_fstest selftest
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/selftest/001.out b/tests/selftest/001.out
new file mode 100644 (file)
index 0000000..88678b8
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 001
+Silence is golden
diff --git a/tests/selftest/002 b/tests/selftest/002
new file mode 100755 (executable)
index 0000000..189ae2c
--- /dev/null
@@ -0,0 +1,13 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# FS QA Test No. 002
+#
+# A test that should always fail due to output mismatch
+#
+. ./common/preamble
+_begin_fstest selftest
+
+echo "I am intentionally broken"
+status=0
+exit
diff --git a/tests/selftest/002.out b/tests/selftest/002.out
new file mode 100644 (file)
index 0000000..61705c7
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 002
+Silence is golden
diff --git a/tests/selftest/003 b/tests/selftest/003
new file mode 100755 (executable)
index 0000000..c7a2848
--- /dev/null
@@ -0,0 +1,15 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# FS QA Test No. 003
+#
+# A test that _fail's
+#
+
+. ./common/preamble
+_begin_fstest selftest
+
+_fail "I have _fail'ed"
+
+status=0
+exit
diff --git a/tests/selftest/003.out b/tests/selftest/003.out
new file mode 100644 (file)
index 0000000..6895fc8
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 003
+Silence is golden
diff --git a/tests/selftest/004 b/tests/selftest/004
new file mode 100755 (executable)
index 0000000..89a3884
--- /dev/null
@@ -0,0 +1,15 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# FS QA Test No. 004
+#
+# A test that should always be skipped
+#
+. ./common/preamble
+_begin_fstest selftest
+
+_notrun "Always skip me"
+
+echo "I should be skipped"
+status=0
+exit
diff --git a/tests/selftest/004.out b/tests/selftest/004.out
new file mode 100644 (file)
index 0000000..af8614a
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 004
+Silence is golden
diff --git a/tests/selftest/005 b/tests/selftest/005
new file mode 100755 (executable)
index 0000000..738cf44
--- /dev/null
@@ -0,0 +1,16 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# FS QA Test No. 005
+#
+# A test that crashes
+#
+. ./common/preamble
+_begin_fstest dangerous_selftest
+
+echo 1 > /proc/sys/kernel/sysrq
+echo c > /proc/sysrq-trigger
+
+echo "I should have crashed by now"
+status=0
+exit
diff --git a/tests/selftest/005.out b/tests/selftest/005.out
new file mode 100644 (file)
index 0000000..a5027f1
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 005
+Silence is golden
diff --git a/tests/selftest/006 b/tests/selftest/006
new file mode 100755 (executable)
index 0000000..150de6e
--- /dev/null
@@ -0,0 +1,19 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# FS QA Test No. 006
+#
+# A test that hangs
+#
+
+. ./common/preamble
+_begin_fstest dangerous_selftest
+
+while :
+do
+    sleep 1d
+done
+
+echo "I should still be sleeping"
+status=0
+exit
diff --git a/tests/selftest/006.out b/tests/selftest/006.out
new file mode 100644 (file)
index 0000000..675c1b7
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 006
+Silence is golden
diff --git a/tests/selftest/Makefile b/tests/selftest/Makefile
new file mode 100644 (file)
index 0000000..cabc35a
--- /dev/null
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 2023 Google, Inc. All Rights Reserved.
+#
+
+TOPDIR = ../..
+include $(TOPDIR)/include/builddefs
+include $(TOPDIR)/include/buildgrouplist
+
+SELFTEST_DIR = selftest
+TARGET_DIR = $(PKG_LIB_DIR)/$(TESTS_DIR)/$(SELFTEST_DIR)
+DIRT = group.list
+
+default: $(DIRT)
+
+include $(BUILDRULES)
+
+install: default
+       $(INSTALL) -m 755 -d $(TARGET_DIR)
+       $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
+       $(INSTALL) -m 644 group.list $(TARGET_DIR)
+       $(INSTALL) -m 644 $(OUTFILES) $(TARGET_DIR)
+
+# Nothing.
+install-dev install-lib:
index bd52b6a0cf78788d76f1f70bbb29a07fff7d0187..1d4e8d943bdaa6b45b8c56d84623b327bbc1fa9a 100755 (executable)
@@ -7,7 +7,7 @@
 # Test that filesystem sends discard requests only on free blocks
 #
 . ./common/preamble
-_begin_fstest auto trim
+_begin_fstest auto trim fiemap
 
 _supported_fs ext4 xfs btrfs
 _require_test
@@ -20,7 +20,7 @@ if [ "$FSTYP" = "btrfs" ]; then
        fssize=3000
 else
        _require_fs_space $TEST_DIR 307200
-       fssize=300
+       fssize=$(_small_fs_size_mb 300)           # 200m phys/virt size
 fi
 
 [ "$FSTYP" = "ext4" ] && _require_dumpe2fs
@@ -46,7 +46,11 @@ get_holes()
        # to established convention which requires the filesystem to be
        # unmounted while we probe the underlying file.
        $UMOUNT_PROG $loop_mnt
-       $XFS_IO_PROG -F -c fiemap $1 | grep hole | $SED_PROG 's/.*\[\(.*\)\.\.\(.*\)\].*/\1 \2/'
+
+       # FIEMAP only works on regular files, so call it on the backing file
+       # and not the loop device like everything else
+       $XFS_IO_PROG -F -c fiemap $img_file | grep hole | \
+               $SED_PROG 's/.*\[\(.*\)\.\.\(.*\)\].*/\1 \2/'
        _mount $loop_dev $loop_mnt
 }
 
@@ -55,7 +59,7 @@ get_free_sectors()
        case $FSTYP in
        ext4)
        $UMOUNT_PROG $loop_mnt
-       $DUMPE2FS_PROG $img_file  2>&1 | grep " Free blocks" | cut -d ":" -f2- | \
+       $DUMPE2FS_PROG $loop_dev  2>&1 | grep " Free blocks" | cut -d ":" -f2- | \
                tr ',' '\n' | $SED_PROG 's/^ //' | \
                $AWK_PROG -v spb=$sectors_per_block 'BEGIN{FS="-"};
                     NF {
@@ -69,7 +73,7 @@ get_free_sectors()
        agsize=`$XFS_INFO_PROG $loop_mnt | $SED_PROG -n 's/.*agsize=\(.*\) blks.*/\1/p'`
        # Convert free space (agno, block, length) to (start sector, end sector)
        $UMOUNT_PROG $loop_mnt
-       $XFS_DB_PROG -r -c "freesp -d" $img_file | $SED_PROG '/^.*from/,$d'| \
+       $XFS_DB_PROG -r -c "freesp -d" $loop_dev | $SED_PROG '/^.*from/,$d'| \
                 $AWK_PROG -v spb=$sectors_per_block -v agsize=$agsize \
                '{ print spb * ($1 * agsize + $2), spb * ($1 * agsize + $2 + $3) - 1 }'
        ;;
@@ -77,15 +81,15 @@ get_free_sectors()
                local device_size=$($BTRFS_UTIL_PROG filesystem show --raw $loop_mnt 2>&1 \
                        | sed -n "s/^.*size \([0-9]*\).*$/\1/p")
 
-               local nodesize=$($BTRFS_UTIL_PROG inspect-internal dump-super $img_file  \
+               local nodesize=$($BTRFS_UTIL_PROG inspect-internal dump-super $loop_dev \
                        | sed -n 's/nodesize\s*\(.*\)/\1/p')
 
                # Get holes within block groups
-               $BTRFS_UTIL_PROG inspect-internal dump-tree -t extent $img_file \
+               $BTRFS_UTIL_PROG inspect-internal dump-tree -t extent $loop_dev \
                        | $AWK_PROG -v sectorsize=512 -v nodesize=$nodesize -f $here/src/parse-extent-tree.awk
 
                # Get holes within unallocated space on disk
-               $BTRFS_UTIL_PROG inspect-internal dump-tree -t dev $img_file \
+               $BTRFS_UTIL_PROG inspect-internal dump-tree -t dev $loop_dev \
                        | $AWK_PROG -v sectorsize=512 -v devsize=$device_size -f $here/src/parse-dev-tree.awk
 
        ;;
@@ -159,7 +163,7 @@ done
 
 # Get reference fiemap, this can contain i.e. uninitialized inode table
 sync
-get_holes $img_file > $fiemap_ref
+get_holes > $fiemap_ref
 
 # Delete some files
 find $loop_mnt -type f -print | $AWK_PROG \
@@ -173,7 +177,7 @@ echo "done."
 echo -n "Detecting interesting holes in image..."
 # Get after-trim fiemap
 sync
-get_holes $img_file > $fiemap_after
+get_holes > $fiemap_after
 echo "done."
 
 echo -n "Comparing holes to the reported space from FS..."
index f312871412a5484acad8e9d5a7bf9bc31d5f6e0e..2e6aa718446171a2403f8e8ebb9a0bc202bf8ee4 100644 (file)
@@ -14,7 +14,7 @@ default: $(DIRT)
 
 include $(BUILDRULES)
 
-install:
+install: default
        $(INSTALL) -m 755 -d $(TARGET_DIR)
        $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
        $(INSTALL) -m 644 group.list $(TARGET_DIR)
diff --git a/tests/tmpfs/001 b/tests/tmpfs/001
new file mode 100755 (executable)
index 0000000..37ef0b1
--- /dev/null
@@ -0,0 +1,27 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Rodrigo Campos Catelin (Microsoft). All Rights Reserved.
+#
+# FS QA Test 001
+#
+# Test that idmapped mounts behave correctly with tmpfs filesystem.
+#
+. ./common/preamble
+_begin_fstest auto quick idmapped
+
+# get standard environment, filters and checks
+. ./common/filter
+
+# real QA test starts here
+
+_supported_fs tmpfs
+_require_idmapped_mounts
+_require_test
+
+echo "Silence is golden"
+
+$here/src/vfs/vfstest --test-tmpfs --device "$TEST_DEV" \
+               --mount "$TEST_DIR" --fstype "$FSTYP"
+
+status=$?
+exit
diff --git a/tests/tmpfs/001.out b/tests/tmpfs/001.out
new file mode 100644 (file)
index 0000000..88678b8
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 001
+Silence is golden
diff --git a/tests/tmpfs/Makefile b/tests/tmpfs/Makefile
new file mode 100644 (file)
index 0000000..46b62da
--- /dev/null
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 2023 Rodrigo Campos Catelin (Microsoft). All Rights Reserved.
+#
+
+TOPDIR = ../..
+include $(TOPDIR)/include/builddefs
+include $(TOPDIR)/include/buildgrouplist
+
+TMPFS_DIR = tmpfs
+TARGET_DIR = $(PKG_LIB_DIR)/$(TESTS_DIR)/$(TMPFS_DIR)
+DIRT = group.list
+
+default: $(DIRT)
+
+include $(BUILDRULES)
+
+install: default
+       $(INSTALL) -m 755 -d $(TARGET_DIR)
+       $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
+       $(INSTALL) -m 644 group.list $(TARGET_DIR)
+       $(INSTALL) -m 644 $(OUTFILES) $(TARGET_DIR)
+
+# Nothing.
+install-dev install-lib:
index ed4434f0ba59864d214f2210fae30597c69a0cdc..feda69a66201e33a498a24c7443423adb64d366e 100644 (file)
@@ -14,7 +14,7 @@ default: $(DIRT)
 
 include $(BUILDRULES)
 
-install:
+install: default
        $(INSTALL) -m 755 -d $(TARGET_DIR)
        $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
        $(INSTALL) -m 644 group.list $(TARGET_DIR)
index 3260b3a2743cfa739ffdf2955a9e02512a09b761..433b0bc39480bcdc2288c606e1869abe8c960fb8 100644 (file)
@@ -1,8 +1,8 @@
 QA output created by 006
 error/fail_at_unmount=1
 error/metadata/default/max_retries=-1
-error/metadata/default/retry_timeout_seconds=0
+error/metadata/default/retry_timeout_seconds=-1
 error/metadata/EIO/max_retries=-1
-error/metadata/EIO/retry_timeout_seconds=0
+error/metadata/EIO/retry_timeout_seconds=-1
 error/metadata/ENOSPC/max_retries=-1
-error/metadata/ENOSPC/retry_timeout_seconds=0
+error/metadata/ENOSPC/retry_timeout_seconds=-1
index a53f6c9296f525b1acf5dccd6c3a4e44021b8cdd..e7d6153b2ace97c2445b091e1731e06d6c27aa13 100755 (executable)
@@ -46,7 +46,7 @@ _do_test()
                echo "holes is in range"
        else
                # quick check - how many holes did we get?
-               count=`xfs_bmap $out | egrep -c ': hole'`
+               count=`xfs_bmap $out | grep -E -c ': hole'`
                # blocks can end up adjacent, therefore number of holes varies
                _within_tolerance "holes" $count $_holes 10% -v
        fi
index d6e9099e961a79d4a033a472f7ad1ebb3e11554e..ed44d074b55925fcc3a7513813b0b2554e9895c6 100755 (executable)
@@ -16,10 +16,11 @@ _begin_fstest auto freeze log metadata quick
 # Override the default cleanup function.
 _cleanup()
 {
+       # Make sure $SCRATCH_MNT is unfreezed
+       xfs_freeze -u $SCRATCH_MNT 2>/dev/null
        $KILLALL_PROG -9 fsstress 2>/dev/null
        wait
        cd /
-       _scratch_unmount 2>/dev/null
        rm -f $tmp.*
 }
 
index 5ebc9058127887153e604319c6967d6b7e5d156d..e0dccc4e428104978945cf7bd29390f403deb683 100755 (executable)
@@ -50,7 +50,7 @@ _do_test()
     if [ $failed -eq 0 ]
     then        
         # quick check - how many holes did we get?
-        count=`xfs_bmap $out | egrep -c ': hole'`
+        count=`xfs_bmap $out | grep -E -c ': hole'`
         echo "    $count hole(s) detected"
         # and how big was the file?
         _filesize $out
index 1f0ebac3447ce5facb49053f6ffcaba04e9df729..be25c17645c69f0616b67cf13c898ea6c6b7220e 100755 (executable)
@@ -12,7 +12,7 @@
 # ENOSPC/EDQUOT.
 #
 . ./common/preamble
-_begin_fstest auto enospc quick quota
+_begin_fstest auto enospc quick quota prealloc
 
 # Import common functions.
 . ./common/filter
index 2bb7b8d5097565cc052814eb3b195cc042913aad..a7f4d2436999898b674c24de4b0e6a6ebea5900a 100755 (executable)
@@ -40,10 +40,10 @@ _require_scratch
 # need 128M space, don't make any assumption
 _scratch_mkfs >/dev/null 2>&1
 _scratch_mount
-_require_fs_space $SCRATCH_MNT 131072
+_require_fs_space $SCRATCH_MNT 196608
 _scratch_unmount
 
-_scratch_mkfs_sized $((32 * 1024 * 1024)) > $tmp.mkfs.raw || _fail "mkfs failed"
+_scratch_mkfs_sized $((96 * 1024 * 1024)) > $tmp.mkfs.raw || _fail "mkfs failed"
 cat $tmp.mkfs.raw | _filter_mkfs >$seqres.full 2>$tmp.mkfs
 # get original data blocks number and agcount
 . $tmp.mkfs
diff --git a/tests/xfs/018 b/tests/xfs/018
new file mode 100755 (executable)
index 0000000..73040ed
--- /dev/null
@@ -0,0 +1,212 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022, Oracle and/or its affiliates.  All Rights Reserved.
+#
+# FS QA Test No. 018
+#
+# Log attribute replay test
+#
+. ./common/preamble
+_begin_fstest auto quick attr
+
+# get standard environment, filters and checks
+. ./common/filter
+. ./common/attr
+. ./common/inject
+
+_cleanup()
+{
+       rm -rf $tmp.*
+       if [ -n "$ORIG_XFS_LARP" ];then
+               echo $ORIG_XFS_LARP > /sys/fs/xfs/debug/larp
+       fi
+}
+
+test_attr_replay()
+{
+       testfile=$testdir/$1
+       attr_name=$2
+       attr_value=$3
+       flag=$4
+       error_tag=$5
+
+       # Inject error
+       _scratch_inject_error $error_tag
+
+       # Set attribute, being careful not to include the trailing newline
+       # in the attr value.
+       echo -n "$attr_value" | ${ATTR_PROG} -$flag "$attr_name" $testfile 2>&1 | \
+                           _filter_scratch
+
+       # FS should be shut down, touch will fail
+       touch $testfile 2>&1 | _filter_scratch
+
+       # Remount to replay log
+       _scratch_remount_dump_log >> $seqres.full
+
+       # FS should be online, touch should succeed
+       touch $testfile
+
+       # Verify attr recovery
+       $ATTR_PROG -l $testfile >> $seqres.full
+       echo "Checking contents of $attr_name" >> $seqres.full
+       echo -n "$attr_name: "
+       $ATTR_PROG -q -g $attr_name $testfile 2> /dev/null | md5sum;
+
+       echo ""
+}
+
+create_test_file()
+{
+       filename=$testdir/$1
+       count=$2
+       attr_value=$3
+
+       touch $filename
+
+       for i in `seq $count`
+       do
+               $ATTR_PROG -s "attr_name$i" -V $attr_value $filename >> \
+                       $seqres.full
+       done
+}
+
+require_larp()
+{
+       touch $SCRATCH_MNT/a
+
+       # Set attribute, being careful not to include the trailing newline
+       # in the attr value.
+       echo -n "attr_value" | ${ATTR_PROG} -s "attr_name" $SCRATCH_MNT/a 2>&1 | \
+               grep 'Operation not supported' && \
+               _notrun 'LARP not supported on this filesystem'
+}
+
+# real QA test starts here
+_supported_fs xfs
+
+_require_scratch
+_require_scratch_xfs_crc
+_require_attrs
+_require_xfs_io_error_injection "larp"
+_require_xfs_io_error_injection "da_leaf_split"
+_require_xfs_io_error_injection "attr_leaf_to_node"
+_require_xfs_sysfs debug/larp
+test -w /sys/fs/xfs/debug/larp || _notrun "larp knob not writable"
+
+ORIG_XFS_LARP=`cat /sys/fs/xfs/debug/larp`
+# turn on log attributes
+echo 1 > /sys/fs/xfs/debug/larp
+
+attr16="0123456789ABCDEF"
+attr17="X$attr16"
+attr64="$attr16$attr16$attr16$attr16"
+attr256="$attr64$attr64$attr64$attr64"
+attr1k="$attr256$attr256$attr256$attr256"
+attr4k="$attr1k$attr1k$attr1k$attr1k"
+attr8k="$attr4k$attr4k"
+attr16k="$attr8k$attr8k"
+attr32k="$attr16k$attr16k"
+attr32l="X$attr32k"
+attr64k="$attr32k$attr32k"
+
+echo "*** mkfs"
+_scratch_mkfs >/dev/null
+
+blk_sz=$(_scratch_xfs_get_sb_field blocksize)
+err_inj_attr_sz=$(( blk_sz / 3 - 50 ))
+err_inj_attr_val=$(printf "A%.0s" $(seq $err_inj_attr_sz))
+
+echo "*** mount FS"
+_scratch_mount
+
+testdir=$SCRATCH_MNT/testdir
+mkdir $testdir
+
+require_larp
+
+# empty, inline
+create_test_file empty_file1 0
+test_attr_replay empty_file1 "attr_name" $attr64 "s" "larp"
+test_attr_replay empty_file1 "attr_name" $attr64 "r" "larp"
+
+# empty, inline with an unaligned value
+create_test_file empty_fileX 0
+test_attr_replay empty_fileX "attr_nameX" $attr17 "s" "larp"
+test_attr_replay empty_fileX "attr_nameX" $attr17 "r" "larp"
+
+# empty, internal
+create_test_file empty_file2 0
+test_attr_replay empty_file2 "attr_name" $attr1k "s" "larp"
+test_attr_replay empty_file2 "attr_name" $attr1k "r" "larp"
+
+# empty, remote
+create_test_file empty_file3 0
+test_attr_replay empty_file3 "attr_name" $attr64k "s" "larp"
+test_attr_replay empty_file3 "attr_name" $attr64k "r" "larp"
+
+# empty, remote with an unaligned value
+create_test_file empty_fileY 0
+test_attr_replay empty_fileY "attr_name" $attr32l "s" "larp"
+test_attr_replay empty_fileY "attr_name" $attr32l "r" "larp"
+
+# inline, inline
+create_test_file inline_file1 1 $attr16
+test_attr_replay inline_file1 "attr_name2" $attr64 "s" "larp"
+test_attr_replay inline_file1 "attr_name2" $attr64 "r" "larp"
+
+# inline, internal
+create_test_file inline_file2 1 $attr16
+test_attr_replay inline_file2 "attr_name2" $attr1k "s" "larp"
+test_attr_replay inline_file2 "attr_name2" $attr1k "r" "larp"
+
+# inline, remote
+create_test_file inline_file3 1 $attr16
+test_attr_replay inline_file3 "attr_name2" $attr64k "s" "larp"
+test_attr_replay inline_file3 "attr_name2" $attr64k "r" "larp"
+
+# extent, internal
+create_test_file extent_file1 1 $attr1k
+test_attr_replay extent_file1 "attr_name2" $attr1k "s" "larp"
+test_attr_replay extent_file1 "attr_name2" $attr1k "r" "larp"
+
+# extent, inject error on split
+create_test_file extent_file2 3 $err_inj_attr_val
+test_attr_replay extent_file2 "attr_name4" $attr256 "s" "da_leaf_split"
+
+# extent, inject error on fork transition
+create_test_file extent_file3 3 $err_inj_attr_val
+test_attr_replay extent_file3 "attr_name4" $attr256 "s" "attr_leaf_to_node"
+
+# extent, remote
+create_test_file extent_file4 1 $attr1k
+test_attr_replay extent_file4 "attr_name2" $attr64k "s" "larp"
+test_attr_replay extent_file4 "attr_name2" $attr64k "r" "larp"
+
+# remote, internal
+create_test_file remote_file1 1 $attr64k
+test_attr_replay remote_file1 "attr_name2" $attr1k "s" "larp"
+test_attr_replay remote_file1 "attr_name2" $attr1k "r" "larp"
+
+# remote, remote
+create_test_file remote_file2 1 $attr64k
+test_attr_replay remote_file2 "attr_name2" $attr64k "s" "larp"
+test_attr_replay remote_file2 "attr_name2" $attr64k "r" "larp"
+
+# replace shortform with different value
+create_test_file sf_file 2 $attr64
+test_attr_replay sf_file "attr_name2" $attr16 "s" "larp"
+
+# replace leaf with different value
+create_test_file leaf_file 3 $attr1k
+test_attr_replay leaf_file "attr_name2" $attr256 "s" "larp"
+
+# replace node with a different value
+create_test_file node_file 1 $attr64k
+$ATTR_PROG -s "attr_name2" -V $attr1k $testdir/node_file \
+               >> $seqres.full
+test_attr_replay node_file "attr_name2" $attr256 "s" "larp"
+
+echo "*** done"
+status=0
+exit
diff --git a/tests/xfs/018.out b/tests/xfs/018.out
new file mode 100644 (file)
index 0000000..ad8fd52
--- /dev/null
@@ -0,0 +1,149 @@
+QA output created by 018
+*** mkfs
+*** mount FS
+attr_set: Input/output error
+Could not set "attr_name" for SCRATCH_MNT/testdir/empty_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_file1': Input/output error
+attr_name: e889d82dd111d6315d7b1edce2b1b30f  -
+
+attr_remove: Input/output error
+Could not remove "attr_name" for SCRATCH_MNT/testdir/empty_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_file1': Input/output error
+attr_name: d41d8cd98f00b204e9800998ecf8427e  -
+
+attr_set: Input/output error
+Could not set "attr_nameX" for SCRATCH_MNT/testdir/empty_fileX
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_fileX': Input/output error
+attr_nameX: cb72c43fb97dd3cb4ac6ad2d9bd365e1  -
+
+attr_remove: Input/output error
+Could not remove "attr_nameX" for SCRATCH_MNT/testdir/empty_fileX
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_fileX': Input/output error
+attr_nameX: d41d8cd98f00b204e9800998ecf8427e  -
+
+attr_set: Input/output error
+Could not set "attr_name" for SCRATCH_MNT/testdir/empty_file2
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_file2': Input/output error
+attr_name: 4198214ee02e6ad7ac39559cd3e70070  -
+
+attr_remove: Input/output error
+Could not remove "attr_name" for SCRATCH_MNT/testdir/empty_file2
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_file2': Input/output error
+attr_name: d41d8cd98f00b204e9800998ecf8427e  -
+
+attr_set: Input/output error
+Could not set "attr_name" for SCRATCH_MNT/testdir/empty_file3
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_file3': Input/output error
+attr_name: 7f6fd1b6d872108bd44bd143cbcdfa19  -
+
+attr_remove: Input/output error
+Could not remove "attr_name" for SCRATCH_MNT/testdir/empty_file3
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_file3': Input/output error
+attr_name: d41d8cd98f00b204e9800998ecf8427e  -
+
+attr_set: Input/output error
+Could not set "attr_name" for SCRATCH_MNT/testdir/empty_fileY
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_fileY': Input/output error
+attr_name: 3ad10faae9dcfa4083f8dfa641688733  -
+
+attr_remove: Input/output error
+Could not remove "attr_name" for SCRATCH_MNT/testdir/empty_fileY
+touch: cannot touch 'SCRATCH_MNT/testdir/empty_fileY': Input/output error
+attr_name: d41d8cd98f00b204e9800998ecf8427e  -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/inline_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/inline_file1': Input/output error
+attr_name2: e889d82dd111d6315d7b1edce2b1b30f  -
+
+attr_remove: Input/output error
+Could not remove "attr_name2" for SCRATCH_MNT/testdir/inline_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/inline_file1': Input/output error
+attr_name2: d41d8cd98f00b204e9800998ecf8427e  -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/inline_file2
+touch: cannot touch 'SCRATCH_MNT/testdir/inline_file2': Input/output error
+attr_name2: 4198214ee02e6ad7ac39559cd3e70070  -
+
+attr_remove: Input/output error
+Could not remove "attr_name2" for SCRATCH_MNT/testdir/inline_file2
+touch: cannot touch 'SCRATCH_MNT/testdir/inline_file2': Input/output error
+attr_name2: d41d8cd98f00b204e9800998ecf8427e  -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/inline_file3
+touch: cannot touch 'SCRATCH_MNT/testdir/inline_file3': Input/output error
+attr_name2: 7f6fd1b6d872108bd44bd143cbcdfa19  -
+
+attr_remove: Input/output error
+Could not remove "attr_name2" for SCRATCH_MNT/testdir/inline_file3
+touch: cannot touch 'SCRATCH_MNT/testdir/inline_file3': Input/output error
+attr_name2: d41d8cd98f00b204e9800998ecf8427e  -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/extent_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/extent_file1': Input/output error
+attr_name2: 4198214ee02e6ad7ac39559cd3e70070  -
+
+attr_remove: Input/output error
+Could not remove "attr_name2" for SCRATCH_MNT/testdir/extent_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/extent_file1': Input/output error
+attr_name2: d41d8cd98f00b204e9800998ecf8427e  -
+
+attr_set: Input/output error
+Could not set "attr_name4" for SCRATCH_MNT/testdir/extent_file2
+touch: cannot touch 'SCRATCH_MNT/testdir/extent_file2': Input/output error
+attr_name4: 5bbe95fdc1361656e833390aefafceb6  -
+
+attr_set: Input/output error
+Could not set "attr_name4" for SCRATCH_MNT/testdir/extent_file3
+touch: cannot touch 'SCRATCH_MNT/testdir/extent_file3': Input/output error
+attr_name4: 5bbe95fdc1361656e833390aefafceb6  -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/extent_file4
+touch: cannot touch 'SCRATCH_MNT/testdir/extent_file4': Input/output error
+attr_name2: 7f6fd1b6d872108bd44bd143cbcdfa19  -
+
+attr_remove: Input/output error
+Could not remove "attr_name2" for SCRATCH_MNT/testdir/extent_file4
+touch: cannot touch 'SCRATCH_MNT/testdir/extent_file4': Input/output error
+attr_name2: d41d8cd98f00b204e9800998ecf8427e  -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/remote_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/remote_file1': Input/output error
+attr_name2: 4198214ee02e6ad7ac39559cd3e70070  -
+
+attr_remove: Input/output error
+Could not remove "attr_name2" for SCRATCH_MNT/testdir/remote_file1
+touch: cannot touch 'SCRATCH_MNT/testdir/remote_file1': Input/output error
+attr_name2: d41d8cd98f00b204e9800998ecf8427e  -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/remote_file2
+touch: cannot touch 'SCRATCH_MNT/testdir/remote_file2': Input/output error
+attr_name2: 7f6fd1b6d872108bd44bd143cbcdfa19  -
+
+attr_remove: Input/output error
+Could not remove "attr_name2" for SCRATCH_MNT/testdir/remote_file2
+touch: cannot touch 'SCRATCH_MNT/testdir/remote_file2': Input/output error
+attr_name2: d41d8cd98f00b204e9800998ecf8427e  -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/sf_file
+touch: cannot touch 'SCRATCH_MNT/testdir/sf_file': Input/output error
+attr_name2: e43df9b5a46b755ea8f1b4dd08265544  -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/leaf_file
+touch: cannot touch 'SCRATCH_MNT/testdir/leaf_file': Input/output error
+attr_name2: 5bbe95fdc1361656e833390aefafceb6  -
+
+attr_set: Input/output error
+Could not set "attr_name2" for SCRATCH_MNT/testdir/node_file
+touch: cannot touch 'SCRATCH_MNT/testdir/node_file': Input/output error
+attr_name2: 5bbe95fdc1361656e833390aefafceb6  -
+
+*** done
index d47da0d646bafbb0c79fb8e82150fff68993d053..ef5dc4fa367e658f5324fb203428969f87d66bc6 100755 (executable)
@@ -45,7 +45,7 @@ _check_root_inos()
 #
 _filter_bad_ids()
 {
-       egrep -v 'bad user id 0xffffffff|bad group id 0xffffffff'
+       grep -E -v 'bad user id 0xffffffff|bad group id 0xffffffff'
 }
 
 # real QA test starts here
index 05de5578ffae7d8191517e52e09f1a024c01b9c8..21b3afe7ce63a4b4c6e914490e85340f21a2a4e3 100755 (executable)
@@ -46,6 +46,9 @@ bsize=`_scratch_mkfs_xfs -dsize=${agsize}m,agcount=1 2>&1 | _filter_mkfs 2>&1 \
 onemeginblocks=`expr 1048576 / $bsize`
 _scratch_mount
 
+# We're growing the data device, so force new file creation there
+_xfs_force_bdev data $SCRATCH_MNT
+
 echo "done"
 
 # full allocation group -> partial; partial -> expand partial + new partial;
index d62eb045fc859a4a6693640e6991f26413567fe4..4433d577e2132167c1852a93241e0de64dec210f 100755 (executable)
@@ -11,7 +11,7 @@
 set +x
 
 . ./common/preamble
-_begin_fstest fsr ioctl auto
+_begin_fstest fsr ioctl auto prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -51,14 +51,14 @@ _require_scratch
 
 _do_die_on_error=message_only
 
-echo -n "Make a 48 megabyte filesystem on SCRATCH_DEV and mount... "
-_scratch_mkfs_xfs -dsize=48m,agcount=3 2>&1 >/dev/null || _fail "mkfs failed"
+echo -n "Make a 96 megabyte filesystem on SCRATCH_DEV and mount... "
+_scratch_mkfs_xfs -dsize=96m,agcount=3 2>&1 >/dev/null || _fail "mkfs failed"
 _scratch_mount
 
 echo "done"
 
-echo -n "Reserve 16 1Mb unfragmented regions... "
-for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+echo -n "Reserve 32 1Mb unfragmented regions... "
+for i in `seq 1 32`
 do
        _do "$XFS_IO_PROG -f -c \"resvsp 0 1m\" $SCRATCH_MNT/hole$i"
        _do "$XFS_IO_PROG -f -c \"resvsp 0 4k\" $SCRATCH_MNT/space$i"
@@ -68,7 +68,7 @@ echo "done"
 
 # set up filesystem
 echo -n "Fill filesystem with fill file... "
-for i in `seq 0 1 31`; do
+for i in `seq 0 1 63`; do
        _do "$XFS_IO_PROG -f -c \"falloc ${i}m 1m\" $SCRATCH_MNT/fill"
 done
 _do "xfs_bmap -vp $SCRATCH_MNT/fill"
@@ -83,7 +83,7 @@ echo "done"
 # create fragmented file
 #_do "Delete every second file" "_cull_files"
 echo -n "Punch every second 4k block... "
-for i in `seq 0 8 32768`; do
+for i in `seq 0 8 65536`; do
        # This generates excessive output that significantly slows down the
        # test. It's not necessary for debug, so just bin it.
        $XFS_IO_PROG -f -c "unresvsp ${i}k 4k" $SCRATCH_MNT/fill \
@@ -94,7 +94,7 @@ _do "sum $SCRATCH_MNT/fill >$tmp.fillsum1"
 echo "done"
 
 echo -n "Create one very large file... "
-_do "$here/src/fill2 -d nbytes=16000000,file=$SCRATCH_MNT/fragmented"
+_do "$here/src/fill2 -d nbytes=32000000,file=$SCRATCH_MNT/fragmented"
 echo "done"
 _do "xfs_bmap -v $SCRATCH_MNT/fragmented"
 _do "sum $SCRATCH_MNT/fragmented >$tmp.sum1"
index a25885b6f394f1ca6a897c83423c838a1d6ca77c..e7ec3d4193aa8891f31df3fda819da7a115662c6 100644 (file)
@@ -1,6 +1,6 @@
 QA output created by 042
-Make a 48 megabyte filesystem on SCRATCH_DEV and mount... done
-Reserve 16 1Mb unfragmented regions... done
+Make a 96 megabyte filesystem on SCRATCH_DEV and mount... done
+Reserve 32 1Mb unfragmented regions... done
 Fill filesystem with fill file... done
 Use up any further available space... done
 Punch every second 4k block... done
index ea70cb50b25296cdd4abe0607cf93d9129b08471..aca867c9402f1175740fcbe84ef34fd06d2c9a53 100755 (executable)
@@ -38,7 +38,7 @@ _scratch_mount
 
 # Start a workload and shutdown the fs. The subsequent mount will require log
 # recovery.
-$FSSTRESS_PROG -n 9999 -p 2 -w -d $SCRATCH_MNT > /dev/null 2>&1 &
+$FSSTRESS_PROG -n 9999 -p 2 -w -d $SCRATCH_MNT &>> $seqres.full &
 sleep 5
 _scratch_shutdown -f
 $KILLALL_PROG -q $FSSTRESS_PROG
index 9fb3f406811084937b9bba3a8dd82ef63f6250f1..6af14c802ca920ac35c75efdff6d1a7616d48377 100755 (executable)
@@ -56,7 +56,7 @@ _scratch_mkfs_sized $((1024 * 1024 * 500)) >> $seqres.full 2>&1 ||
 _scratch_mount
 
 # populate the fs with some data and cycle the mount to reset the log head/tail
-$FSSTRESS_PROG -d $SCRATCH_MNT -z -fcreat=1 -p 4 -n 100000 > /dev/null 2>&1
+$FSSTRESS_PROG -d $SCRATCH_MNT -z -fcreat=1 -p 4 -n 100000 >> $seqres.full
 _scratch_cycle_mount || _fail "cycle mount failed"
 
 # Pin the tail and start a file removal workload. File removal tends to
index a81b226b0be18374629a3e684c19f61366ea418b..f67c29fd4b9ffbe0ac18a31f832a50cbb80e5fb6 100755 (executable)
@@ -31,7 +31,7 @@ _ls_size_filter()
     # Filter out the housekeeping files of xfsrestore
     #
     $AWK_PROG 'NF == 9 { print $5, $9 }' |\
-    egrep -v 'dumpdir|housekeeping|dirattr|dirextattr|namreg|state|tree' 
+    grep -E -v 'dumpdir|housekeeping|dirattr|dirextattr|namreg|state|tree'
 }
 
 # real QA test starts here
@@ -48,7 +48,7 @@ while [ $i -le 9 ]; do
     if [ $i -gt 0 ]; then
        sleep 2
        _modify_level $i
-    fi 
+    fi
 
     _stable_fs
     sleep 2
index 8485dee6a1d8287e5c266469855ffe6e2d3d4513..819d385eaec357d92d69ae6987fe5fa3a5675a50 100755 (executable)
@@ -37,7 +37,7 @@ _list_dir()
     find $__dir  -exec $here/src/lstat64 -t {} \; |\
     sed -e 's/.*dumpdir/dumpdir/' -e '/^dumpdir /d' |\
     sed -e 's/.*restoredir/restoredir/' -e '/^restoredir /d' |\
-    egrep -v 'housekeeping|dirattr|dirextattr|namreg|state|tree' |\
+    grep -E -v 'housekeeping|dirattr|dirextattr|namreg|state|tree' |\
     awk '$3 ~ /^d/ { $2 = "XXX" } {print}' |\
     LC_COLLATE=POSIX sort
 } 
index bf4aa20242fb6516c67a9562516fa26b6ecfe339..b3074e254cee4b399a81c479debddd0a8631d811 100755 (executable)
@@ -22,6 +22,7 @@ _require_scratch
 
 _scratch_mkfs_xfs >/dev/null 2>&1
 _scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 8388608
 
 small=$SCRATCH_MNT/small
 big=$SCRATCH_MNT/big
index 8eef136775fcd9c1d6190d7a14f14d744bdb2f41..a6ec0568b922114551c2129bdafc9e058c476d3a 100755 (executable)
@@ -18,7 +18,7 @@
 # inodes (.i.e., free space) have been consumed.
 #
 . ./common/preamble
-_begin_fstest auto enospc punch
+_begin_fstest auto enospc punch prealloc
 
 # Import common functions.
 . ./common/filter
@@ -69,7 +69,7 @@ _require_xfs_sparse_inodes
 # bitmap consuming all the free space in our small data device.
 unset SCRATCH_RTDEV
 
-_scratch_mkfs "-d size=50m -m crc=1 -i sparse" | tee -a $seqres.full |
+_scratch_mkfs "-d size=96m -m crc=1 -i sparse" | tee -a $seqres.full |
        _filter_mkfs > /dev/null 2> $tmp.mkfs
 . $tmp.mkfs    # for isize
 
index 20b20a08ebb8e71a734de8ea8e2313b6ad64c9e2..2d90b6c450d02b5ecfa2bad0589b4d6ae4777c5c 100755 (executable)
@@ -7,7 +7,7 @@
 # rwtest (iogen|doio)
 #
 . ./common/preamble
-_begin_fstest rw ioctl
+_begin_fstest rw ioctl auto quick
 
 # Import common functions.
 . ./common/filter
@@ -21,6 +21,7 @@ _cleanup()
 
 _supported_fs xfs
 _require_test
+_require_xfs_io_command falloc # iogen requires falloc
 
 quiet=-q
 clean=-c
diff --git a/tests/xfs/081 b/tests/xfs/081
new file mode 100755 (executable)
index 0000000..3af7377
--- /dev/null
@@ -0,0 +1,80 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Red Hat, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 081
+#
+# This's a regression test for:
+#   a1de97fe296c ("xfs: Fix the free logic of state in xfs_attr_node_hasname")
+#
+# After we corrupted an attr leaf block (under node block), getxattr might hit
+# EFSCORRUPTED in xfs_attr_node_get when it does xfs_attr_node_hasname. A bug
+# cause xfs_attr_node_get won't do xfs_buf_trans release job, then a subsequent
+# removexattr will hang.
+#
+. ./common/preamble
+_begin_fstest auto quick attr
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+. ./common/populate
+
+# real QA test starts here
+_supported_fs xfs
+_fixed_by_kernel_commit a1de97fe296c \
+       "xfs: Fix the free logic of state in xfs_attr_node_hasname"
+
+_require_scratch_nocheck
+# Only test with v5 xfs on-disk format
+_require_scratch_xfs_crc
+_require_attrs
+_require_populate_commands
+_require_xfs_db_blocktrash_z_command
+
+_scratch_mkfs_xfs | _filter_mkfs >$seqres.full 2>$tmp.mkfs
+source $tmp.mkfs
+_scratch_mount
+
+# Create more than $((dbsize / 32)) xattr entries to force the creation of a
+# node block and two (or more) leaf blocks, which we need for this test.
+nr_xattr="$((dbsize / 32))"
+localfile="${SCRATCH_MNT}/attrfile"
+touch $localfile
+for ((i=0; i<nr_xattr; i++));do
+       $SETFATTR_PROG -n user.x$(printf "%.09d" "$i") -v "aaaaaaaaaaaaaaaa" $localfile
+done
+inumber="$(stat -c '%i' $localfile)"
+_scratch_unmount
+
+# Expect the ablock 0 is a node block, later ablocks(>=1) are leaf blocks, then corrupt
+# the last leaf block. (Don't corrupt node block, or can't reproduce the bug)
+magic=$(_scratch_xfs_get_metadata_field "hdr.info.hdr.magic" "inode $inumber" "ablock 0")
+level=$(_scratch_xfs_get_metadata_field "hdr.level" "inode $inumber" "ablock 0")
+count=$(_scratch_xfs_get_metadata_field "hdr.count" "inode $inumber" "ablock 0")
+if [ "$magic" = "0x3ebe" -a "$level" = "1" ];then
+       # Corrupt the last leaf block
+       _scratch_xfs_db -x -c "inode ${inumber}" -c "ablock $count" -c "stack" \
+               -c "blocktrash -x 32 -y $((dbsize*8)) -3 -z" >> $seqres.full
+else
+       _fail "The ablock 0 (magic=$magic, level=$level) isn't a root node block, maybe case issue"
+fi
+
+# This's the real testing, expect removexattr won't hang or panic.
+if _try_scratch_mount >> $seqres.full 2>&1; then
+       for ((i=0; i<nr_xattr; i++));do
+               $GETFATTR_PROG -n user.x$(printf "%.09d" "$i") $localfile >/dev/null 2>&1
+               $SETFATTR_PROG -x user.x$(printf "%.09d" "$i") $localfile 2>/dev/null
+       done
+else
+       # Due to we corrupt the xfs manually, so can't be sure if xfs can
+       # detect this corruption and refuse the mount directly in one day.
+       # If so, it's not a testing fail, so "notrun" directly to mark this
+       # test isn't really done
+       _notrun "XFS refused to mount with this xattr corruption, test skipped"
+fi
+
+echo "Silence is golden"
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/081.out b/tests/xfs/081.out
new file mode 100644 (file)
index 0000000..663a886
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 081
+Silence is golden
diff --git a/tests/xfs/082 b/tests/xfs/082
new file mode 100755 (executable)
index 0000000..cc6bfa6
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 082
+#
+# Regression test for xfsprogs commit:
+#
+# XXXXXXXX ("xfs_copy: don't use cached buffer reads until after libxfs_mount")
+#
+# It was discovered that passing xfs_copy a source device containing an ext4
+# filesystem would cause xfs_copy to crash.  Further investigation revealed
+# that any readable path that didn't have a plausible XFS superblock in block
+# zero would produce the same crash, so this regression test exploits that.
+#
+. ./common/preamble
+_begin_fstest auto copy quick
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_xfs_copy
+_require_test
+
+rm -f $TEST_DIR/$seq.*
+$XFS_IO_PROG -f -c 'truncate 100m' $TEST_DIR/$seq.a
+$XFS_IO_PROG -f -c 'truncate 100m' $TEST_DIR/$seq.b
+
+filter_copy() {
+       sed -e 's/Superblock has bad magic number.*/bad magic number/'
+}
+
+$XFS_COPY_PROG $TEST_DIR/$seq.a $TEST_DIR/$seq.b 2>&1 | filter_copy
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/082.out b/tests/xfs/082.out
new file mode 100644 (file)
index 0000000..edf5936
--- /dev/null
@@ -0,0 +1,3 @@
+QA output created by 082
+bad magic number
+xfs_copy: couldn't read superblock, error=22
index a9acc9f560eefa03f7fc2759487b5107cb9af8fb..edab3b7b092222cf5800eccff2bf2182f70fd6f9 100755 (executable)
@@ -145,7 +145,7 @@ cat "$ROUND2_LOG" >> $seqres.full
 echo "++ check fs (2)" >> $seqres.full
 _repair_scratch_fs >> $seqres.full 2>&1
 
-egrep -q '(did not fix|makes no progress)' $seqres.full && echo "xfs_repair failed" | tee -a $seqres.full
+grep -E -q '(did not fix|makes no progress)' $seqres.full && echo "xfs_repair failed" | tee -a $seqres.full
 if [ "$(wc -l < "$ROUND2_LOG")" -ne 8 ]; then
        echo "xfs_repair did not fix everything" | tee -a $seqres.full
 fi
index e796fec4ac22bcd999de7260e496c002159878a5..ab7343556098cd2286def1508554b58e52a5c273 100755 (executable)
@@ -8,7 +8,7 @@
 # for data corruption (zeroes read) near the end of file.
 #
 . ./common/preamble
-_begin_fstest ioctl rw auto
+_begin_fstest ioctl rw auto prealloc
 
 # Import common functions.
 . ./common/filter
index 4cad7216cd4f2a6048b6793d3ef1b008d937ee1c..1df34eeddcf770c89d279777853dd2f30a4a88ac 100755 (executable)
@@ -42,7 +42,7 @@ _scratch_mkfs_xfs -m crc=1,finobt=1 > /dev/null
 
 echo "+ mount fs image"
 _scratch_mount
-$XFS_INFO_PROG "${SCRATCH_MNT}" | grep -q "finobt=1" || _notrun "finobt not enabled"
+_require_xfs_has_feature "$SCRATCH_MNT" finobt
 blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
 
 echo "+ make some files"
index a7eaff6e0cec5f3ff76480ed42b2a145fb989a52..82bef8ad26f17962f7f5a3624ae8a56c5ecf8e83 100755 (executable)
@@ -37,7 +37,7 @@ _scratch_mkfs_xfs > /dev/null
 
 echo "+ mount fs image"
 _scratch_mount
-dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
 nr="$((dblksz / 40))"
 blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
 leaf_lblk="$((32 * 1073741824 / blksz))"
index 79da8cb02c5c01bb58164b2a198ca478bcd9dc05..e638b4ba1750159f9fbef8c7c939d9da04c52e89 100755 (executable)
@@ -37,7 +37,7 @@ _scratch_mkfs_xfs > /dev/null
 
 echo "+ mount fs image"
 _scratch_mount
-dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
 nr="$((dblksz / 12))"
 blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
 leaf_lblk="$((32 * 1073741824 / blksz))"
index 64f4705aca46700b8fb2c66cbfd1ffe9341dc0d5..11ed329110f9eba1d3d2da4e03e5655d22074ec1 100755 (executable)
@@ -37,7 +37,7 @@ _scratch_mkfs_xfs > /dev/null
 
 echo "+ mount fs image"
 _scratch_mount
-dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
 nr="$((dblksz / 12))"
 blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
 leaf_lblk="$((32 * 1073741824 / blksz))"
index 24dce43058b3f5bb2b7f1f37eecaf55db0d0b333..43f453918189bfe5d638b90473a329a3fda295af 100755 (executable)
@@ -37,7 +37,7 @@ _scratch_mkfs_xfs > /dev/null
 
 echo "+ mount fs image"
 _scratch_mount
-dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
 nr="$((16 * dblksz / 40))"
 blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
 leaf_lblk="$((32 * 1073741824 / blksz))"
index 22a8bf9fb0c9a5364bee023c05156b8e7a8eb8f6..002a712883d6b4fb966342ba8fb4b3db1b2870d6 100755 (executable)
@@ -37,7 +37,7 @@ _scratch_mkfs_xfs > /dev/null
 
 echo "+ mount fs image"
 _scratch_mount
-dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
 nr="$((16 * dblksz / 40))"
 blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
 leaf_lblk="$((32 * 1073741824 / blksz))"
index fc2281af52cb9f85ed297f807832aa252ec001e0..388873bdee6c051dc1d60e7f9bb11ced01e1462e 100755 (executable)
@@ -59,13 +59,13 @@ clean_files()
 
 filter_quot()
 {
-       _filter_quota | grep -v "root \|\#0 " \
+       _filter_quota | grep -v "root \|#0 " \
                | sed -e '/#[0-9]*/s/#[0-9]*/#ID/g'
 }
 
 filter_report()
 {
-       _filter_quota | grep -v "^root \|^\#0 " \
+       _filter_quota | grep -v "^root \|^#0 " \
                | sed -e '/^#[0-9]*/s/^#[0-9]*/#ID/g'
 }
 
index 4607000544c0314dd0513b8ed32af99710261399..8593edbdd223efe6720423bd31584536b7701b5f 100755 (executable)
@@ -32,6 +32,14 @@ test_files()
        done
 }
 
+# Some filesystem configurations fragment the file mapping more than others,
+# which leads to the quota block counts being slightly higher than the 48MB
+# written.
+filter_quota()
+{
+       sed -e 's/48\.[01]M/48M/g' | _filter_quota
+}
+
 test_accounting()
 {
        echo "### some controlled buffered, direct and mmapd IO (type=$type)"
@@ -49,9 +57,9 @@ test_accounting()
                $here/src/lstat64 $file | head -3 | _filter_scratch
        done
        $XFS_IO_PROG -c syncfs $SCRATCH_MNT
-       $XFS_QUOTA_PROG -c "quota -hnb -$type $id" $QARGS | _filter_quota
-       $XFS_QUOTA_PROG -c "quota -hni -$type $id" $QARGS | _filter_quota
-       $XFS_QUOTA_PROG -c "quota -hnr -$type $id" $QARGS | _filter_quota
+       $XFS_QUOTA_PROG -c "quota -hnb -$type $id" $QARGS | filter_quota
+       $XFS_QUOTA_PROG -c "quota -hni -$type $id" $QARGS | filter_quota
+       $XFS_QUOTA_PROG -c "quota -hnr -$type $id" $QARGS | filter_quota
 }
 
 export MOUNT_OPTIONS="-opquota"
index 6cb6917af6f51f0290100426c7421f7490e75708..e3e491f1df22e8c469de4fdbb09439442926de70 100755 (executable)
@@ -78,7 +78,7 @@ if [ -n "$FASTSTART" -a -f $SCRATCH_MNT/f0 ]; then
 fi
 _scratch_unmount
 
-_scratch_mkfs_xfs -dsize=160m,agcount=4 $faststart | _filter_mkfs 2>$tmp.mkfs
+_scratch_mkfs_xfs -dsize=320m,agcount=4 $faststart | _filter_mkfs 2>$tmp.mkfs
 cat $tmp.mkfs >>$seqres.full
 _scratch_mount
 
index bc1ab628950807cc7a1945edcb325a27d2922d74..e2d5932da6fa7d1f9fe8142ba1aa93511c502ac1 100755 (executable)
@@ -37,7 +37,7 @@ _scratch_mkfs_xfs > /dev/null
 
 echo "+ mount fs image"
 _scratch_mount
-dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
 nr="$((16 * dblksz / 40))"
 blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
 leaf_lblk="$((32 * 1073741824 / blksz))"
index e820ed96da9339d6509c2d57cf5b6d702af74394..9bb2cd304bd73e90e501ffaa99750a35f74caeea 100755 (executable)
@@ -37,7 +37,7 @@ _scratch_mkfs_xfs > /dev/null
 
 echo "+ mount fs image"
 _scratch_mount
-dblksz="$($XFS_INFO_PROG "${SCRATCH_MNT}" | grep naming.*bsize | sed -e 's/^.*bsize=//g' -e 's/\([0-9]*\).*$/\1/g')"
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
 nr="$((128 * dblksz / 40))"
 blksz="$(stat -f -c '%s' "${SCRATCH_MNT}")"
 leaf_lblk="$((32 * 1073741824 / blksz))"
index a0ea1d13451b4158b751e10083824af228194491..0e8a0529abdce5c989d0bac52cca34afc3bfe0bc 100755 (executable)
@@ -9,7 +9,7 @@
 # extents on either side of the collapse area are mergeable.
 #
 . ./common/preamble
-_begin_fstest auto quick clone rmap collapse insert
+_begin_fstest auto quick clone rmap collapse insert prealloc
 
 # Import common functions.
 . ./common/filter
@@ -18,6 +18,8 @@ _begin_fstest auto quick clone rmap collapse insert
 # real QA test starts here
 _supported_fs xfs
 _require_test_program "punch-alternating"
+_require_cp_reflink
+_require_scratch_reflink
 _require_xfs_scratch_rmapbt
 _require_xfs_io_command "falloc"
 _require_xfs_io_command "fcollapse"
@@ -35,6 +37,8 @@ len1=$((blocks1 * blksz))
 len2=$((blocks2 * blksz))
 file_blksz=$(_get_file_block_size $SCRATCH_MNT)
 
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
+
 echo "Create some files"
 $XFS_IO_PROG -f \
        -c "falloc 0 $len1" \
index 03755b286ee5acaaf76e6d526acd41e06e8bef95..6bb81a3abc3919dc64b47d9d3b53fae50e703f32 100755 (executable)
@@ -16,7 +16,7 @@
 # ip->i_df.if_bytes not ip->i_d.di_nextents in xfs_swap_extent_forks
 #
 . ./common/preamble
-_begin_fstest auto quick fsr
+_begin_fstest auto quick fsr prealloc
 
 # Import common functions.
 . ./common/filter
index a118037115c4b2e2c85b9a0539483c05a80927bc..5ffbce25c6a5f7ffbde4ccec39289de44d246051 100755 (executable)
 . ./common/preamble
 _begin_fstest log v2log auto freeze
 
+# Override the default cleanup function.
+_cleanup()
+{
+       # Make sure $SCRATCH_MNT is unfreezed
+       xfs_freeze -u $SCRATCH_MNT 2>/dev/null
+       cd /
+       rm -f $tmp.*
+}
+
 # Import common functions.
 . ./common/filter
 
@@ -20,6 +29,7 @@ _begin_fstest log v2log auto freeze
 _supported_fs xfs
 
 _require_scratch
+_require_freeze
 
 # this may hang
 sync
index 520061508803eb76fb2e572d5b3f57ffe0cd8be3..4e5ba1dfee757b479e425a465e0d7a60cdd7dca6 100755 (executable)
@@ -17,17 +17,30 @@ _begin_fstest other auto quick clone realtime
 _supported_fs xfs
 _require_command "$INDENT_PROG" indent
 
+# Starting in Linux 6.1, the EFI log formats were adjusted away from using
+# single-element arrays as flex arrays.
+_wants_kernel_commit 03a7485cd701 \
+       "xfs: fix memcpy fortify errors in EFI log format copying"
+
 # filter out known changes to xfs type sizes
 _type_size_filter()
 {
        # lazy SB adds __be32 agf_btreeblks - pv960372
+       # flexarray conversion of the attr structures in Linux 6.5 changed
+       # the sizeof output
        if [ "$($MKFS_XFS_PROG 2>&1 | grep -c lazy-count )" == "0" ]; then
                perl -ne '
 s/sizeof\( xfs_agf_t \) = 60/sizeof( xfs_agf_t ) = <SIZE>/;
+s/sizeof\(struct xfs_attr3_leafblock\) = 80/sizeof(struct xfs_attr3_leafblock) = 88/;
+s/sizeof\(struct xfs_attr_shortform\) = 4/sizeof(struct xfs_attr_shortform) = 8/;
+s/sizeof\(xfs_attr_leafblock_t\) = 32/sizeof(xfs_attr_leafblock_t) = 40/;
                print;'
        else
                perl -ne '
 s/sizeof\( xfs_agf_t \) = 64/sizeof( xfs_agf_t ) = <SIZE>/;
+s/sizeof\(struct xfs_attr3_leafblock\) = 80/sizeof(struct xfs_attr3_leafblock) = 88/;
+s/sizeof\(struct xfs_attr_shortform\) = 4/sizeof(struct xfs_attr_shortform) = 8/;
+s/sizeof\(xfs_attr_leafblock_t\) = 32/sizeof(xfs_attr_leafblock_t) = 40/;
                print;'
        fi
 }
@@ -182,11 +195,11 @@ echo 'int main(int argc, char *argv[]) {' >>$cprog
 #
 cat /usr/include/xfs/xfs*.h | indent |\
 _attribute_filter |\
-egrep '(} *xfs_.*_t|^struct xfs_[a-z0-9_]*$)' |\
-egrep -v -f $tmp.ignore |\
+grep -E '(} *xfs_.*_t|^(union|struct) xfs_[a-z0-9_]*$)' |\
+grep -E -v -f $tmp.ignore |\
 sed -e 's/^.*}[[:space:]]*//g' -e 's/;.*$//g' -e 's/_t, /_t\n/g' |\
 sort | uniq |\
-awk '{printf("printf(\"sizeof(%s) = \\%zu\\n\", sizeof(%s));\n", $0, $0);}' |\
+awk '{printf("printf(\"sizeof(%s) = %%zu\\n\", sizeof(%s));\n", $0, $0);}' |\
 cat >> $cprog
 
 #
@@ -199,7 +212,7 @@ awk '
    /typedef struct xfs_sb/ { structon = 1; next }
    structon && $2 ~ /^sb_/ { sub(/[;,]/,"",$2)
                              sub(/XFSLABEL_MAX/,"12",$2)
-                             printf("printf(\"offsetof(xfs_sb_t, %s) = \\%zu\\n\", offsetof(xfs_sb_t, %s));", $2, $2); next}
+                             printf("printf(\"offsetof(xfs_sb_t, %s) = %%zu\\n\", offsetof(xfs_sb_t, %s));", $2, $2); next}
    structon && /}/ { structon = 0; next}
 '>>$cprog
 
index a56cbee84ff8500d1fb786be3ac88db77a9311ce..a2b57cfb9b03e0b168cc3b97e9846ea22004ccd5 100644 (file)
@@ -62,6 +62,8 @@ sizeof(struct xfs_agfl) = 36
 sizeof(struct xfs_attr3_leaf_hdr) = 80
 sizeof(struct xfs_attr3_leafblock) = 88
 sizeof(struct xfs_attr3_rmt_hdr) = 56
+sizeof(struct xfs_attr_sf_entry) = 3
+sizeof(struct xfs_attr_sf_hdr) = 4
 sizeof(struct xfs_attr_shortform) = 8
 sizeof(struct xfs_attrd_log_format) = 16
 sizeof(struct xfs_attri_log_format) = 40
@@ -90,6 +92,7 @@ sizeof(struct xfs_disk_dquot) = 104
 sizeof(struct xfs_dqblk) = 136
 sizeof(struct xfs_dsb) = 264
 sizeof(struct xfs_dsymlink_hdr) = 56
+sizeof(struct xfs_exch_range) = 120
 sizeof(struct xfs_extent_data) = 24
 sizeof(struct xfs_extent_data_info) = 32
 sizeof(struct xfs_fs_eofblocks) = 128
@@ -117,7 +120,12 @@ sizeof(struct xfs_rtrmap_root) = 4
 sizeof(struct xfs_rud_log_format) = 16
 sizeof(struct xfs_rui_log_format) = 16
 sizeof(struct xfs_scrub_metadata) = 64
+sizeof(struct xfs_swap_extent) = 64
+sizeof(struct xfs_sxd_log_format) = 16
+sizeof(struct xfs_sxi_log_format) = 80
 sizeof(struct xfs_unmount_log_format) = 8
+sizeof(union xfs_rtword_raw) = 4
+sizeof(union xfs_suminfo_raw) = 4
 sizeof(xfs_agf_t) = 224
 sizeof(xfs_agfl_t) = 36
 sizeof(xfs_agi_t) = 344
@@ -161,10 +169,10 @@ sizeof(xfs_disk_dquot_t) = 104
 sizeof(xfs_dq_logformat_t) = 24
 sizeof(xfs_dqblk_t) = 136
 sizeof(xfs_dsb_t) = 264
-sizeof(xfs_efd_log_format_32_t) = 28
-sizeof(xfs_efd_log_format_64_t) = 32
-sizeof(xfs_efi_log_format_32_t) = 28
-sizeof(xfs_efi_log_format_64_t) = 32
+sizeof(xfs_efd_log_format_32_t) = 16
+sizeof(xfs_efd_log_format_64_t) = 16
+sizeof(xfs_efi_log_format_32_t) = 16
+sizeof(xfs_efi_log_format_64_t) = 16
 sizeof(xfs_error_injection_t) = 8
 sizeof(xfs_exntfmt_t) = 4
 sizeof(xfs_exntst_t) = 4
index db5d9a60db18d0e1a12f8ed5e1f423419d052ac1..8c1663c6c5c8d7ee9b59871f14180f81bb4e5651 100755 (executable)
@@ -7,7 +7,7 @@
 # Ensure that xfs_fsr un-reflinks files while defragmenting
 #
 . ./common/preamble
-_begin_fstest auto quick clone fsr
+_begin_fstest auto quick clone fsr prealloc
 
 # Import common functions.
 . ./common/filter
@@ -34,7 +34,20 @@ margin=160
 blksz=65536
 real_blksz="$(_get_block_size $testdir)"
 blksz_factor=$((blksz / real_blksz))
+
+# The expected free space numbers in this test require file1 and file4 to share
+# the same blocks at the end of the test.  Therefore, we need the allocator to
+# give file1 a single extent at the start of the test so that fsr will not be
+# tempted to "defragment" a multi-extent file1 or file4.  Defragmenting really
+# means rewriting the file, and if that succeeds on either file, we'll have
+# unshared the space and there will be too little free space.  Therefore,
+# preallocate space to try to produce a single extent.
+$XFS_IO_PROG -f -c "falloc 0 $((blks * blksz))" $testdir/file1 >> $seqres.full
 _pwrite_byte 0x61 0 $((blks * blksz)) $testdir/file1 >> $seqres.full
+sync
+
+nextents=$($XFS_IO_PROG -c 'stat' $testdir/file1 | grep 'fsxattr.nextents' | awk '{print $3}')
+
 _cp_reflink $testdir/file1 $testdir/file2
 _cp_reflink $testdir/file2 $testdir/file3
 _cp_reflink $testdir/file3 $testdir/file4
@@ -68,7 +81,7 @@ c13=$(_md5_checksum $testdir/file3)
 c14=$(_md5_checksum $testdir/file4)
 
 echo "Defragment"
-lsattr -l $testdir/ | _filter_scratch | _filter_spaces
+lsattr -l $testdir/ | _filter_scratch | _filter_spaces | sed -e 's/DAX/---/g'
 $XFS_FSR_PROG -v -d $testdir/file1 >> $seqres.full
 $XFS_FSR_PROG -v -d $testdir/file2 >> $seqres.full # fsr probably breaks the link
 $XFS_FSR_PROG -v -d $testdir/file3 >> $seqres.full # fsr probably breaks the link
@@ -106,10 +119,23 @@ test $c14 = $c24 || echo "File4 changed by defrag"
 
 #echo $free_blocks0 $free_blocks1 $free_blocks2 $free_blocks3
 
-_within_tolerance "free blocks after creating some reflink copies" $free_blocks1 $((free_blocks0 - (blks * blksz_factor) )) $margin -v
-_within_tolerance "free blocks after CoW some reflink copies" $free_blocks2 $((free_blocks1 - 2)) $margin -v
-_within_tolerance "free blocks after defragging all reflink copies" $free_blocks3 $((free_blocks2 - (blks * 2 * blksz_factor))) $margin -v
-_within_tolerance "free blocks after all tests" $free_blocks3 $((free_blocks0 - (blks * 3 * blksz_factor))) $margin -v
+freesp_bad=0
+
+_within_tolerance "free blocks after creating some reflink copies" \
+       $free_blocks1 $((free_blocks0 - (blks * blksz_factor) )) $margin -v || freesp_bad=1
+
+_within_tolerance "free blocks after CoW some reflink copies" \
+       $free_blocks2 $((free_blocks1 - 2)) $margin -v || freesp_bad=1
+
+_within_tolerance "free blocks after defragging all reflink copies" \
+       $free_blocks3 $((free_blocks2 - (blks * 2 * blksz_factor))) $margin -v || freesp_bad=1
+
+_within_tolerance "free blocks after all tests" \
+       $free_blocks3 $((free_blocks0 - (blks * 3 * blksz_factor))) $margin -v || freesp_bad=1
+
+if [ $freesp_bad -ne 0 ] && [ $nextents -gt 1 ]; then
+       echo "free space checks probably failed because file1 nextents was $nextents"
+fi
 
 # success, all done
 status=0
index 90887d5476907d988003149e6336ad1cf9b81c0e..ec1a2ff3da1c68c869e274042a34450ac972baeb 100755 (executable)
@@ -16,24 +16,27 @@ _cleanup()
 {
     cd /
     _scratch_unmount > /dev/null 2>&1
-    rm -rf $tmp.* $testdir $metadump_file $TEST_DIR/image
+    _xfs_cleanup_verify_metadump
+    rm -rf $tmp.* $testdir
 }
 
 # Import common functions.
 . ./common/filter
 . ./common/reflink
+. ./common/metadump
 
 # real QA test starts here
 _supported_fs xfs
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
 _require_loop
 _require_scratch_reflink
+_xfs_setup_verify_metadump
 
 _scratch_mkfs >/dev/null 2>&1
 _scratch_mount
 
 testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
-metadump_file=$TEST_DIR/${seq}_metadump
 
 echo "Create the original file blocks"
 blksz="$(_get_file_block_size $testdir)"
@@ -46,18 +49,10 @@ seq 1 2 $((nr_blks - 1)) | while read nr; do
                        $testdir/file2 $((nr * blksz)) $blksz >> $seqres.full
 done
 
-echo "Create metadump file"
 _scratch_unmount
-_scratch_xfs_metadump $metadump_file
 
-# Now restore the obfuscated one back and take a look around
-echo "Restore metadump"
-xfs_mdrestore $metadump_file $TEST_DIR/image
-SCRATCH_DEV=$TEST_DIR/image _scratch_mount
-SCRATCH_DEV=$TEST_DIR/image _scratch_unmount
-
-echo "Check restored fs"
-_check_generic_filesystem $metadump_file
+echo "Create metadump file, restore it and check restored fs"
+_xfs_verify_metadumps
 
 # success, all done
 status=0
index da6f43fdd9094fc61c4e4514adf25571b61cd8d8..0f24c431fe68cfe9ac8d68bbd56b3c46aadcce84 100644 (file)
@@ -1,6 +1,4 @@
 QA output created by 129
 Create the original file blocks
 Reflink every other block
-Create metadump file
-Restore metadump
-Check restored fs
+Create metadump file, restore it and check restored fs
index 966ac03ad338402ff57367f919a63b2369d08533..c49d2d9ee0d611f5a32dd6eafaa988e9ac1a13f4 100755 (executable)
@@ -33,7 +33,7 @@ echo "Test with interactive"
 (echo "sb 0"; sleep 0.5;
  echo "p magicnum"; sleep 0.5;
  echo "source $tmp.a"; sleep 0.5;
- echo "p magicnum"; sleep 0.5) | _scratch_xfs_db 2>&1 | sed -e 's/xfs_db> //g' -e 's/0x58465342/XFS_MAGIC/g' | egrep '(This is file|magicnum =)'
+ echo "p magicnum"; sleep 0.5) | _scratch_xfs_db 2>&1 | sed -e 's/xfs_db> //g' -e 's/0x58465342/XFS_MAGIC/g' | grep -E '(This is file|magicnum =)'
 
 # success, all done
 status=0
diff --git a/tests/xfs/144 b/tests/xfs/144
new file mode 100755 (executable)
index 0000000..706aff6
--- /dev/null
@@ -0,0 +1,55 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 144
+#
+# Now that we've increased the default log size calculation, test mkfs with
+# various stripe units and filesystem sizes to see if we can provoke mkfs into
+# breaking.
+#
+. ./common/preamble
+_begin_fstest auto mkfs
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_test
+
+# The last testcase creates a (sparse) fs image with a 2GB log, so we need
+# 3GB to avoid failing the mkfs due to ENOSPC.
+_require_fs_space $TEST_DIR $((3 * 1048576))
+echo Silence is golden
+
+testfile=$TEST_DIR/a
+rm -f $testfile
+
+test_format() {
+       local tag="$1"
+       shift
+
+       echo "$tag" >> $seqres.full
+       $MKFS_XFS_PROG -f $@ -d file,name=$testfile &>> $seqres.full
+       local res=$?
+       test $res -eq 0 || echo "$tag FAIL $res" | tee -a $seqres.full
+}
+
+# First we try various small filesystems and stripe sizes.
+for M in `seq 298 302` `seq 490 520`; do
+       for S in `seq 32 4 64`; do
+               test_format "M=$M S=$S" -dsu=${S}k,sw=1,size=${M}m -N
+       done
+done
+
+# log end rounded beyond EOAG due to stripe unit
+test_format "log end beyond eoag" -d agcount=3200,size=6366g -d su=256k,sw=4 -N
+
+# Log so large it pushes the root dir into AG 1.  We can't use -N for the mkfs
+# because this check only occurs after the root directory has been allocated,
+# which mkfs -N doesn't do.
+test_format "log pushes rootdir into AG 1" -d agcount=3200,size=6366g -lagnum=0
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/144.out b/tests/xfs/144.out
new file mode 100644 (file)
index 0000000..e33e294
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 144
+Silence is golden
index d32e726e7e5582ff206d5efebff9486b14ae3a36..5fd8dcadfcb05ab95cf2d6b484cda7dcb6d63995 100755 (executable)
@@ -18,6 +18,9 @@ _begin_fstest auto quick quota
 
 # real QA test starts here
 _supported_fs xfs
+_fixed_by_kernel_commit 1aecf3734a95 \
+       "xfs: fix chown leaking delalloc quota blocks when fssetxattr fails"
+
 _require_command "$FILEFRAG_PROG" filefrag
 _require_test_program "chprojid_fail"
 _require_quota
index 5516d396bf7189e186cbb62f04a92d62b56c00c6..5090435976c83b916a52e9b137ec275ee86ddf14 100755 (executable)
@@ -15,7 +15,7 @@
 # error and returns EFSCORRUPTED.
 
 . ./common/preamble
-_begin_fstest auto quick rw realtime
+_begin_fstest auto quick rw realtime prealloc
 
 # Import common functions.
 . ./common/filter
@@ -24,6 +24,7 @@ _begin_fstest auto quick rw realtime
 _supported_fs xfs
 _require_scratch
 _require_realtime
+_require_xfs_io_command "falloc"
 _require_test_program "punch-alternating"
 
 # Format filesystem to get the block size
@@ -31,7 +32,7 @@ _scratch_mkfs > $seqres.full
 _scratch_mount >> $seqres.full
 
 blksz=$(_get_block_size $SCRATCH_MNT)
-rextsize=$($XFS_INFO_PROG $SCRATCH_MNT | grep realtime.*extsz | sed -e 's/^.*extsz=\([0-9]*\).*$/\1/g')
+rextsize=$(_xfs_get_rtextsize "$SCRATCH_MNT")
 rextblks=$((rextsize / blksz))
 
 echo "blksz $blksz rextsize $rextsize rextblks $rextblks" >> $seqres.full
index e21fdd330cb35314dcf2837d14465378fd57cd63..33b3c99633cfe29aed64ccca1acb1e2bb040703b 100755 (executable)
@@ -29,7 +29,7 @@ _scratch_mkfs -r extsize=256k > $seqres.full
 _scratch_mount >> $seqres.full
 
 blksz=$(_get_block_size $SCRATCH_MNT)
-rextsize=$($XFS_INFO_PROG $SCRATCH_MNT | grep realtime.*extsz | sed -e 's/^.*extsz=\([0-9]*\).*$/\1/g')
+rextsize=$(_xfs_get_rtextsize "$SCRATCH_MNT")
 rextblks=$((rextsize / blksz))
 
 echo "blksz $blksz rextsize $rextsize rextblks $rextblks" >> $seqres.full
index 66425f6710c1770890e154ef578e82b015a0614e..b2fe16aefb269313677225d0ef9fc6aea10e254d 100755 (executable)
@@ -24,8 +24,7 @@ echo "Format filesystem and populate"
 _scratch_mkfs > $seqres.full
 _scratch_mount >> $seqres.full
 
-$XFS_INFO_PROG $SCRATCH_MNT | grep -q ftype=1 || \
-       _notrun "filesystem does not support ftype"
+_require_xfs_has_feature "$SCRATCH_MNT" ftype
 
 filter_ls() {
        awk '
index dd33801d5ec55aae686598fbbde11cfb8637d790..325a05c14124cba91a013036b467dd9b1596746d 100755 (executable)
@@ -98,13 +98,13 @@ clean_files()
 
 filter_quot()
 {
-       _filter_quota | grep -v "root \|\#0 " \
+       _filter_quota | grep -v "root \|#0 " \
                | sed -e '/#[0-9]*/s/#[0-9]*/#ID/g'
 }
 
 filter_report()
 {
-       _filter_quota | grep -v "^root \|^\#0 " \
+       _filter_quota | grep -v "^root \|^#0 " \
                | sed -e '/^#[0-9]*/s/^#[0-9]*/#ID/g'
 }
 
index 3f90a397d843acc2635d56c7b7ffcb77fa241505..548c94905f5b03452ee7a10aece853181bf18101 100755 (executable)
@@ -48,7 +48,7 @@ test $? -eq 137 || echo "repair should have been killed??"
 _check_scratch_xfs_features NEEDSREPAIR
 _try_scratch_mount &> $tmp.mount
 res=$?
-_filter_scratch < $tmp.mount
+_filter_error_mount < $tmp.mount
 if [ $res -eq 0 ]; then
        echo "Should not be able to mount after needsrepair crash"
        _scratch_unmount
index 12f154abd07f2676a8dbae89562e48d1c093eb2c..1263f091ffa02e56303b028340c65f3986a26ec2 100644 (file)
@@ -1,4 +1,4 @@
 QA output created by 154
 FEATURES: NEEDSREPAIR:YES
-mount: SCRATCH_MNT: mount(2) system call failed: Structure needs cleaning.
+mount: Structure needs cleaning
 FEATURES: NEEDSREPAIR:NO
index c4ee8e20ef5066eab5fef9ca6649305e29e4f53f..8bf1aba1ffaca3f080856648daa59c41287893ac 100755 (executable)
@@ -26,6 +26,11 @@ _require_scratch_nocheck
 _require_scratch_xfs_crc               # needsrepair only exists for v5
 _require_populate_commands
 _require_libxfs_debug_flag LIBXFS_DEBUG_WRITE_CRASH
+_require_command "$TIMEOUT_PROG" timeout
+
+# Inject a 10 minute abortive timeout on the repair program so that deadlocks
+# in the program do not cause fstests to hang indefinitely.
+XFS_REPAIR_PROG="$TIMEOUT_PROG -s ABRT 10m $XFS_REPAIR_PROG"
 
 # Populate the filesystem
 _scratch_populate_cached nofill >> $seqres.full 2>&1
@@ -55,7 +60,7 @@ for ((nr_writes = 1; nr_writes < max_writes; nr_writes += nr_incr)); do
        # but repair -n says the fs is clean, then it's possible that the
        # injected error caused it to abort immediately after the write that
        # cleared NEEDSREPAIR.
-       if ! _check_scratch_xfs_features NEEDSREPAIR > /dev/null &&
+       if ! _check_scratch_xfs_features NEEDSREPAIR &> /dev/null &&
           ! _scratch_xfs_repair -n &>> $seqres.full; then
                echo "NEEDSREPAIR should be set on corrupt fs"
        fi
@@ -63,11 +68,12 @@ done
 
 # If NEEDSREPAIR is still set on the filesystem, ensure that a full run
 # cleans everything up.
-if _check_scratch_xfs_features NEEDSREPAIR > /dev/null; then
+echo "Checking filesystem one last time after $allowed_writes writes." >> $seqres.full
+if _check_scratch_xfs_features NEEDSREPAIR &> /dev/null; then
        echo "Clearing NEEDSREPAIR" >> $seqres.full
        _scratch_xfs_repair 2>> $seqres.full
        _check_scratch_xfs_features NEEDSREPAIR > /dev/null && \
-               echo "Repair failed to clear NEEDSREPAIR on the $nr_writes writes test"
+               echo "Repair failed to clear NEEDSREPAIR on the $allowed_writes writes test"
 fi
 
 # success, all done
index 505a9c73286ca1d07be43b7e911cd7c1194d749d..4440adf6ee5723f727ddb5da766b8a933f1057b0 100755 (executable)
@@ -51,7 +51,7 @@ test $? -eq 137 || echo "repair should have been killed??"
 _check_scratch_xfs_features NEEDSREPAIR INOBTCNT
 _try_scratch_mount &> $tmp.mount
 res=$?
-_filter_scratch < $tmp.mount
+_filter_error_mount < $tmp.mount
 if [ $res -eq 0 ]; then
        echo "needsrepair should have prevented mount"
        _scratch_unmount
index 4f9dfd0882c8d24aa12354531f60b750597936ac..5461031a38d4e0f509f46c608c5a8294d7e9509c 100644 (file)
@@ -8,7 +8,7 @@ FEATURES: INOBTCNT:NO
 Fail partway through upgrading
 Adding inode btree counts to filesystem.
 FEATURES: NEEDSREPAIR:YES INOBTCNT:YES
-mount: SCRATCH_MNT: mount(2) system call failed: Structure needs cleaning.
+mount: Structure needs cleaning
 Re-run repair to finish upgrade
 FEATURES: NEEDSREPAIR:NO INOBTCNT:YES
 Filesystem should be usable again
index 42379961e321776d32ebce1e766f0c5dad1e3323..45f28e7733b88212aa014290f7cfea1f451643b1 100755 (executable)
@@ -7,7 +7,7 @@
 # ->page-mkwrite test - unwritten extents and mmap
 #
 . ./common/preamble
-_begin_fstest rw metadata auto quick
+_begin_fstest rw metadata auto quick prealloc
 
 # Import common functions.
 . ./common/filter
@@ -16,12 +16,12 @@ _begin_fstest rw metadata auto quick
 # the others are unwritten.
 _filter_blocks()
 {
-       $AWK_PROG '
+       $AWK_PROG -v file_size=$FILE_SIZE '
 /^ +[0-9]/ {
        if (!written_size) {
                written_size = $6
-               unwritten1 = ((1048576/512) / 2) - written_size
-               unwritten2 = ((1048576/512) / 2) - 2 * written_size
+               unwritten1 = ((file_size/512) / 2) - written_size
+               unwritten2 = ((file_size/512) / 2) - 2 * written_size
        }
 
        # is the extent unwritten?
@@ -58,7 +58,22 @@ _scratch_mount
 
 TEST_FILE=$SCRATCH_MNT/test_file
 TEST_PROG=$here/src/unwritten_mmap
-FILE_SIZE=1048576
+
+# Beginning with 5.18, some filesystems support creating large folios for the
+# page cache.  A system with 64k pages can create 256k folios, which means
+# that with the old file size of 1M, the last half of the file is completely
+# converted from unwritten to written by page_mkwrite.  The test will fail on
+# the golden output when this happens, so increase the size from the original
+# 1MB file size to at least (6 * 256k == 1.5MB) prevent this from happening.
+#
+# However, increasing the file size to around 2MB causes regressions when fsdax
+# is enabled because fsdax will try to use PMD entries for the mappings.  Hence
+# we need to set the file size to (6 * 2MB == 12MB) to cover all cases.
+FILE_SIZE=$((12 * 1048576))
+
+# The xfs_bmap results in the golden output requires file allocations to align
+# to 1M boundaries.
+_require_congruent_file_oplen $SCRATCH_MNT $FILE_SIZE
 
 rm -f $TEST_FILE
 $TEST_PROG $FILE_SIZE $TEST_FILE
index 5f9850102aa614518bb352d4958299899883972b..734c107fb8adcab9d1aecb8273bb5ae31e38ab95 100755 (executable)
@@ -7,7 +7,7 @@
 # unwritten extent conversion test
 #
 . ./common/preamble
-_begin_fstest rw metadata auto stress
+_begin_fstest rw metadata auto stress prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -47,13 +47,11 @@ _require_fs_space $SCRATCH_MNT 10485760
 
 TEST_FILE=$SCRATCH_MNT/test_file
 TEST_PROG=$here/src/unwritten_sync
-LOOPS=50
+LOOPS=$((5 * $TIME_FACTOR))
 
 echo "*** test unwritten extent conversion under heavy I/O"
-
-workout
-
 rm -f $TEST_FILE
+workout
 $TEST_PROG $LOOPS $TEST_FILE
 
 echo "     *** test done"
index ba4aae5910c49cf3b92e1de1a1a238947a95a07d..49f7492c030bd1ecd6f49e716c1c559e07da26a1 100755 (executable)
@@ -8,7 +8,7 @@
 # of the filesystem is now in the middle of a sparse inode cluster.
 #
 . ./common/preamble
-_begin_fstest auto quick shrinkfs
+_begin_fstest auto quick shrinkfs prealloc punch
 
 # Import common functions.
 . ./common/filter
@@ -51,7 +51,7 @@ _scratch_mount
 _xfs_force_bdev data $SCRATCH_MNT
 old_dblocks=$($XFS_IO_PROG -c 'statfs' $SCRATCH_MNT | grep geom.datablocks)
 
-mkdir $SCRATCH_MNT/save/
+mkdir $SCRATCH_MNT/save/ $SCRATCH_MNT/urk/
 sino=$(stat -c '%i' $SCRATCH_MNT/save)
 
 _consume_freesp()
index 10891550853af7f42de8ff78d2ecfaffc158042b..1e59bd6c507e773ae856441093ccad755da3a000 100755 (executable)
@@ -39,6 +39,8 @@ _cleanup()
 
 # Modify as appropriate.
 _supported_fs xfs
+_fixed_by_kernel_commit f38a032b165d "xfs: fix I_DONTCACHE"
+
 _require_xfs_io_command "bulkstat"
 _require_scratch
 
index a65197cde388397bac819995ede2725518ff908a..fee1e92bf3c62e92934dc01377fa5fc7db250c4c 100755 (executable)
 . ./common/preamble
 _begin_fstest mkfs other auto
 
+filter_repair() {
+       _filter_repair | sed \
+               -e '/unable to verify superblock, continuing/d' \
+               -e '/found candidate secondary superblock/d' \
+               -e '/error reading superblock.*-- seek to offset/d'
+}
+
 # dd the 1st sector then repair
 _dd_repair_check()
 {
        #dd first sector
        dd if=/dev/zero of=$1 bs=$2 count=1 2>&1 | _filter_dd
        #xfs_repair
-       _scratch_xfs_repair 2>&1 | _filter_repair
+       _scratch_xfs_repair 2>&1 | filter_repair
        #check repair
        if _check_scratch_fs; then
                echo "repair passed"
index 0bebe553ebb3a50224aa1e23e22f8a53cfa33085..711e90cc26eb2fd12d9b4745dcc570659b9e8001 100644 (file)
@@ -9,7 +9,6 @@ Phase 1 - find and verify superblock...
 bad primary superblock - bad magic number !!!
 
 attempting to find secondary superblock...
-found candidate secondary superblock...
 verified secondary superblock...
 writing modified primary superblock
 sb root inode INO inconsistent with calculated value INO
@@ -45,7 +44,6 @@ Phase 1 - find and verify superblock...
 bad primary superblock - bad magic number !!!
 
 attempting to find secondary superblock...
-found candidate secondary superblock...
 verified secondary superblock...
 writing modified primary superblock
 sb root inode INO inconsistent with calculated value INO
index ec0cb7e5b469cb16abec5e907a7bdd7533442099..98b01476c963288ace3d8ff9bd5b298ab0f53124 100755 (executable)
@@ -21,17 +21,29 @@ _require_scratch_nocheck
 _require_cp_reflink
 _require_test_program "punch-alternating"
 
+_fixed_by_kernel_commit b25d1984aa88 \
+       "xfs: estimate post-merge refcounts correctly"
+
 echo "Format and mount"
 _scratch_mkfs -d agcount=1 > $seqres.full 2>&1
 _scratch_mount >> $seqres.full 2>&1
 
+# This test modifies the refcount btree on the data device, so we must force
+# rtinherit off so that the test files are created there.
+_xfs_force_bdev data $SCRATCH_MNT
+
 testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
+# Set the file size to 10x the block size to guarantee that the COW writes will
+# touch multiple blocks and exercise the refcount extent merging code.  This is
+# necessary to catch a bug in the refcount extent merging code that handles
+# MAXREFCOUNT edge cases.
 blksz=65536
+filesz=$((blksz * 10))
 
 echo "Create original files"
-_pwrite_byte 0x61 0 $blksz $testdir/file1 >> $seqres.full
+_pwrite_byte 0x61 0 $filesz $testdir/file1 >> $seqres.full
 _cp_reflink $testdir/file1 $testdir/file2 >> $seqres.full
 
 echo "Change reference count"
@@ -56,9 +68,23 @@ _scratch_xfs_db -c 'agf 0' -c 'addr refcntroot' -c 'p recs[1]' >> $seqres.full
 _scratch_mount >> $seqres.full
 
 echo "CoW a couple files"
-_pwrite_byte 0x62 0 $blksz $testdir/file3 >> $seqres.full
-_pwrite_byte 0x62 0 $blksz $testdir/file5 >> $seqres.full
+_pwrite_byte 0x62 0 $filesz $testdir/file3 >> $seqres.full
+_pwrite_byte 0x62 0 $filesz $testdir/file5 >> $seqres.full
+
+# For the last COW test, write single blocks at the start, middle, and end of
+# the shared file to exercise a refcount btree update that targets a single
+# block of the multiblock refcount record that we just modified.
+#
+# This trips a bug where XFS didn't correctly identify refcount record merge
+# candidates when any of the records are pinned at MAXREFCOUNT.  The bug was
+# originally discovered by enabling fsdax + reflink, but the bug can be
+# triggered by any COW that doesn't target the entire extent.
+#
+# The bug was fixed by kernel commit b25d1984aa88 ("xfs: estimate post-merge
+# refcounts correctly")
 _pwrite_byte 0x62 0 $blksz $testdir/file7 >> $seqres.full
+_pwrite_byte 0x62 $((blksz * 4)) $blksz $testdir/file7 >> $seqres.full
+_pwrite_byte 0x62 $((filesz - blksz)) $blksz $testdir/file7 >> $seqres.full
 
 echo "Check scratch fs"
 _scratch_unmount
index 72a1b738fa158e7f0bdc657cce3fdc8d510168ed..d2fac03a9e77b83e9690d1e778be30a7c313ad78 100755 (executable)
@@ -11,7 +11,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
@@ -23,6 +23,7 @@ _require_scratch_reflink
 _require_cp_reflink
 _require_xfs_io_command "fiemap"
 _require_xfs_io_command "cowextsize"
+_require_no_xfs_always_cow
 
 echo "Format and mount"
 _scratch_mkfs > $seqres.full 2>&1
@@ -32,6 +33,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=128
 filesize=$((blksz * nr))
 bufnr=16
index ea56582403f7afe1e3216fb081cd51284f8a8c95..511aca6f2dfab8731b4379ac514d97f480a3f447 100755 (executable)
@@ -11,7 +11,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
@@ -24,6 +24,7 @@ _require_cp_reflink
 _require_xfs_io_command "fiemap"
 _require_xfs_io_command "cowextsize"
 _require_odirect
+_require_no_xfs_always_cow     # writes have to converge to overwrites
 
 echo "Format and mount"
 _scratch_mkfs > $seqres.full 2>&1
@@ -33,6 +34,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=128
 filesize=$((blksz * nr))
 bufnr=16
@@ -54,9 +56,9 @@ md5sum $testdir/file2 | _filter_scratch
 
 echo "CoW and unmount"
 $XFS_IO_PROG -f -c "cowextsize" $testdir/file2 >> $seqres.full
-$XFS_IO_PROG -d -f -c "pwrite -R -S 0x63 -b $real_blksz 0 $((filesize + 1))" \
+$XFS_IO_PROG -d -f -c "pwrite -R -S 0x63 -b $real_blksz 0 $filesize" \
        $testdir/file2 2>&1 >> $seqres.full | _filter_xfs_io_error
-$XFS_IO_PROG -d -f -c "pwrite -S 0x63 -b $real_blksz 0 $((filesize + 1))" \
+$XFS_IO_PROG -d -f -c "pwrite -S 0x63 -b $real_blksz 0 $filesize" \
        $testdir/file2 2>&1 >> $seqres.full | _filter_xfs_io_error
 _scratch_cycle_mount
 
index 41384437ad6f56605e398b48d3a0ec94cc6b1c3e..8821bcd5bdac5030da782f801218c302a2a99ae9 100644 (file)
@@ -5,7 +5,6 @@ Compare files
 2909feb63a37b0e95fe5cfb7f274f7b1  SCRATCH_MNT/test-182/file1
 2909feb63a37b0e95fe5cfb7f274f7b1  SCRATCH_MNT/test-182/file2
 CoW and unmount
-pwrite: Invalid argument
 Compare files
 2909feb63a37b0e95fe5cfb7f274f7b1  SCRATCH_MNT/test-182/file1
 c6ba35da9f73ced20d7781a448cc11d4  SCRATCH_MNT/test-182/file2
index 95250b2996e23b5858ea0d0a771ef0f638a427ae..97f529f666edf5a84fccdda6aabef91bed5e3f7a 100755 (executable)
@@ -11,7 +11,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
 
 # Import common functions.
 . ./common/filter
@@ -19,6 +19,8 @@ _begin_fstest auto quick clone
 
 # real QA test starts here
 _supported_fs xfs
+_require_scratch
+_require_scratch_delalloc
 _require_scratch_reflink
 _require_cp_reflink
 _require_xfs_io_command "fiemap"
@@ -33,6 +35,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=128
 filesize=$((blksz * nr))
 bufnr=16
index f0e87642b4eb372c05b1be7c2475f47ce1ffa700..abeb052580a4590dd68d733476fc7588af276e19 100755 (executable)
@@ -16,7 +16,7 @@
 # smaller than the rt device.
 #
 . ./common/preamble
-_begin_fstest auto fsmap
+_begin_fstest auto fsmap prealloc punch
 
 _cleanup()
 {
@@ -108,7 +108,7 @@ expected_end="$(( (alloc_rtx * rtextsize - 1) / 512 ))"
 # file_offset file_end physical_offset physical_end
 rtfile_exts() {
        $XFS_IO_PROG -c 'bmap -elp' $rtfile | \
-               egrep -v '(^/|EXT:|hole)' | \
+               grep -E -v '(^/|EXT:|hole)' | \
                tr ':.[]' '    ' | \
                while read junk foff fend physoff physend junk; do
                        echo "$foff $fend $physoff $physend"
index b54fcf26109b41cc9524bce5a0117ec3a0c0d063..f44158e31ce389022bd573b41dea560b29bb253c 100755 (executable)
@@ -55,8 +55,8 @@ _filter_inode()
 {
        tee -a $seqres.full | \
                sed -e "s/core.forkoff/forkoff/g" | \
-               egrep '^u.sfdir2|^u.sfdir3|^a.sfattr|forkoff' | \
-               egrep -v 'inumber|parent' | \
+               grep -E '^u.sfdir2|^u.sfdir3|^a.sfattr|forkoff' | \
+               grep -E -v 'inumber|parent' | \
                sed -e s/sfdir3/sfdir2/g | \
                grep -v filetype
 }
index a9dfb30a338dbb66ed6870d241e6a3b839472669..7c34d8e630e86ee5a517e11a0ea4404fdf971d4c 100755 (executable)
@@ -28,7 +28,7 @@
 # the fix patches themselves.
 #
 . ./common/preamble
-_begin_fstest auto quick rw realtime
+_begin_fstest auto quick rw realtime prealloc punch
 
 # Import common functions.
 . ./common/filter
index e601881a6fbacf1f832b721f17317aed3ce3f99d..bc7ccca565a7f7fd7fe76d505ce51eee7608cc16 100755 (executable)
 #
 # <---- Normal programming is resumed ---->
 #
+# <---- Bbbzzzzzzztttt ---->
+#
+# < systemd enters the chat >
+#
+# xfs/189 [not run] noattr2 mount option not supported on /dev/vdc
+# xfs/190 1s ... mount: (hint) your fstab has been modified, but systemd still uses
+#        the old version; use 'systemctl daemon-reload' to reload.
+#  1s
+#  xfs/192 3s ... mount: (hint) your fstab has been modified, but systemd still uses
+#        the old version; use 'systemctl daemon-reload' to reload.
+#
+# mount/systemd sees that /etc/fstab has changed (because mtime changed)
+# and so it whines that systemd needs updating on every mount from this point
+# onwards. Yes, that's totally obnoxious behaviour from mount/systemd but we
+# have to work around it.
+#
+# < systemd leaves the chat >
 #
 . ./common/preamble
 _begin_fstest mount auto quick
@@ -190,6 +207,10 @@ ENDL
 # Example fstab entry
 # /dev/sdb2            /mnt/scratch1        xfs       defaults 0 0
 #
+# Note that to avoid mnt/systemd whining about /etc/fstab being modified, we
+# need to ensure that it reloads it's state once we restore the fstab to
+# original.
+#
 _add_scratch_fstab()
 {
        # comment out any existing SCRATCH_DEV
@@ -201,7 +222,7 @@ _add_scratch_fstab()
 
 _modify_scratch_fstab()
 {
-       opts=$1
+       local opts=$1
 
        # modify our fstab entry that we added
        # modify opts by looking for last word which has non-space chars
@@ -215,6 +236,9 @@ _putback_scratch_fstab()
 
        # remove the one we added at the end
        $SED_PROG -i "/# $tag/d" /etc/fstab
+
+       # stop mount/systemd whining that /etcfstab was changed.
+       command -v systemctl > /dev/null 2>&1 && systemctl daemon-reload
 }
 
 # Import common functions.
diff --git a/tests/xfs/191 b/tests/xfs/191
new file mode 100755 (executable)
index 0000000..7a02f1b
--- /dev/null
@@ -0,0 +1,130 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 191
+#
+# Make sure that XFS can handle empty leaf xattr blocks correctly.  These
+# blocks can appear in files as a result of system crashes in the middle of
+# xattr operations, which means that we /must/ handle them gracefully.
+# Check that read and write verifiers won't trip, that the get/list/setxattr
+# operations don't stumble over them, and that xfs_repair will offer to remove
+# the entire xattr fork if the root xattr leaf block is empty.
+#
+# Regression test for
+# kernel commit:
+# 7be3bd8856fb ("xfs: empty xattr leaf header blocks are not corruption")
+# xfsprogs commit:
+# f50d3462c654 ("xfs_repair: ignore empty xattr leaf blocks")
+#
+. ./common/preamble
+_begin_fstest auto quick attr
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+
+# real QA test starts here
+
+_supported_fs xfs
+_require_scratch
+_require_scratch_xfs_crc # V4 is deprecated
+_fixed_by_kernel_commit 7be3bd8856fb "xfs: empty xattr leaf header blocks are not corruption"
+_fixed_by_kernel_commit e87021a2bc10 "xfs: use larger in-core attr firstused field and detect overflow"
+_fixed_by_git_commit xfsprogs f50d3462c654 "xfs_repair: ignore empty xattr leaf blocks"
+
+_scratch_mkfs_xfs | _filter_mkfs >$seqres.full 2>$tmp.mkfs
+cat $tmp.mkfs >> $seqres.full
+source $tmp.mkfs
+_scratch_mount
+
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 64k' $SCRATCH_MNT/largefile >> $seqres.full
+$XFS_IO_PROG -f -c "pwrite -S 0x58 0 $isize" $SCRATCH_MNT/smallfile >> $seqres.full
+
+smallfile_md5=$(_md5_checksum $SCRATCH_MNT/smallfile)
+largefile_md5=$(_md5_checksum $SCRATCH_MNT/largefile)
+
+# Try to force the creation of a single leaf block in each of three files.
+# The first one gets a local attr, the second a remote attr, and the third
+# is left for scrub and repair to find.
+touch $SCRATCH_MNT/e0
+touch $SCRATCH_MNT/e1
+touch $SCRATCH_MNT/e2
+
+$ATTR_PROG -s x $SCRATCH_MNT/e0 < $SCRATCH_MNT/smallfile >> $seqres.full
+$ATTR_PROG -s x $SCRATCH_MNT/e1 < $SCRATCH_MNT/smallfile >> $seqres.full
+$ATTR_PROG -s x $SCRATCH_MNT/e2 < $SCRATCH_MNT/smallfile >> $seqres.full
+
+e0_ino=$(stat -c '%i' $SCRATCH_MNT/e0)
+e1_ino=$(stat -c '%i' $SCRATCH_MNT/e1)
+e2_ino=$(stat -c '%i' $SCRATCH_MNT/e2)
+
+_scratch_unmount
+
+# We used to think that it wasn't possible for empty xattr leaf blocks to
+# exist, but it turns out that setting a large xattr on a file that has no
+# xattrs can race with a log flush and crash, which results in an empty
+# leaf block being logged and recovered.  This is rather hard to trip, so we
+# use xfs_db to turn a regular leaf block into an empty one.
+make_empty_leaf() {
+       local inum="$1"
+
+       echo "editing inode $inum" >> $seqres.full
+
+       magic=$(_scratch_xfs_get_metadata_field hdr.info.hdr.magic "inode $inum" "ablock 0")
+       if [ "$magic" != "0x3bee" ]; then
+               _scratch_xfs_db -x -c "inode $inum" -c "ablock 0" -c print >> $seqres.full
+               _fail "inode $inum ablock 0 is not a leaf block?"
+       fi
+
+       base=$(_scratch_xfs_get_metadata_field "hdr.freemap[0].base" "inode $inum" "ablock 0")
+
+       # 64k dbsize is a special case since it overflows the 16 bit firstused
+       # field and it needs to be set to the special XFS_ATTR3_LEAF_NULLOFF (0)
+       # value to indicate a null leaf.
+       if [ $dbsize -eq 65536 ]; then
+               firstused=0
+       else
+               firstused=$dbsize
+       fi
+
+       _scratch_xfs_db -x -c "inode $inum" -c "ablock 0" \
+               -c "write -d hdr.count 0" \
+               -c "write -d hdr.usedbytes 0" \
+               -c "write -d hdr.firstused $firstused" \
+               -c "write -d hdr.freemap[0].size $((dbsize - base))" \
+               -c print >> $seqres.full
+}
+
+make_empty_leaf $e0_ino
+make_empty_leaf $e1_ino
+make_empty_leaf $e2_ino
+
+_scratch_mount
+
+# Check that listxattr/getxattr/removexattr do nothing.
+$ATTR_PROG -l $SCRATCH_MNT/e0 2>&1 | _filter_scratch
+$ATTR_PROG -g x $SCRATCH_MNT/e0 2>&1 | _filter_scratch
+$ATTR_PROG -r x $SCRATCH_MNT/e0 2>&1 | _filter_scratch
+
+# Add a small attr to e0
+$ATTR_PROG -s x $SCRATCH_MNT/e0 < $SCRATCH_MNT/smallfile > /dev/null
+$ATTR_PROG -l $SCRATCH_MNT/e0 2>&1 | sed -e 's/\([0-9]*\) byte/XXX byte/g' | _filter_scratch
+small_md5="$($GETFATTR_PROG -n user.x --absolute-names --only-values $SCRATCH_MNT/e0 | _md5_checksum)"
+test "$small_md5" = "$smallfile_md5" || \
+       echo "smallfile $smallfile_md5 does not match small attr $small_md5"
+
+# Add a large attr to e1
+$ATTR_PROG -s x $SCRATCH_MNT/e1 < $SCRATCH_MNT/largefile > /dev/null
+$ATTR_PROG -l $SCRATCH_MNT/e1 2>&1 | _filter_scratch
+large_md5="$($GETFATTR_PROG -n user.x --absolute-names --only-values $SCRATCH_MNT/e1 | _md5_checksum)"
+test "$large_md5" = "$largefile_md5" || \
+       echo "largefile $largefile_md5 does not match large attr $large_md5"
+
+
+# Leave e2 to try to trip the repair tools, since xfs_repair used to flag
+# empty leaf blocks incorrectly too.
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/191.out b/tests/xfs/191.out
new file mode 100644 (file)
index 0000000..46dc05c
--- /dev/null
@@ -0,0 +1,7 @@
+QA output created by 191
+attr_get: No data available
+Could not get "x" for SCRATCH_MNT/e0
+attr_remove: No data available
+Could not remove "x" for SCRATCH_MNT/e0
+Attribute "x" has a XXX byte value for SCRATCH_MNT/e0
+Attribute "x" has a 65536 byte value for SCRATCH_MNT/e1
index 1eb9d52e09c21d88a0ef0ceef3d22c186e29dd80..ef7da55be50b3d42515ab888880ce24ba880a04c 100755 (executable)
@@ -11,7 +11,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
 
 # Import common functions.
 . ./common/filter
@@ -19,12 +19,15 @@ _begin_fstest auto quick clone
 
 # real QA test starts here
 _supported_fs xfs
+_require_scratch
+_require_scratch_delalloc
 _require_scratch_reflink
 _require_cp_reflink
 _require_xfs_io_command "fiemap"
 _require_xfs_io_command "cowextsize"
 _require_xfs_io_command "funshare"
 _require_odirect
+_require_no_xfs_always_cow     # writes have to converge to overwrites
 
 echo "Format and mount"
 _scratch_mkfs > $seqres.full 2>&1
@@ -34,6 +37,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=128
 filesize=$((blksz * nr))
 bufnr=16
index 1bc486106781f053133cf53e451a739744b0d2f2..c71aef354737ea16d52170bac99a02f423ab2502 100755 (executable)
@@ -10,7 +10,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
@@ -31,6 +31,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=128
 filesize=$((blksz * nr))
 bufnr=16
index 0c650874f069adfb5ef386d0d959bd36c6c26da6..e5b98609de09252696ccb1bc6f570d7c4bedbe0b 100755 (executable)
@@ -10,7 +10,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
@@ -23,6 +23,7 @@ _require_cp_reflink
 _require_xfs_io_command "fiemap"
 _require_xfs_io_command "cowextsize"
 _require_odirect
+_require_no_xfs_always_cow     # writes have to converge to overwrites
 
 echo "Format and mount"
 _scratch_mkfs > $seqres.full 2>&1
@@ -32,6 +33,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=128
 filesize=$((blksz * nr))
 bufnr=16
index 2324fbdb60d8a856297e36d311dcd6e5f5164b77..a9d6ce1bb6dab6337c3ddf4c3008a6568fcd60ea 100755 (executable)
@@ -13,7 +13,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
 
 # Import common functions.
 . ./common/filter
@@ -21,6 +21,8 @@ _begin_fstest auto quick clone
 
 # real QA test starts here
 _supported_fs xfs
+_require_scratch
+_require_scratch_delalloc
 _require_scratch_reflink
 _require_cp_reflink
 _require_xfs_io_command "fiemap"
@@ -35,6 +37,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=128
 filesize=$((blksz * nr))
 bufnr=16
index a12ae7c31cd35985d3cebd003d1ec130a6c6ab27..9a4a45641afefb42b0b569301d8a37845fdf355e 100755 (executable)
@@ -51,6 +51,10 @@ _require_scratch
 _scratch_mkfs > /dev/null 2>&1
 _scratch_mount > /dev/null 2>&1
 
+# The xfs_bmap results in the golden output requires file allocations to align
+# to 64k boundaries.
+_require_congruent_file_oplen $SCRATCH_MNT 65536
+
 for i in 10 14 15 16 17 28 29 30 31; do
         rm -f $SCRATCH_MNT/hole_file
        _write_holes $SCRATCH_MNT/hole_file${i} ${i}
index 931be12878995c33224bf248d18a7848529e8a34..7dacfa2de7138c25c892c962f0b6a1d80c626825 100755 (executable)
@@ -13,7 +13,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
 
 # Import common functions.
 . ./common/filter
@@ -21,12 +21,15 @@ _begin_fstest auto quick clone
 
 # real QA test starts here
 _supported_fs xfs
+_require_scratch
+_require_scratch_delalloc
 _require_scratch_reflink
 _require_cp_reflink
 _require_xfs_io_command "fiemap"
 _require_xfs_io_command "cowextsize"
 _require_xfs_io_command "funshare"
 _require_odirect
+_require_no_xfs_always_cow     # writes have to converge to overwrites
 
 echo "Format and mount"
 _scratch_mkfs > $seqres.full 2>&1
@@ -36,6 +39,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=128
 filesize=$((blksz * nr))
 bufnr=16
index ba07f1ee86cc0c2535cfc33a7a8fcd88d309eb8b..4bdafd2610172d172cfd797a65c61da71f512b75 100755 (executable)
@@ -10,7 +10,7 @@
 # - Ensure that whatever we set we get back later.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
index 66c3eda1188ecd3c67ecf4b1360d3c84118f26ee..1e7734b8222dce330dcd9269070b689c6929e186 100755 (executable)
@@ -14,7 +14,7 @@
 # - Repeat, but with extsz = 1MB and cowextsz = $blocksize.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
@@ -26,6 +26,7 @@ _require_scratch_reflink
 _require_cp_reflink
 _require_xfs_io_command "fiemap"
 _require_xfs_io_command "cowextsize"
+_require_no_xfs_always_cow
 
 echo "Format and mount"
 _scratch_mkfs > $seqres.full 2>&1
@@ -35,6 +36,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=128
 filesize=$((blksz * nr))
 bufnr=16
index 220ea31de02f1e15d60c872d1771aa57d8c12104..1ef6d33d88e87f2bdee96feb139d07188f1e2986 100755 (executable)
@@ -7,7 +7,7 @@
 # Make sure setting cowextsz on a directory propagates it to subfiles.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
@@ -23,6 +23,7 @@ _require_xfs_io_command "cowextsize"
 echo "Format and mount"
 _scratch_mkfs > $seqres.full 2>&1
 _scratch_mount >> $seqres.full 2>&1
+_require_congruent_file_oplen $SCRATCH_MNT 1048576
 
 testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
index 6edc56062d370bfd2942eb3790331c3fe16f9323..5f05ab26f88c2e898d2c5387f3ff73d797962be7 100755 (executable)
@@ -11,7 +11,7 @@
 # otherwise.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
@@ -27,6 +27,7 @@ _require_xfs_io_command "cowextsize"
 echo "Format and mount"
 _scratch_mkfs > $seqres.full 2>&1
 _scratch_mount >> $seqres.full 2>&1
+_require_congruent_file_oplen $SCRATCH_MNT 65536
 
 testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
index 055150417ff2f9cebbeb9b7591def083f26bfef9..3ce6496afcd71edd400a4ddc917891141cae5b9f 100755 (executable)
@@ -11,7 +11,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest clone_stress
+_begin_fstest clone_stress fiemap
 
 # Import common functions.
 . ./common/filter
@@ -24,6 +24,7 @@ _require_cp_reflink
 _require_xfs_io_command "fiemap"
 _require_xfs_io_command "cowextsize"
 _require_odirect
+_require_no_xfs_always_cow     # writes have to converge to overwrites
 
 echo "Format and mount"
 _scratch_mkfs > $seqres.full 2>&1
@@ -33,6 +34,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=50000
 filesize=$((blksz * nr))
 bufnr=16
index b133e09b4123ec63df3b1fc6133caabb52cc5c97..6df036089d7f11280c9970c92ed930f6c9757656 100755 (executable)
@@ -10,7 +10,7 @@
 # - Crash the FS to test recovery.
 #
 . ./common/preamble
-_begin_fstest shutdown auto quick clone
+_begin_fstest shutdown auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
@@ -30,6 +30,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=16
 filesize=$((blksz * nr))
 bufnr=2
index 3e1c45cdad34e09688b690bf96e86d21b2060c82..e1849624525c689f24e5c424b5335b71de2729b4 100755 (executable)
@@ -9,7 +9,7 @@
 # play with cowextsz.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
index 7c694e92f9b2dc599b829ba829ed2654b5ec4003..84ba838f3a0ebebba51bfd6bd1a24afc5b62d8f9 100755 (executable)
@@ -9,7 +9,7 @@
 # with cowextsz.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap
 
 # Import common functions.
 . ./common/filter
index 20217187b257cb3af9d5d5fdf88d7b57966e4a2a..d2c0d6fc981632aafd97438742ef30f1c06d82d8 100755 (executable)
@@ -13,7 +13,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -34,6 +34,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 real_blksz=$(_get_block_size $testdir)
index b834bbeb226f619365be5465c15514083da2a81e..1a994d7959969ff9256e35c6791a691a87417767 100755 (executable)
@@ -13,7 +13,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -33,6 +33,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 real_blksz=$(_get_block_size $testdir)
index b0eeb7840212cd10cebe399efb78d1f06c55dc66..507b033b28d83cdb0770cd3a5a88fbe4a2a64c14 100755 (executable)
@@ -13,7 +13,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -34,6 +34,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 real_blksz=$(_get_block_size $testdir)
index 09b2067dc85c5e539a17f75da3a546639f1c324b..598df3f1b869a16b2b371e9c29bfa0c6f6c93e4a 100755 (executable)
@@ -13,7 +13,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -33,6 +33,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 real_blksz=$(_get_block_size $testdir)
index 11dbad14d84397ed9f4e4ea7ed96bd5b068bce7a..849667d4edd83c0b64f660046ac2f3930dc7f17a 100755 (executable)
@@ -14,7 +14,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -36,6 +36,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 real_blksz=$(_get_block_size $testdir)
index f8bab07ee6757236ec50248cd50db699fdbb0c52..6f6dcd04d1d04ff5a86e61a5d69c06af2f4b7552 100755 (executable)
@@ -14,7 +14,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -35,6 +35,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 real_blksz=$(_get_block_size $testdir)
index 52a37d641d6b368445f2953bbe95f2a496f025fa..8722d5065779a84d715f6369e5dbda31f099b7cd 100755 (executable)
@@ -13,7 +13,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -34,6 +34,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 real_blksz=$(_get_block_size $testdir)
index 826bd08df096d77d58b71d98c72cb683f4a4f4c6..a5f46a14bc0a2b3636add684565b08672a45fe01 100755 (executable)
@@ -13,7 +13,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -33,6 +33,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 real_blksz=$(_get_block_size $testdir)
index f2f2f6a92d504694a5e1d9d2e13451ca8e66496e..504f9288a2909836d478dd75e9b4486aec205ec5 100755 (executable)
@@ -17,7 +17,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
 
 # Import common functions.
 . ./common/filter
@@ -41,6 +41,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 real_blksz=$(_get_block_size $testdir)
index 2221b9c49c79e19d95da4c65f01b467ff8a245cf..a58fd16bba32ce77581ae0abb93831037a5a5879 100755 (executable)
@@ -31,11 +31,16 @@ _require_fs_space $TEST_DIR 3200000
 TDIR="${TEST_DIR}/t_holes"
 NFILES="10"
 EXTSIZE="256k"
-_xfs_force_bdev data $TEST_DIR
 
 # Create the test directory
 mkdir ${TDIR}
 
+# Per-directory extent size hints aren't particularly useful for files that
+# are created on the realtime section.  Force the test file to be created on
+# the data directory.  Do not change the rtinherit flag on $TEST_DIR because
+# that will affect other tests.
+_xfs_force_bdev data $TDIR
+
 # Set the test directory extsize
 $XFS_IO_PROG -c "extsize ${EXTSIZE}" ${TDIR}
 
index 15f6b6849c3c0ccc11243a904f802fd4b2b9df0d..fd1209bea6c18e3e7e67338aecd20e631a37336a 100755 (executable)
@@ -17,7 +17,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
 
 # Import common functions.
 . ./common/filter
@@ -41,6 +41,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 real_blksz=$(_get_block_size $testdir)
index 8155d0ab1ae178bcf82c501b59b34a4aca1800f2..31f267eece5f95e61a079dafaa500fc0f43b74f5 100755 (executable)
@@ -12,7 +12,7 @@
 # - Write more and see how bad fragmentation is.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -45,6 +45,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 bufnr=2
index 062174669b1affd00c91a28367acdbce24cede18..0cb26fa1cd7d8cb7de1ce70b057b898205b2ba97 100755 (executable)
@@ -13,7 +13,7 @@
 # - Write more and see how bad fragmentation is.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -30,6 +30,8 @@ _cleanup()
 
 # real QA test starts here
 _supported_fs xfs
+_require_scratch
+_require_scratch_delalloc
 _require_xfs_io_command "cowextsize"
 _require_scratch_reflink
 _require_cp_reflink
@@ -46,6 +48,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 bufnr=2
index 6ff2f228e86c2e9e624543e07ba66994afe2cad4..6fdea42d218cca20b5abc118405dea8fb021db7f 100755 (executable)
@@ -15,25 +15,27 @@ _begin_fstest auto quick rmap punch metadump
 _cleanup()
 {
     cd /
-    _scratch_unmount > /dev/null 2>&1
-    rm -rf $tmp.* $metadump_file $TEST_DIR/image
+    _xfs_cleanup_verify_metadump
+    rm -rf $tmp.* $testdir
 }
 
 # Import common functions.
 . ./common/filter
+. ./common/metadump
 
 # real QA test starts here
 _supported_fs xfs
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
 _require_loop
 _require_xfs_scratch_rmapbt
 _require_xfs_io_command "fpunch"
+_xfs_setup_verify_metadump
 
 _scratch_mkfs >/dev/null 2>&1
 _scratch_mount
 
 testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
-metadump_file=$TEST_DIR/${seq}_metadump
 
 echo "Create the original file blocks"
 blksz="$(_get_block_size $testdir)"
@@ -46,18 +48,10 @@ seq 1 2 $((nr_blks - 1)) | while read nr; do
        $XFS_IO_PROG -c "fpunch $((nr * blksz)) $blksz" $testdir/file1 >> $seqres.full
 done
 
-echo "Create metadump file"
 _scratch_unmount
-_scratch_xfs_metadump $metadump_file
 
-# Now restore the obfuscated one back and take a look around
-echo "Restore metadump"
-xfs_mdrestore $metadump_file $TEST_DIR/image
-SCRATCH_DEV=$TEST_DIR/image _scratch_mount
-SCRATCH_DEV=$TEST_DIR/image _scratch_unmount
-
-echo "Check restored fs"
-_check_generic_filesystem $metadump_file
+echo "Create metadump file, restore it and check restored fs"
+_xfs_verify_metadumps
 
 # success, all done
 status=0
index 463d4660ad21399d551d3c85dae2b0463e856636..fc2ddd770c482a4133f9b859c824fb53b3974620 100644 (file)
@@ -1,6 +1,4 @@
 QA output created by 234
 Create the original file blocks
 Punch every other block
-Create metadump file
-Restore metadump
-Check restored fs
+Create metadump file, restore it and check restored fs
index 34d54a6c9b49881b393dabcbc9cb3b42052ef235..db235e05b90ed3412943321f92d36c8b2df2a4eb 100755 (executable)
@@ -46,6 +46,7 @@ bufsize=$((blksz * bufnr))
 alignment=`_min_dio_alignment $TEST_DEV`
 
 _require_fs_space $SCRATCH_MNT $((filesize / 1024 * 3 * 5 / 4))
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 
 echo "Create the original files"
 $XFS_IO_PROG -c "cowextsize $((bufsize * 2))" $testdir
index 5143cc2e03b11f00256897ccde0e6be41336b36a..f04460bc5cb92a7df0a5640ff74b6ece681b6961 100755 (executable)
@@ -35,6 +35,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=640
 bufnr=128
 filesize=$((blksz * nr))
index e5d35a83cea9e52f237dfdddbedeeb69537fdfda..a65c270d23de88bc38cce578bc979cf5fcafa4bc 100755 (executable)
@@ -40,6 +40,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=640
 bufnr=128
 filesize=$((blksz * nr))
index 7988c2d87490d3eb71543829e4efa4505572ac98..d9879788ae7536b0aa42644b4de9a48956a5cc5e 100755 (executable)
@@ -36,6 +36,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=640
 bufnr=128
 filesize=$((blksz * nr))
index 8f0c5939967de86fc8d0eb9749367e6b9d53bafc..2e537f3f5578f2932e5b3e90e95e5d8ead36e7ac 100755 (executable)
@@ -17,7 +17,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone punch
+_begin_fstest auto quick clone punch prealloc
 
 # Import common functions.
 . ./common/filter
@@ -27,6 +27,7 @@ _begin_fstest auto quick clone punch
 _supported_fs xfs
 _require_xfs_debug
 _require_scratch_reflink
+_require_scratch_delalloc
 _require_xfs_io_command "falloc"
 _require_xfs_io_command "cowextsize"
 _require_xfs_io_command "fpunch"
@@ -91,16 +92,16 @@ echo "Delayed allocation CoW extents:"
 test $(_xfs_bmapx_find cow $testdir/file3 delalloc) -gt 0 || \
        echo "Expected to find a delalloc CoW extent"
 echo "Shared data extents:"
-test $(_xfs_bmapx_find data $testdir/file3 '100000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file3 -E '10[01]{4}$') -gt 0 || \
        echo "Expected to find a shared data extent"
 echo "Unwritten data extents:"
-test $(_xfs_bmapx_find data $testdir/file3 '10000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file3 -E '1[01]{4}$') -gt 0 || \
        echo "Expected to find an unwritten data extent"
 echo "Hole data extents:"
 test $(_xfs_bmapx_find data $testdir/file3 hole) -gt 0 || \
        echo "Expected to find a hole data extent"
 echo "Regular data extents:"
-test $(_xfs_bmapx_find data $testdir/file3 '000000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file3 -E '00[01]{4}$') -gt 0 || \
        echo "Expected to find a regular data extent"
 
 sync
@@ -114,16 +115,16 @@ echo "Real CoW extents:"
 test $(_xfs_bmapx_find cow $testdir/file3 delalloc ) -eq 0 || \
        echo "Expected to find zero delalloc CoW extent"
 echo "Shared data extents:"
-test $(_xfs_bmapx_find data $testdir/file3 '100000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file3 -E '10[01]{4}$') -gt 0 || \
        echo "Expected to find a shared data extent"
 echo "Unwritten data extents:"
-test $(_xfs_bmapx_find data $testdir/file3 '10000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file3 -E '1[01]{4}$') -gt 0 || \
        echo "Expected to find an unwritten data extent"
 echo "Hole data extents:"
 test $(_xfs_bmapx_find data $testdir/file3 hole) -gt 0 || \
        echo "Expected to find a hole data extent"
 echo "Regular data extents:"
-test $(_xfs_bmapx_find data $testdir/file3 '000000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file3 -E '00[01]{4}$') -gt 0 || \
        echo "Expected to find a regular data extent"
 
 _scratch_cycle_mount
index 417dd18c39b7b63c21908aebe718d1843e5aa96e..595a5938b42aaffad2a478e0d4e49fd658781000 100755 (executable)
@@ -11,7 +11,7 @@
 # - compare file[12]
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -42,17 +42,17 @@ md5sum $testdir/file1 | _filter_scratch
 md5sum $testdir/file2 | _filter_scratch
 
 echo "Unwritten data extents"
-test $(_xfs_bmapx_find data $testdir/file1 '10000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file1 -E '1[01]{4}$') -gt 0 || \
        echo "Expected to find an unwritten file1 extent"
 echo "Shared data extents"
-test $(_xfs_bmapx_find data $testdir/file1 '100000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file1 -E '10[01]{4}$') -gt 0 || \
        echo "Expected to find a shared data extent"
 
 echo "Hole data extents"
 test $(_xfs_bmapx_find data $testdir/file2 'hole') -gt 0 || \
        echo "Expected to find a hole data extent"
 echo "Shared data extents"
-test $(_xfs_bmapx_find data $testdir/file2 '100000$') -gt 0 || \
+test $(_xfs_bmapx_find data $testdir/file2 -E '10[01]{4}$') -gt 0 || \
        echo "Expected to find a shared data extent"
 
 echo "Hole cow extents"
index 32902cb75fd356d74a9cb56b956d63addf938586..9b95af1d78babab4cbfdc1e1dcdcbc0337463228 100755 (executable)
@@ -14,7 +14,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -34,6 +34,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 $XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
index 774d3bf25eca0db9a7e988c2ecaa383d40fcb25b..4febb79c0586e4e6bc5efe33723186d70e033dda 100755 (executable)
@@ -14,7 +14,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -35,6 +35,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 $XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
index 0b090180b212f0d9b9b9fbf062bd1036b0ae09a0..7e21b50249ea879c5c554e3df1f470c4dd0b7aae 100755 (executable)
@@ -16,7 +16,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -36,6 +36,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 $XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
index 43740cb4b748e68a55db490502c259a61bde7297..f167fd5a4e812b822541f8372746e683e33cf5f2 100755 (executable)
@@ -7,7 +7,7 @@
 # Test fallocate hole punching
 #
 . ./common/preamble
-_begin_fstest auto quick prealloc punch
+_begin_fstest auto quick prealloc punch fiemap
 
 # Import common functions.
 . ./common/filter
index 1899ce5226fc959f673edfe07fb388a2be278eac..18c58eb8d55a509c854398076f322e535039fd69 100755 (executable)
@@ -26,19 +26,21 @@ _cleanup()
     cd /
     rm -f $tmp.*
     rm -rf "${OUTPUT_DIR}"
-    rm -f "${METADUMP_FILE}"
+    _xfs_cleanup_verify_metadump
 }
 
 # Import common functions.
 . ./common/filter
+. ./common/metadump
 
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
 _require_test
 _require_scratch
+_xfs_setup_verify_metadump
 
 # real QA test starts here
 
 OUTPUT_DIR="${SCRATCH_MNT}/test_${seq}"
-METADUMP_FILE="${TEST_DIR}/${seq}_metadump"
 ORPHANAGE="lost+found"
 
 _supported_fs xfs
@@ -48,21 +50,30 @@ function create_file() {
        touch $(printf "$@")
 }
 
+extra_test() {
+       cd "${SCRATCH_MNT}"
+
+       # Get a listing of all the files after obfuscation
+       echo "Metadump v1" >> $seqres.full
+       ls -R >> $seqres.full
+       ls -R | od -c >> $seqres.full
+
+       cd /
+}
+
 echo "Disciplyne of silence is goed."
 
 _scratch_mkfs >/dev/null 2>&1
 _scratch_mount
 
-# Initialize and mount the scratch filesystem, then create a bunch
-# of files that exercise the original problem.
+# Initialize and mount the scratch filesystem, then create a bunch of
+# files that exercise the original problem.
 #
 # The problem arose when a file name produced a hash that contained
-# either 0x00 (string terminator) or 0x27 ('/' character) in a
-# spot used to determine a character in an obfuscated name.  This
-# occurred in one of 5 spots at the end of the name, at position
-# (last-4), (last-3), (last-2), (last-1), or (last).
-
-rm -f "${METADUMP_FILE}"
+# either 0x00 (string terminator) or 0x27 ('/' character) in a spot used
+# to determine a character in an obfuscated name.  This occurred in one
+# of 5 spots at the end of the name, at position (last-4), (last-3),
+# (last-2), (last-1), or (last).
 
 mkdir -p "${OUTPUT_DIR}"
 
@@ -77,8 +88,8 @@ create_file 'lmno'            # hash 0x0d9b776f (4-byte name)
 create_file 'pqrstu'           # hash 0x1e5cf9f2 (6-byte name)
 create_file 'abcdefghijklmnopqrstuvwxyz' # a most remarkable word (0x55004ae3)
 
-# Create a short directory name; it won't be obfuscated.  Populate
-# it with some longer named-files.  The first part of the obfuscated
+# Create a short directory name; it won't be obfuscated.  Populate it
+# with some longer named-files.  The first part of the obfuscated
 # filenames should use printable characters.
 mkdir foo
 create_file 'foo/longer_file_name_1'   # hash 0xe83634ec
@@ -148,22 +159,12 @@ ls -R | od -c >> $seqres.full
 cd $here
 
 _scratch_unmount
-_scratch_xfs_metadump $METADUMP_FILE
 
-# Now restore the obfuscated one back and take a look around
-xfs_mdrestore "${METADUMP_FILE}" "${SCRATCH_DEV}"
-
-_scratch_mount
-
-# Get a listing of all the files after obfuscation
-cd ${SCRATCH_MNT}
-ls -R >> $seqres.full
-ls -R | od -c >> $seqres.full
+_xfs_verify_metadumps '' extra_test
 
 # Finally, re-make the filesystem since to ensure we don't
 # leave a directory with duplicate entries lying around.
 cd /
-_scratch_unmount
 _scratch_mkfs >/dev/null 2>&1
 
 # all done
index 40d176fc0519f4acd13d9c0efef7d3f32b2624b5..f31b6651abf32475afbe5072bd60828a959220a3 100755 (executable)
@@ -16,7 +16,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -37,6 +37,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 $XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
index 255f3b2f69bd3237c0cb96b938ee4eed1784fb32..c2eade3e5a12e258759ffc03e38bd70e6187ee6a 100755 (executable)
@@ -16,7 +16,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -36,6 +36,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 $XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
index 1c703242d4fe260f47c7deade8668b8036e0a0dd..5162e450a8de424c2943ab12b6a88014321e0a4f 100755 (executable)
@@ -16,7 +16,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -37,6 +37,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 $XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
index 6a58f0ac7070566581dea01ef7fe76cfe4196245..ae1dc98dbb58ea3ffe8ebede125e90da5767859d 100755 (executable)
@@ -17,7 +17,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -38,6 +38,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 $XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
index 2865cdf9cd0996c2c0f4c22d573b29be307624a8..7ce7bd2eec22627536c2ac6b58db323b9993d3cf 100755 (executable)
@@ -17,7 +17,7 @@
 #   - Check that the files are now different where we say they're different.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 # Import common functions.
 . ./common/filter
@@ -39,6 +39,7 @@ mkdir $testdir
 
 echo "Create the original files"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=64
 filesize=$((blksz * nr))
 $XFS_IO_PROG -c "cowextsize $((blksz * 16))" $testdir >> $seqres.full
index 78db5e342142d0b44fe3734170a41b012e1fbeba..a4ff6a471c74c81f40b2cbfa5af9459fc5d0e128 100755 (executable)
@@ -9,7 +9,7 @@
 # executable and libraries!) to see what happens.
 #
 . ./common/preamble
-_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair
+_begin_fstest fuzzers scrub online_repair
 
 _register_cleanup "_cleanup" BUS
 
@@ -29,6 +29,9 @@ _require_xfs_io_error_injection "force_repair"
 echo "Format and populate"
 _scratch_mkfs > "$seqres.full" 2>&1
 _scratch_mount
+
+_require_scratch_xfs_scrub
+
 cp $XFS_SCRUB_PROG $SCRATCH_MNT/xfs_scrub
 $LDD_PROG $XFS_SCRUB_PROG | sed -e '/\//!d;/linux-gate/d;/=>/ {s/.*=>[[:blank:]]*\([^[:blank:]]*\).*/\1/};s/[[:blank:]]*\([^[:blank:]]*\) (.*)/\1/' | while read lib; do
        cp $lib $SCRATCH_MNT/
index fadd6280a2bb28dcebb2581850cbcafabe6ae19c..bce4e13f92243c787265c312c5e23f8b6bf96a37 100755 (executable)
@@ -72,12 +72,11 @@ function test_all_state()
 }
 
 echo "==== NO CRC ===="
-# Control size to control inode numbers
-_scratch_mkfs_xfs "-m crc=0 -n ftype=0 -d size=512m" >> $seqres.full
+_scratch_mkfs_xfs "-m crc=0 -n ftype=0" >> $seqres.full
 test_all_state
 
 echo "==== CRC ===="
-_scratch_mkfs_xfs "-m crc=1 -d size=512m" >>$seqres.full
+_scratch_mkfs_xfs "-m crc=1" >>$seqres.full
 test_all_state
 
 status=0
index 502e72d3ac5cb588a389319121816c975647cef6..e45ac5a5beb84619429b1c5f862530699ba24ff2 100644 (file)
@@ -2,20 +2,20 @@ QA output created by 264
 === Test EIO/max_retries ===
 error/fail_at_unmount=1
 error/metadata/default/max_retries=-1
-error/metadata/default/retry_timeout_seconds=0
+error/metadata/default/retry_timeout_seconds=-1
 error/metadata/EIO/max_retries=-1
-error/metadata/EIO/retry_timeout_seconds=0
+error/metadata/EIO/retry_timeout_seconds=-1
 error/metadata/ENOSPC/max_retries=-1
-error/metadata/ENOSPC/retry_timeout_seconds=0
+error/metadata/ENOSPC/retry_timeout_seconds=-1
 error/fail_at_unmount=0
 error/metadata/EIO/max_retries=1
 === Test EIO/retry_timeout_seconds ===
 error/fail_at_unmount=1
 error/metadata/default/max_retries=-1
-error/metadata/default/retry_timeout_seconds=0
+error/metadata/default/retry_timeout_seconds=-1
 error/metadata/EIO/max_retries=-1
-error/metadata/EIO/retry_timeout_seconds=0
+error/metadata/EIO/retry_timeout_seconds=-1
 error/metadata/ENOSPC/max_retries=-1
-error/metadata/ENOSPC/retry_timeout_seconds=0
+error/metadata/ENOSPC/retry_timeout_seconds=-1
 error/fail_at_unmount=0
 error/metadata/EIO/retry_timeout_seconds=1
index 0ab0c7d807ad3a2dd7edf9d8ed7475bbf65f6d7a..16e508035a9c7d04b247315a9cf903ed4722ac07 100755 (executable)
@@ -17,18 +17,55 @@ _begin_fstest auto quick mount
 
 # real QA test starts here
 _supported_fs xfs
+_fixed_by_kernel_commit 74ad4693b647 \
+       "xfs: fix log recovery when unknown rocompat bits are set"
 # skip fs check because superblock contains unknown ro-compat features
 _require_scratch_nocheck
 # Only V5 XFS disallow rw mount/remount with unknown ro-compat features
 _require_scratch_xfs_crc
-
-_scratch_mkfs_xfs >>$seqres.full 2>&1
+_require_scratch_shutdown
 
 # set the highest bit of features_ro_compat, use it as an unknown
 # feature bit. If one day this bit become known feature, please
 # change this case.
-_scratch_xfs_set_metadata_field "features_ro_compat" "$((2**31))" "sb 0" | \
-       grep 'features_ro_compat'
+set_bad_rocompat() {
+       ro_compat=$(_scratch_xfs_get_metadata_field "features_ro_compat" "sb 0")
+       echo $ro_compat | grep -q -E '^0x[[:xdigit:]]+$'
+       if [[ $? != 0  ]]; then
+               echo ":$ro_compat:"
+               echo "features_ro_compat has an invalid value."
+               return 1
+       fi
+
+       ro_compat=$(echo $ro_compat | \
+                           awk '/^0x[[:xdigit:]]+/ {
+                                       printf("0x%x\n", or(strtonum($1), 0x80000000))
+                               }')
+
+       # write the new ro compat field to the superblock
+       _scratch_xfs_set_metadata_field "features_ro_compat" "$ro_compat" "sb 0" \
+                                       > $seqres.full 2>&1
+
+       # read the newly set ro compat filed for verification
+       new_ro_compat=$(_scratch_xfs_get_metadata_field "features_ro_compat" "sb 0" \
+                                                       2>/dev/null)
+
+       # verify the new ro_compat field is correct. Without xfsprogs commit
+       # f4afdcb0ad ("xfs_db: clean up the salvage read callsites in set_cur()"),
+       # we can't get new_ro_compat value.
+       if [ "$new_ro_compat" != "$ro_compat" ]; then
+               echo "Unable to set new features_ro_compat. Wanted $ro_compat, got $new_ro_compat"
+               return 1
+       fi
+       return 0
+}
+
+# Once with a clean filesystem...
+_scratch_mkfs_xfs >>$seqres.full 2>&1
+_scratch_mount
+echo moo > $SCRATCH_MNT/testfile
+_scratch_unmount
+set_bad_rocompat
 
 # rw mount with unknown ro-compat feature should fail
 echo "rw mount test"
@@ -44,6 +81,7 @@ if [ $? -ne 0 ]; then
        _fail "ro mount test failed"
 else
        # no hang/panic is fine
+       cat $SCRATCH_MNT/testfile > /dev/null
        $FSSTRESS_PROG -d $SCRATCH_MNT -p 4 -n 400 >>$seqres.full 2>&1
 fi
 
@@ -57,6 +95,29 @@ fi
 
 _scratch_unmount
 
+# And again with a dirty filesystem...
+_scratch_mkfs_xfs >>$seqres.full 2>&1
+_scratch_mount
+echo moo > $SCRATCH_MNT/testfile
+$XFS_IO_PROG -x -c 'shutdown -f' "${SCRATCH_MNT}"
+_scratch_unmount
+set_bad_rocompat
+
+# rw mount with unknown ro-compat feature should fail
+echo "rw mount test"
+_try_scratch_mount 2>>$seqres.full
+if [ $? -eq 0 ]; then
+       _fail "rw mount test failed"
+fi
+
+# ro mount should succeed even with log recovery
+echo "ro mount test"
+_try_scratch_mount -o ro 2>>$seqres.full
+if [ $? -ne 0 ]; then
+       _fail "ro mount test failed"
+fi
+cat $SCRATCH_MNT/testfile > /dev/null
+
 # success, all done
 status=0
 exit
index 0a8b385137f8c41daa651aa54e25d6345c111bd3..a519d2f328fa462f80947694a70c6bbf0e7992d8 100644 (file)
@@ -1,5 +1,6 @@
 QA output created by 270
-features_ro_compat = 0x80000000
 rw mount test
 ro mount test
 rw remount test
+rw mount test
+ro mount test
index 14d64cd0e55ff4ab2f88ff91971bd172d644818b..d67ac4d6c1f9f933c45a5d3e65789e1ca00af832 100755 (executable)
@@ -37,7 +37,7 @@ agcount=$(_xfs_mount_agcount $SCRATCH_MNT)
 # same owner (per-AG metadata) for rmap btree blocks and blocks on the AGFL and
 # the reverse mapping index merges records, the number of per-AG extents
 # reported will vary depending on whether the refcount btree is enabled.
-$XFS_INFO_PROG $SCRATCH_MNT | grep -q reflink=1
+_require_xfs_has_feature "$SCRATCH_MNT" reflink
 has_reflink=$(( 1 - $? ))
 perag_metadata_exts=2
 test $has_reflink -gt 0 && perag_metadata_exts=$((perag_metadata_exts + 1))
index 7ed8b95122221b22e913fe84b7010ea1df880bdd..c68fa9d6140e8b51a9bfd70b018b91dd45821033 100755 (executable)
@@ -49,10 +49,10 @@ $XFS_IO_PROG -c 'bmap -v' $SCRATCH_MNT/urk | grep '^[[:space:]]*[0-9]*:' | grep
 
 echo "Check bmap and fsmap" | tee -a $seqres.full
 cat $TEST_DIR/bmap | while read ext offrange colon blockrange ag agrange total crap; do
-       qstr="^[[:space:]]*[0-9]*:[[:space:]]*[0-9]*:[0-9]*[[:space:]]*${blockrange} :[[:space:]]*${ino}[[:space:]]*${offrange}[[:space:]]*${ag}[[:space:]]*${agrange}[[:space:]]*${total}$"
+       qstr="^[[:space:]]*[0-9]*:[[:space:]]*[0-9]*:[0-9]*[[:space:]]*${blockrange} :[[:space:]]*${ino}[[:space:]]*${offrange}[[:space:]]*${ag}[[:space:]]*${agrange}[[:space:]]*${total}(| [01]*)$"
        echo "${qstr}" >> $seqres.full
        grep "${qstr}" $TEST_DIR/fsmap >> $seqres.full
-       found=$(grep -c "${qstr}" $TEST_DIR/fsmap)
+       found=$(grep -E -c "${qstr}" $TEST_DIR/fsmap)
        test $found -eq 1 || echo "Unexpected output for offset ${offrange}."
 done
 
index dcaea6880469c8ae2fce3bfbdaa79dd36b0af2fb..cd483d77bc2469e02f9ec424d9cc4f0de4639c19 100755 (executable)
@@ -49,10 +49,10 @@ $XFS_IO_PROG -c 'bmap -v' $SCRATCH_MNT/f1 | grep '^[[:space:]]*[0-9]*:' | grep -
 
 echo "Check f1 bmap and fsmap" | tee -a $seqres.full
 cat $TEST_DIR/bmap | while read ext offrange colon blockrange ag agrange total crap; do
-       qstr="^[[:space:]]*[0-9]*:[[:space:]]*[0-9]*:[0-9]*[[:space:]]*${blockrange} :[[:space:]]*${ino}[[:space:]]*${offrange}[[:space:]]*${ag}[[:space:]]*${agrange}[[:space:]]*${total} 0100000$"
+       qstr="^[[:space:]]*[0-9]*:[[:space:]]*[0-9]*:[0-9]*[[:space:]]*${blockrange} :[[:space:]]*${ino}[[:space:]]*${offrange}[[:space:]]*${ag}[[:space:]]*${agrange}[[:space:]]*${total} 010[01]{4}$"
        echo "${qstr}" >> $seqres.full
        grep "${qstr}" $TEST_DIR/fsmap >> $seqres.full
-       found=$(grep -c "${qstr}" $TEST_DIR/fsmap)
+       found=$(grep -E -c "${qstr}" $TEST_DIR/fsmap)
        test $found -eq 1 || echo "Unexpected output for offset ${offrange}."
 done
 
@@ -62,10 +62,10 @@ $XFS_IO_PROG -c 'bmap -v' $SCRATCH_MNT/f2 | grep '^[[:space:]]*[0-9]*:' | grep -
 
 echo "Check f2 bmap and fsmap" | tee -a $seqres.full
 cat $TEST_DIR/bmap | while read ext offrange colon blockrange ag agrange total crap; do
-       qstr="^[[:space:]]*[0-9]*:[[:space:]]*[0-9]*:[0-9]*[[:space:]]*${blockrange} :[[:space:]]*${ino}[[:space:]]*${offrange}[[:space:]]*${ag}[[:space:]]*${agrange}[[:space:]]*${total} 0100000$"
+       qstr="^[[:space:]]*[0-9]*:[[:space:]]*[0-9]*:[0-9]*[[:space:]]*${blockrange} :[[:space:]]*${ino}[[:space:]]*${offrange}[[:space:]]*${ag}[[:space:]]*${agrange}[[:space:]]*${total} 010[01]{4}$"
        echo "${qstr}" >> $seqres.full
        grep "${qstr}" $TEST_DIR/fsmap >> $seqres.full
-       found=$(grep -c "${qstr}" $TEST_DIR/fsmap)
+       found=$(grep -E -c "${qstr}" $TEST_DIR/fsmap)
        test $found -eq 1 || echo "Unexpected output for offset ${offrange}."
 done
 
index 835d187f516e4bb2b71b4a770463957f14a93956..9f366d1e759d26c21248cf8a6da370a6f7274711 100755 (executable)
@@ -26,6 +26,7 @@ _cleanup()
 _supported_fs xfs
 
 _require_scsi_debug
+size=$(_small_fs_size_mb 128)
 
 # Remove xfs signature so -f isn't needed to re-mkfs
 _wipe_device()
@@ -55,7 +56,7 @@ _check_mkfs()
 (
 echo "==================="
 echo "4k physical 512b logical aligned"
-SCSI_DEBUG_DEV=`_get_scsi_debug_dev 4096 512 0 128`
+SCSI_DEBUG_DEV=`_get_scsi_debug_dev 4096 512 0 $size`
 test -b "$SCSI_DEBUG_DEV" || _notrun "Could not get scsi_debug device"
 # sector size should default to 4k
 _check_mkfs $SCSI_DEBUG_DEV
@@ -68,7 +69,7 @@ _put_scsi_debug_dev
 (
 echo "==================="
 echo "4k physical 512b logical unaligned"
-SCSI_DEBUG_DEV=`_get_scsi_debug_dev 4096 512 1 128`
+SCSI_DEBUG_DEV=`_get_scsi_debug_dev 4096 512 1 $size`
 test -b "$SCSI_DEBUG_DEV" || _notrun "Could not get scsi_debug device"
 # should fail on misalignment
 _check_mkfs $SCSI_DEBUG_DEV
@@ -85,7 +86,7 @@ _put_scsi_debug_dev
 (
 echo "==================="
 echo "hard 4k physical / 4k logical"
-SCSI_DEBUG_DEV=`_get_scsi_debug_dev 4096 4096 0 128`
+SCSI_DEBUG_DEV=`_get_scsi_debug_dev 4096 4096 0 $size`
 test -b "$SCSI_DEBUG_DEV" || _notrun "Could not get scsi_debug device"
 # block size smaller than sector size should fail 
 _check_mkfs -b size=2048 $SCSI_DEBUG_DEV
index bc26e629681034162a82a90e18c66b3b78d151c1..35598b2f30043229dbc08ecfcf0d264344306b6b 100755 (executable)
@@ -7,7 +7,7 @@
 # Check that GETBMAPX accurately report shared extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone prealloc
 
 _register_cleanup "_cleanup" BUS
 
@@ -30,6 +30,7 @@ mkdir $testdir
 
 blocks=5
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 sz=$((blocks * blksz))
 
 echo "Create the original files"
index fa7bfdd0f0b6c35869568ee48d520179798ad57b..443c3757595fc367ee54497989a85400d5212f1d 100755 (executable)
@@ -24,6 +24,7 @@ _cleanup()
 
 # real QA test starts here
 _supported_fs xfs
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
 _require_xfs_copy
 _require_test
 _require_scratch
@@ -41,14 +42,14 @@ COPY_FILE="${TEST_DIR}/${seq}_copyfile"
 # xfs_metadump should refuse to dump a mounted device
 _scratch_mkfs >> $seqres.full 2>&1
 _scratch_mount
-_scratch_xfs_metadump $METADUMP_FILE 2>&1 | filter_mounted
+_scratch_xfs_metadump $METADUMP_FILE -a -o 2>&1 | filter_mounted
 _scratch_unmount
 
 # Test restore to a mounted device
 # xfs_mdrestore should refuse to restore to a mounted device
-_scratch_xfs_metadump $METADUMP_FILE
+_scratch_xfs_metadump $METADUMP_FILE -a -o
 _scratch_mount
-xfs_mdrestore $METADUMP_FILE $SCRATCH_DEV 2>&1 | filter_mounted
+_scratch_xfs_mdrestore $METADUMP_FILE 2>&1 | filter_mounted
 _scratch_unmount
 
 # Test xfs_copy to a mounted device
index 711211d412a6a4cef0ad51f8e4174641afb2a7b5..0056baeb1c7329a25299e2cf7b1c54bf415164e5 100755 (executable)
@@ -4,55 +4,35 @@
 #
 # FS QA Test No. 285
 #
-# Race fio and xfs_scrub for a while to see if we crash or livelock.
+# Race fsstress and xfs_scrub in read-only mode for a while to see if we crash
+# or livelock.
 #
 . ./common/preamble
-_begin_fstest dangerous_fuzzers dangerous_scrub
+_begin_fstest scrub dangerous_fsstress_scrub
 
+_cleanup() {
+       cd /
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       rm -r -f $tmp.*
+}
 _register_cleanup "_cleanup" BUS
 
 # Import common functions.
 . ./common/filter
 . ./common/fuzzy
 . ./common/inject
+. ./common/xfs
 
 # real QA test starts here
 _supported_fs xfs
-_require_test_program "feature"
-_require_command "$KILLALL_PROG" killall
-_require_command "$TIMEOUT_PROG" timeout
-_require_scrub
 _require_scratch
+_require_xfs_stress_scrub
 
-echo "Format and populate"
 _scratch_mkfs > "$seqres.full" 2>&1
 _scratch_mount
-
-STRESS_DIR="$SCRATCH_MNT/testdir"
-mkdir -p $STRESS_DIR
-
-cpus=$(( $($here/src/feature -o) * 4 * LOAD_FACTOR))
-$FSSTRESS_PROG -d $STRESS_DIR -p $cpus -n $((cpus * 100000)) $FSSTRESS_AVOID >/dev/null 2>&1 &
-$XFS_SCRUB_PROG -d -T -v -n $SCRATCH_MNT >> $seqres.full
-
-killstress() {
-       sleep $(( 60 * TIME_FACTOR ))
-       $KILLALL_PROG -q $FSSTRESS_PROG
-}
-
-echo "Concurrent scrub"
-start=$(date +%s)
-end=$((start + (60 * TIME_FACTOR) ))
-killstress &
-echo "Scrub started at $(date --date="@${start}"), ending at $(date --date="@${end}")" >> $seqres.full
-while [ "$(date +%s)" -lt "$end" ]; do
-       $TIMEOUT_PROG -s TERM $(( end - $(date +%s) + 2 )) $XFS_SCRUB_PROG -d -T -v -n $SCRATCH_MNT >> $seqres.full 2>&1
-done
-
-echo "Test done"
-echo "Scrub finished at $(date)" >> $seqres.full
-$KILLALL_PROG -q $FSSTRESS_PROG
+_scratch_xfs_stress_scrub -S '-n'
 
 # success, all done
+echo Silence is golden
 status=0
 exit
index be6b49a9fb9daaff9c9bd335e81706b2261cee95..ab12da9ae7ad8f050157d8d81a52064d5fbe90fc 100644 (file)
@@ -1,4 +1,2 @@
 QA output created by 285
-Format and populate
-Concurrent scrub
-Test done
+Silence is golden
index 7edc9c427b50ab1f7601248889319cffb8ea279c..0f61a924db3b06909c9c03bfdb1e1378805af1b5 100755 (executable)
@@ -4,57 +4,35 @@
 #
 # FS QA Test No. 286
 #
-# Race fio and xfs_scrub for a while to see if we crash or livelock.
+# Race fsstress and xfs_scrub in force-repair mode for a while to see if we
+# crash or livelock.
 #
 . ./common/preamble
-_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair
+_begin_fstest online_repair dangerous_fsstress_repair
 
+_cleanup() {
+       cd /
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       rm -r -f $tmp.*
+}
 _register_cleanup "_cleanup" BUS
 
 # Import common functions.
 . ./common/filter
 . ./common/fuzzy
 . ./common/inject
+. ./common/xfs
 
 # real QA test starts here
 _supported_fs xfs
-_require_test_program "feature"
-_require_command "$KILLALL_PROG" killall
-_require_command "$TIMEOUT_PROG" timeout
-_require_scrub
 _require_scratch
-# xfs_scrub will turn on error injection itself
-_require_xfs_io_error_injection "force_repair"
+_require_xfs_stress_online_repair
 
-echo "Format and populate"
 _scratch_mkfs > "$seqres.full" 2>&1
 _scratch_mount
-
-STRESS_DIR="$SCRATCH_MNT/testdir"
-mkdir -p $STRESS_DIR
-
-cpus=$(( $($here/src/feature -o) * 4 * LOAD_FACTOR))
-$FSSTRESS_PROG -d $STRESS_DIR -p $cpus -n $((cpus * 100000)) $FSSTRESS_AVOID >/dev/null 2>&1 &
-$XFS_SCRUB_PROG -d -T -v -n $SCRATCH_MNT >> $seqres.full
-
-killstress() {
-       sleep $(( 60 * TIME_FACTOR ))
-       $KILLALL_PROG -q $FSSTRESS_PROG
-}
-
-echo "Concurrent repair"
-start=$(date +%s)
-end=$((start + (60 * TIME_FACTOR) ))
-killstress &
-echo "Repair started at $(date --date="@${start}"), ending at $(date --date="@${end}")" >> $seqres.full
-while [ "$(date +%s)" -lt "$end" ]; do
-       XFS_SCRUB_FORCE_REPAIR=1 $TIMEOUT_PROG -s TERM $(( end - $(date +%s) + 2 )) $XFS_SCRUB_PROG -d -T -v $SCRATCH_MNT >> $seqres.full
-done
-
-echo "Test done"
-echo "Repair finished at $(date)" >> $seqres.full
-$KILLALL_PROG -q $FSSTRESS_PROG
+_scratch_xfs_stress_online_repair -S '-k'
 
 # success, all done
+echo Silence is golden
 status=0
 exit
index 80e12b54955899f7b23046771251d23cae8238e9..35c4800694b14f0689020df0ec1d1a6e3fed35e9 100644 (file)
@@ -1,4 +1,2 @@
 QA output created by 286
-Format and populate
-Concurrent repair
-Test done
+Silence is golden
index e3d230e9991a1a1e83e1f2f8b99d5c41fe49fc84..aa664a266e5d36ff3384c083f68d97a8da0b2a84 100755 (executable)
@@ -8,7 +8,7 @@
 # that leaf directly (as xfsprogs commit f714016).
 #
 . ./common/preamble
-_begin_fstest auto quick repair fuzzers
+_begin_fstest auto quick repair fuzzers attr
 
 # Import common functions.
 . ./common/filter
@@ -50,25 +50,19 @@ if [ "$count" != "0" ]; then
        _notrun "xfs_db can't set attr hdr.count to 0"
 fi
 
-# make sure xfs_repair can find above corruption. If it can't, that
-# means we need to fix this bug on current xfs_repair
-_scratch_xfs_repair -n >> $seqres.full 2>&1
-if [ $? -eq 0 ];then
-       _fail "xfs_repair can't find the corruption"
-else
-       # If xfs_repair can find this corruption, then this repair
-       # should junk above leaf attribute and fix this XFS.
-       _scratch_xfs_repair >> $seqres.full 2>&1
+# Check that xfs_repair discards the attr fork if block 0 is an empty leaf
+# block.  Empty leaf blocks at the start of the xattr data can be a byproduct
+# of a shutdown race, and hence are not a corruption.
+_scratch_xfs_repair >> $seqres.full 2>&1
 
-       # Old xfs_repair maybe find and fix this corruption by
-       # reset the first used heap value and the usedbytes cnt
-       # in ablock 0. That's not what we want. So check if
-       # xfs_repair has junked the whole ablock 0 by xfs_db.
-       _scratch_xfs_db -x -c "inode $inum" -c "ablock 0" | \
-               grep -q "no attribute data"
-       if [ $? -ne 0 ]; then
-               _fail "xfs_repair didn't junk the empty attr leaf"
-       fi
+# Old xfs_repair maybe find and fix this corruption by
+# reset the first used heap value and the usedbytes cnt
+# in ablock 0. That's not what we want. So check if
+# xfs_repair has junked the whole ablock 0 by xfs_db.
+_scratch_xfs_db -x -c "inode $inum" -c "ablock 0" | \
+       grep -q "no attribute data"
+if [ $? -ne 0 ]; then
+       _fail "xfs_repair didn't junk the empty attr leaf"
 fi
 
 echo "Silence is golden"
index 6d5e247eafad3e8fb0317923238b3fa90729a812..2bd94d7b9b5f533add1c31ed4a2bb4ae66a183cc 100755 (executable)
@@ -9,10 +9,21 @@
 . ./common/preamble
 _begin_fstest auto repair metadump
 
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.*
+       _xfs_cleanup_verify_metadump
+}
+
 # Import common functions.
 . ./common/filter
+. ./common/metadump
 
 _supported_fs xfs
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
+_xfs_setup_verify_metadump
 
 # real QA test starts here
 _require_scratch
@@ -91,14 +102,7 @@ _scratch_xfs_check >> $seqres.full 2>&1 || _fail "xfs_check failed"
 
 # Yes they can!  Now...
 # Can xfs_metadump cope with this monster?
-_scratch_xfs_metadump $tmp.metadump || _fail "xfs_metadump failed"
-xfs_mdrestore $tmp.metadump $tmp.img || _fail "xfs_mdrestore failed"
-[ "$USE_EXTERNAL" = yes ] && [ -n "$SCRATCH_RTDEV" ] && \
-       rt_repair_opts="-r $SCRATCH_RTDEV"
-[ "$USE_EXTERNAL" = yes ] && [ -n "$SCRATCH_LOGDEV" ] && \
-       log_repair_opts="-l $SCRATCH_LOGDEV"
-$XFS_REPAIR_PROG $rt_repair_opts $log_repair_opts -f $tmp.img >> $seqres.full 2>&1 || \
-       _fail "xfs_repair of metadump failed"
+_xfs_verify_metadumps '-a -o'
 
 # Yes it can; success, all done
 status=0
index 20f8c48689b6459c0b4fc1c39b1723c0dcf9f47c..7a17eb81c43927c303eb5d57f9ae2a366faa02c4 100755 (executable)
@@ -33,7 +33,7 @@ esac
 _require_command "$(type -P $CAT)" $CAT
 
 for COMMAND in `$XFS_IO_PROG -c help | awk '{print $1}' | grep -v "^Use"`; do
-  $CAT "$MANPAGE" | egrep -q "^\.B.*$COMMAND" || \
+  $CAT "$MANPAGE" | grep -E -q "^\.B.*$COMMAND" || \
        echo "$COMMAND not documented in the xfs_io manpage"
 done
 
index e00f012736331159af3c49edcb7af07f11a8fe7a..d381e2c85cf92566f42e1414231dc79db61b7928 100755 (executable)
@@ -14,7 +14,7 @@
 # Failure is a hang; KASAN should also catch this.
 #
 . ./common/preamble
-_begin_fstest auto dir metadata
+_begin_fstest auto dir metadata prealloc punch
 
 # Import common functions.
 . ./common/filter
index ca482e064f388899ca25c3d6abb03e9fa7b7e92c..1d101876174b2d822ffd6a669810e372abb41b98 100755 (executable)
 . ./common/preamble
 _begin_fstest auto freeze
 
+# Override the default cleanup function.
+_cleanup()
+{
+       # Make sure $SCRATCH_MNT is unfreezed
+       xfs_freeze -u $SCRATCH_MNT 2>/dev/null
+       $KILLALL_PROG -q -9 $FSSTRESS_PROG
+       wait
+       cd /
+       rm -f $tmp.*
+}
+
 # Import common functions.
 . ./common/filter
 
@@ -28,7 +39,7 @@ _scratch_mount
 STRESS_DIR="$SCRATCH_MNT/testdir"
 mkdir -p $STRESS_DIR
 
-$FSSTRESS_PROG -d $STRESS_DIR -n 100 -p 1000 $FSSTRESS_AVOID >/dev/null 2>&1 &
+$FSSTRESS_PROG -d $STRESS_DIR -n 100 -p 1000 $FSSTRESS_AVOID >>$seqres.full &
 
 # Freeze/unfreeze file system randomly
 echo "Start freeze/unfreeze randomly" | tee -a $seqres.full
index 41c7b7f86ac1bf523336dd4db5b9f8cab72ffc27..d8a6712e528c8cd4d3d17bd5798184dd175fc581 100755 (executable)
@@ -36,7 +36,7 @@ _exercise()
        _qmount
        mkdir -p $QUOTA_DIR
 
-       $FSSTRESS_PROG -d $QUOTA_DIR -n 1000000 -p 100 $FSSTRESS_AVOID >/dev/null 2>&1 &
+       $FSSTRESS_PROG -d $QUOTA_DIR -n 1000000 -p 100 $FSSTRESS_AVOID >>$seqres.full &
        sleep 10
        $XFS_QUOTA_PROG -x -c "disable -$type" $SCRATCH_DEV
        sleep 5
index ba7204dd00062c14bc2d3bd19923b779382c10c9..f3c970fadf46471185fbcf8a76e4c4c19e6f5620 100755 (executable)
@@ -22,7 +22,7 @@ _require_scratch_reflink
 echo "Format"
 _scratch_mkfs > $seqres.full 2>&1
 _scratch_mount >> $seqres.full
-is_rmap=$($XFS_INFO_PROG $SCRATCH_MNT | grep -c "rmapbt=1")
+is_rmap=$(_xfs_has_feature $SCRATCH_MNT rmapbt -v)
 _scratch_unmount
 
 _get_agf_data() {
index d0f47f5038e428a1af743bfb6fc9489e675de38e..6da6622e14a61c2af68f38e7d92e617678814bae 100755 (executable)
@@ -22,7 +22,7 @@ _require_scratch_reflink
 echo "Format"
 _scratch_mkfs > $seqres.full 2>&1
 _scratch_mount >> $seqres.full
-is_rmap=$($XFS_INFO_PROG $SCRATCH_MNT | grep -c "rmapbt=1")
+is_rmap=$(_xfs_has_feature $SCRATCH_MNT rmapbt -v)
 _scratch_xfs_unmount_dirty
 
 _get_agf_data() {
index 3214e04b275a11ad901b73e47ae2a1618f30b993..edd7d0d787aa9aa53c08e4fa1e59b9f813f09819 100755 (executable)
@@ -7,7 +7,7 @@
 # Create a file with more than 2^21 blocks (the max length of a bmbt record).
 #
 . ./common/preamble
-_begin_fstest auto clone rmap
+_begin_fstest auto clone rmap prealloc
 
 # Override the default cleanup function.
 _cleanup()
index 94f868fe91c8be608eeecc83188dc00f8afbef97..cb232bdf8355143e7b0419e7043a40a9c49ffa77 100755 (executable)
@@ -8,7 +8,7 @@
 # Inject an error during block remap to test log recovery.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone punch
 
 # Override the default cleanup function.
 _cleanup()
@@ -36,6 +36,7 @@ sz=$((blksz * blks))
 echo "Format filesystem"
 _scratch_mkfs >/dev/null 2>&1
 _scratch_mount >> $seqres.full
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 
 echo "Create files"
 _pwrite_byte 0x66 0 $sz $SCRATCH_MNT/file1 >> $seqres.full
index 9c7cf5b94a7579806965c2f840b2d3cfd50312de..21e36982c6e8e7aff63edf7c8a479a4f4324a17f 100755 (executable)
@@ -8,7 +8,7 @@
 # Inject an error during refcount updates to test log recovery.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone punch
 
 # Override the default cleanup function.
 _cleanup()
index 105515ab0ce2f5054322e08cba0b028dee84c8e7..9f6b39c8ccf0fce29b9627dda0ec9dae97ba5324 100755 (executable)
@@ -37,6 +37,7 @@ sz=$((blksz * blks))
 echo "Format filesystem"
 _scratch_mkfs >/dev/null 2>&1
 _scratch_mount >> $seqres.full
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 
 $XFS_IO_PROG -c "cowextsize $sz" $SCRATCH_MNT
 
index f0af19d2542beeed8d71da82b3d97d452820e7e8..7f7bdd6419f6889bdbcaa7647860183c7cb64ce7 100755 (executable)
@@ -8,7 +8,7 @@
 # Force XFS into "two refcount updates per transaction" mode.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone punch
 
 # Override the default cleanup function.
 _cleanup()
index 38c7aa6016b4cf79bac0a61db488427340bdd510..5798f9a38eac826dc0315cd1f9f788eaf8a216d0 100755 (executable)
@@ -7,13 +7,14 @@
 # Simulate free extent errors with a file write and a file remove.
 #
 . ./common/preamble
-_begin_fstest auto quick rw
+_begin_fstest auto quick rw freeze
 
 # Override the default cleanup function.
 _cleanup()
 {
+       # Make sure $SCRATCH_MNT is unfreezed
+       xfs_freeze -u $SCRATCH_MNT 2>/dev/null
        cd /
-       _scratch_unmount > /dev/null 2>&1
        rm -rf $tmp.*
 }
 
@@ -26,6 +27,7 @@ _supported_fs xfs
 _require_scratch
 _require_error_injection
 _require_xfs_io_error_injection "rmap_finish_one"
+_require_freeze
 
 blksz=65536
 blks=64
index 89a2f7413131446ccacc56ae701d4cec8922ba28..a2c3720ed8c842839debc148cbd1519487142057 100755 (executable)
@@ -36,6 +36,7 @@ sz=$((blksz * blks))
 echo "Format filesystem"
 _scratch_mkfs >/dev/null 2>&1
 _scratch_mount >> $seqres.full
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 
 echo "Create files"
 _pwrite_byte 0x66 0 $sz $SCRATCH_MNT/file1 >> $seqres.full
index 9909db62127d143a674b5c508c3090c099ef642d..57cab86a6859c40b2046f4a6152ef73f957271cb 100755 (executable)
@@ -8,7 +8,7 @@
 # Force XFS into "two refcount updates per transaction" mode.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone punch
 
 # Override the default cleanup function.
 _cleanup()
index 5b26b2b37f385f4ae7a8b65a78ba5b552785ad21..43fb09a6be4ed970f32e7d988de8cdc2c3874cb3 100755 (executable)
@@ -8,13 +8,14 @@
 # Inject an error during extent freeing to test log recovery.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone freeze
 
 # Override the default cleanup function.
 _cleanup()
 {
+       # Make sure $SCRATCH_MNT is unfreezed
+       xfs_freeze -u $SCRATCH_MNT 2>/dev/null
        cd /
-       _scratch_unmount > /dev/null 2>&1
        rm -rf $tmp.*
 }
 
@@ -29,6 +30,7 @@ _require_cp_reflink
 _require_scratch_reflink
 _require_error_injection
 _require_xfs_io_error_injection "free_extent"
+_require_freeze
 
 blksz=65536
 blks=30
index 8b95a18a203b8e6f6032f8c0a51f637853f034b9..ac620fc433fdbb2327c29640cf8fb795e3ca3873 100755 (executable)
@@ -10,7 +10,7 @@
 # instead of when we're stashing the CoW orphan record.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone punch
 
 # Override the default cleanup function.
 _cleanup()
@@ -40,8 +40,21 @@ echo "Format filesystem"
 _scratch_mkfs >/dev/null 2>&1
 _scratch_mount >> $seqres.full
 
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 $XFS_IO_PROG -c "cowextsize $sz" $SCRATCH_MNT
 
+# This test uses a very large cowextszhint to manipulate the COW fork to
+# contain a large unwritten extent before injecting the error.  The goal is
+# that the write() will succeed, writeback will flush the dirty data to disk,
+# and writeback completion will shut down the filesystem when it tries to
+# remove the staging extent record from the refcount btree.  In other words,
+# this test ensures that XFS always finishes a COW completion it has started.
+#
+# This test isn't compatible with always_cow mode because the hole in the COW
+# fork left by the first write means that writeback tries to allocate a COW
+# staging extent for an unshared extent and trips over the injected error.
+_require_no_xfs_always_cow
+
 echo "Create files"
 _pwrite_byte 0x66 0 $sz $SCRATCH_MNT/file1 >> $seqres.full
 _cp_reflink $SCRATCH_MNT/file1 $SCRATCH_MNT/file2
index c45fa5f8ad429e311fa9a4c26488b94b7bf98fdb..30e364eb8b9b7a2f296288711766bcf69e3e5355 100755 (executable)
@@ -7,7 +7,7 @@
 # See how well xfs_fsr handles "defragging" a file with a hojillion extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone fsr
+_begin_fstest auto quick clone fsr prealloc
 
 # Import common functions.
 . ./common/filter
index e9a30d05901502bf573fab2c1cd183261f1618a4..12b7c6084269d500da7c38fabe1724fb0acf5d31 100755 (executable)
@@ -22,6 +22,7 @@ _require_cp_reflink
 _require_command "$XFS_FSR_PROG" "xfs_fsr"
 _require_xfs_io_error_injection "bmap_finish_one"
 _require_xfs_scratch_rmapbt
+_require_xfs_io_command falloc # fsr requires support for preallocation
 
 rm -f "$seqres.full"
 
@@ -31,6 +32,7 @@ _scratch_mount >> "$seqres.full" 2>&1
 
 testdir="$SCRATCH_MNT/test-$seq"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 blks=3
 mkdir "$testdir"
 
@@ -51,11 +53,11 @@ $XFS_FSR_PROG -v -d $testdir/file1 >> $seqres.full 2>&1
 echo "FS should be shut down, touch will fail"
 touch $SCRATCH_MNT/badfs 2>&1 | _filter_scratch
 
-echo "Remount to replay log" | tee /dev/ttyprintk
+echo "Remount to replay log" | _tee_kernlog
 _scratch_remount_dump_log >> $seqres.full
 new_nextents=$(_count_extents $testdir/file1)
 
-echo "Check extent count" | tee /dev/ttyprintk
+echo "Check extent count" | _tee_kernlog
 $XFS_IO_PROG -c 'stat -v' $testdir/file1 >> $seqres.full
 $XFS_IO_PROG -c 'stat -v' $testdir/file2 >> $seqres.full
 echo "extents: $old_nextents -> $new_nextents" >> $seqres.full
index 40be4c46bfcb066e4b3d1934a43a17842303b87f..c6e74e67e8a856bfc8d406cd95167c769ebaff96 100755 (executable)
@@ -7,7 +7,7 @@
 # Ensure that xfs_fsr handles quota correctly while defragging files.
 #
 . ./common/preamble
-_begin_fstest auto quick clone fsr quota
+_begin_fstest auto quick clone fsr quota prealloc
 
 # Import common functions.
 . ./common/filter
@@ -26,7 +26,7 @@ _require_nobody
 
 do_repquota()
 {
-       repquota $SCRATCH_MNT | egrep '^(fsgqa|root|nobody)' | sort -r
+       repquota $SCRATCH_MNT | grep -E '^(fsgqa|root|nobody)' | sort -r
 }
 
 rm -f "$seqres.full"
index 733ff58fab9dd6b73c3a664d4dfa1e472ccd33a0..2332533fbee4abf2f4fc8d30c9a5520af1dfd30c 100755 (executable)
@@ -7,7 +7,7 @@
 # Create a big enough rmapbt that we tickle a fdblocks accounting bug.
 #
 . ./common/preamble
-_begin_fstest auto quick rmap clone
+_begin_fstest auto quick rmap clone prealloc
 
 # Import common functions.
 . ./common/filter
index 4cc01e971cc932eafbeb0c31e493a3419ec3df9c..a2d37ee905fb52f518026250680288a14f267977 100755 (executable)
@@ -7,7 +7,7 @@
 # Make sure query_range returns -EINVAL if lowkey > highkey.
 #
 . ./common/preamble
-_begin_fstest auto quick rmap clone collapse punch insert zero
+_begin_fstest auto quick rmap clone collapse punch insert zero prealloc
 
 # Import common functions.
 . ./common/filter
index ccc508e79c9519cc8343e37c270ae22a23307a90..79d0711647d40f885cce0445a90926e77d7e37b1 100755 (executable)
@@ -7,7 +7,7 @@
 # Exercise expanding and shrinking the realtime rmap btree.
 #
 . ./common/preamble
-_begin_fstest auto rmap realtime
+_begin_fstest auto rmap realtime prealloc
 
 # Import common functions.
 . ./common/filter
@@ -31,7 +31,8 @@ blksz="$(_get_block_size $SCRATCH_MNT)"
 echo "Create a three-level rtrmapbt"
 # inode core size is at least 176 bytes; btree header is 56 bytes;
 # rtrmap record is 32 bytes; and rtrmap key/pointer are 56 bytes.
-i_ptrs=$(( (isize - 176) / 56 ))
+i_core_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
+i_ptrs=$(( (isize - i_core_size) / 56 ))
 bt_ptrs=$(( (blksz - 56) / 56 ))
 bt_recs=$(( (blksz - 56) / 32 ))
 
index 6592419d034bf5cc05d0c900dbd24fe33aff36f7..3c30f1a40b08e7b310a492ef31d30f31b8e6e4f3 100755 (executable)
@@ -7,7 +7,7 @@
 # Exercise metadump on realtime rmapbt preservation.
 #
 . ./common/preamble
-_begin_fstest auto rmap realtime metadump
+_begin_fstest auto rmap realtime metadump prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -21,6 +21,7 @@ _cleanup()
 
 # real QA test starts here
 _supported_fs xfs
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
 _require_realtime
 _require_xfs_scratch_rmapbt
 _require_test_program "punch-alternating"
@@ -41,7 +42,8 @@ rm -rf $metadump_file
 echo "Create a three-level rtrmapbt"
 # inode core size is at least 176 bytes; btree header is 56 bytes;
 # rtrmap record is 32 bytes; and rtrmap key/pointer are 56 bytes.
-i_ptrs=$(( (isize - 176) / 56 ))
+i_core_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
+i_ptrs=$(( (isize - i_core_size) / 56 ))
 bt_ptrs=$(( (blksz - 56) / 56 ))
 bt_recs=$(( (blksz - 56) / 32 ))
 
@@ -60,11 +62,11 @@ _scratch_cycle_mount
 
 echo "Create metadump file"
 _scratch_unmount
-_scratch_xfs_metadump $metadump_file
+_scratch_xfs_metadump $metadump_file -a -o
 
 # Now restore the obfuscated one back and take a look around
 echo "Restore metadump"
-xfs_mdrestore $metadump_file $TEST_DIR/image
+SCRATCH_DEV=$TEST_DIR/image _scratch_xfs_mdrestore $metadump_file
 SCRATCH_DEV=$TEST_DIR/image _scratch_mount
 SCRATCH_DEV=$TEST_DIR/image _scratch_unmount
 
index a2515e36cee975a8435afe4c94dc780405b6335f..f74baae9b007d0b22a8a77b289b52bf1f0b24de1 100755 (executable)
@@ -7,7 +7,7 @@
 # Corrupt the realtime rmapbt and see how the kernel and xfs_repair deal.
 #
 . ./common/preamble
-_begin_fstest fuzzers rmap realtime
+_begin_fstest fuzzers rmap realtime prealloc
 
 # Import common functions.
 . ./common/filter
@@ -33,7 +33,8 @@ blksz="$(_get_block_size $SCRATCH_MNT)"
 
 # inode core size is at least 176 bytes; btree header is 56 bytes;
 # rtrmap record is 32 bytes; and rtrmap key/pointer are 56 bytes.
-i_ptrs=$(( (isize - 176) / 56 ))
+i_core_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
+i_ptrs=$(( (isize - i_core_size) / 56 ))
 bt_ptrs=$(( (blksz - 56) / 56 ))
 bt_recs=$(( (blksz - 56) / 32 ))
 
index f026aa379084bba490ea5976ba04cae33f8812fb..1f734c90150f1d2d6c08152f033b3490e96a7d6c 100755 (executable)
@@ -7,7 +7,7 @@
 # Cross-link file block into rtrmapbt and see if repair fixes it.
 #
 . ./common/preamble
-_begin_fstest auto quick rmap realtime
+_begin_fstest auto quick rmap realtime prealloc
 
 # Import common functions.
 . ./common/filter
@@ -33,7 +33,8 @@ rtextsz_blks=$((rtextsz / blksz))
 
 # inode core size is at least 176 bytes; btree header is 56 bytes;
 # rtrmap record is 32 bytes; and rtrmap key/pointer are 56 bytes.
-i_ptrs=$(( (isize - 176) / 56 ))
+i_core_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
+i_ptrs=$(( (isize - i_core_size) / 56 ))
 bt_recs=$(( (blksz - 56) / 32 ))
 
 blocks=$((i_ptrs * bt_recs + 1))
index 1ae414eb09caf49d75b1fb9f6fa332bd39508a65..538c8987ef241acc4a58b1da17340dd7ad2b5db7 100755 (executable)
@@ -7,7 +7,7 @@
 # Cross-link rtrmapbt block into a file and see if repair fixes it.
 #
 . ./common/preamble
-_begin_fstest auto quick rmap realtime
+_begin_fstest auto quick rmap realtime prealloc
 
 # Import common functions.
 . ./common/filter
@@ -30,7 +30,8 @@ blksz="$(_get_block_size $SCRATCH_MNT)"
 
 # inode core size is at least 176 bytes; btree header is 56 bytes;
 # rtrmap record is 32 bytes; and rtrmap key/pointer are 56 bytes.
-i_ptrs=$(( (isize - 176) / 56 ))
+i_core_size="$(_xfs_get_inode_core_bytes $SCRATCH_MNT)"
+i_ptrs=$(( (isize - i_core_size) / 56 ))
 bt_recs=$(( (blksz - 56) / 32 ))
 
 blocks=$((i_ptrs * bt_recs + 1))
index 816ff2412ea43ae2f515e9b05c8c5739af37d382..bffcc7d9ac737ff515146c75d154771b9b63d74a 100755 (executable)
@@ -7,7 +7,7 @@
 # Basic rmap manipulation tests for realtime files.
 #
 . ./common/preamble
-_begin_fstest auto quick rmap collapse punch insert zero realtime
+_begin_fstest auto quick rmap collapse punch insert zero realtime prealloc
 
 # Import common functions.
 . ./common/filter
index f87095bf62e543bb0237c80d2c50b7802adc0d97..adb6627e481f4420d058481f78fb0758e4b39c48 100755 (executable)
@@ -11,7 +11,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
 
 # Import common functions.
 . ./common/filter
index 702c6d827a652c71ba2c080d80b2531724369664..36625e83400c6ec25e283fb4e5310ec1285e3e5d 100755 (executable)
@@ -10,7 +10,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
 
 # Import common functions.
 . ./common/filter
index bb5422024bef81491cdd53d855c1ff871c42fc63..9ce58ab8bdf474397d44e0b26ba3faad24877bdb 100755 (executable)
@@ -11,7 +11,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
 
 # Import common functions.
 . ./common/filter
@@ -34,6 +34,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=128
 filesize=$((blksz * nr))
 bufnr=8
index 63ee1ec6810e149154894a52f3675e03883670d1..1867c08ccabc6a1b7d1699ce91a3517dc1f9391a 100755 (executable)
@@ -11,7 +11,7 @@
 # - Check the number of extents.
 #
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone fiemap unshare
 
 # Import common functions.
 . ./common/filter
@@ -33,6 +33,7 @@ testdir=$SCRATCH_MNT/test-$seq
 mkdir $testdir
 
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 nr=128
 filesize=$((blksz * nr))
 bufnr=8
index faf2dca50b07ee3280dfa726773afcf11278cabc..d1645d9462e5a79193e66363fe61e704f7fed4db 100755 (executable)
@@ -39,7 +39,7 @@ mknod $testdir/CHRDEV c 1 1
 mknod $testdir/BLKDEV b 1 1
 mknod $testdir/FIFO p
 
-$XFS_INFO_PROG $SCRATCH_MNT | grep -q "ftype=1" && FTYPE_FEATURE=1
+_xfs_has_feature $SCRATCH_MNT ftype && FTYPE_FEATURE=1
 
 # Record test dir inode for xfs_repair filter
 inode_filter=$tmp.sed
index b10ce1d68fd875360141ceeed3cbb873fd6b84d5..8abf527ea62872360b5460f7f2389624bbda1121 100755 (executable)
@@ -28,8 +28,13 @@ echo "Fuzz AGFL"
 _scratch_xfs_fuzz_metadata '' 'offline' 'agfl 0' >> $seqres.full
 echo "Done fuzzing AGFL"
 
-echo "Fuzz AGFL flfirst"
+# Restore a correct copy of the filesystem before we start the second round of
+# fuzzing.  This avoids corruption errors from xfs_db when we probe for flfirst
+# in the AGF and later when _scratch_xfs_fuzz_metadata probes the AGFL fields.
+__scratch_xfs_fuzz_mdrestore
 flfirst=$(_scratch_xfs_db -c 'agf 0' -c 'p flfirst' | sed -e 's/flfirst = //g')
+
+echo "Fuzz AGFL flfirst"
 SCRATCH_XFS_LIST_METADATA_FIELDS="bno[${flfirst}]" _scratch_xfs_fuzz_metadata '' 'offline' 'agfl 0' >> $seqres.full
 echo "Done fuzzing AGFL flfirst"
 
index 530c9a970a364d9458bce947ab1a2fd555f702c9..2d552a591cc65b97613df9bfb53f1083d8e696b4 100755 (executable)
@@ -28,8 +28,13 @@ echo "Fuzz AGFL"
 _scratch_xfs_fuzz_metadata '' 'online' 'agfl 0' >> $seqres.full
 echo "Done fuzzing AGFL"
 
-echo "Fuzz AGFL flfirst"
+# Restore a correct copy of the filesystem before we start the second round of
+# fuzzing.  This avoids corruption errors from xfs_db when we probe for flfirst
+# in the AGF and later when _scratch_xfs_fuzz_metadata probes the AGFL fields.
+__scratch_xfs_fuzz_mdrestore
 flfirst=$(_scratch_xfs_db -c 'agf 0' -c 'p flfirst' | sed -e 's/flfirst = //g')
+
+echo "Fuzz AGFL flfirst"
 SCRATCH_XFS_LIST_METADATA_FIELDS="bno[${flfirst}]" _scratch_xfs_fuzz_metadata '' 'online' 'agfl 0' >> $seqres.full
 echo "Done fuzzing AGFL flfirst"
 
index 8a2c920ef46deb4025c721af24a4a87688074c09..25af8624dbcd951f18b898aece6f5a9b18ba28f5 100755 (executable)
@@ -25,7 +25,7 @@ echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
 echo "Fuzz AGI"
-_scratch_xfs_fuzz_metadata '' 'online' 'agi 0' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online' 'agi 1' >> $seqres.full
 echo "Done fuzzing AGI"
 
 # success, all done
index a00eb6f9cb35e017e460a9120de0277aca1847d7..92180e5196f3efef5c8e7580fbaf4bb520ebc370 100755 (executable)
@@ -24,8 +24,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+       _fail "could not find two-level bnobt"
+
 echo "Fuzz bnobt recs"
-_scratch_xfs_fuzz_metadata '' 'offline'  'agf 0' 'addr bnoroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline'  "$path" 'addr bnoroot' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing bnobt recs"
 
 # success, all done
index f0a82db4b852451a7272298be90853731fd5f26d..0498aaccf56c67751b4e0d2be6ea3bc4592e1f6a 100755 (executable)
@@ -24,8 +24,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+       _fail "could not find two-level bnobt"
+
 echo "Fuzz bnobt recs"
-_scratch_xfs_fuzz_metadata '' 'online'  'agf 0' 'addr bnoroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online'  "$path" 'addr bnoroot' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing bnobt recs"
 
 # success, all done
index 3942ffd1b2a359ec0469e3735f77ef5efd300717..c34f455403de7a5d944457a0febd93b3787b69e3 100755 (executable)
@@ -24,8 +24,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+       _fail "could not find two-level bnobt"
+
 echo "Fuzz bnobt keyptr"
-_scratch_xfs_fuzz_metadata '' 'offline'  'agf 0' 'addr bnoroot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline'  "$path" 'addr bnoroot' >> $seqres.full
 echo "Done fuzzing bnobt keyptr"
 
 # success, all done
index b7ee0f6f94cb2e5bacf968b3b9d523c63c80d3b3..22b1af4ea33f79df5d3da4e6aaeea01fc5121272 100755 (executable)
@@ -24,8 +24,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+       _fail "could not find two-level bnobt"
+
 echo "Fuzz bnobt keyptr"
-_scratch_xfs_fuzz_metadata '' 'online'  'agf 0' 'addr bnoroot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online'  "$path" 'addr bnoroot' >> $seqres.full
 echo "Done fuzzing bnobt keyptr"
 
 # success, all done
index f711661b02d8dd733b0bf569a4244dd082d95fd4..51727edc061d75e5f5e0d7b128ee5c4aa2f76450 100755 (executable)
@@ -24,8 +24,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'cnt' 2)" || \
+       _fail "could not find two-level cntbt"
+
 echo "Fuzz cntbt"
-_scratch_xfs_fuzz_metadata '' 'offline'  'agf 0' 'addr cntroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline'  "$path" 'addr cntroot' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing cntbt"
 
 # success, all done
index 6be9109eca7c4e9cacb9832f4c9b5cdc332dd6ff..8a62c1c82123e0081d836b058233b54fdd9afdc2 100755 (executable)
@@ -24,8 +24,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'cnt' 2)" || \
+       _fail "could not find two-level cntbt"
+
 echo "Fuzz cntbt"
-_scratch_xfs_fuzz_metadata '' 'online'  'agf 0' 'addr cntroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online'  "$path" 'addr cntroot' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing cntbt"
 
 # success, all done
index fcd18fe6861086017150e9e50382112fa577f785..984ecdafedd9ed7b4308d5688a179c85e7d1ecf1 100755 (executable)
@@ -24,8 +24,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+       _fail "could not find two-level inobt"
+
 echo "Fuzz inobt"
-_scratch_xfs_fuzz_metadata '' 'offline'  'agi 0' 'addr root' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline'  "$path" 'addr root' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing inobt"
 
 # success, all done
index 6f116f9b9cd67950ec82b8d559d75eae070c1755..e4325c35d19338e62c5200cdd8133173ea029af1 100755 (executable)
@@ -24,8 +24,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+       _fail "could not find two-level inobt"
+
 echo "Fuzz inobt"
-_scratch_xfs_fuzz_metadata '' 'online'  'agi 1' 'addr root' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online'  "$path" 'addr root' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing inobt"
 
 # success, all done
index 4c651288c0baceb53d38b130c4321eb1f01b4c8b..8a52d21a0fdfca6ca41c50ae2faf214ba3a8d3f9 100755 (executable)
@@ -25,8 +25,11 @@ _require_xfs_finobt
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'fino' 2)" || \
+       _fail "could not find two-level finobt"
+
 echo "Fuzz finobt"
-_scratch_xfs_fuzz_metadata '' 'offline'  'agi 0' 'addr free_root' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline'  "$path" 'addr free_root' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing finobt"
 
 # success, all done
index c474a9e7d853a4c9363bb7304820e5faf0bc3965..d9d07faab20d3cf372b0946cc949cb9ea0a1b4d9 100755 (executable)
@@ -25,8 +25,11 @@ _require_xfs_finobt
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'fino' 2)" || \
+       _fail "could not find two-level finobt"
+
 echo "Fuzz finobt"
-_scratch_xfs_fuzz_metadata '' 'online'  'agi 0' 'addr free_root' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online'  "$path" 'addr free_root' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing finobt"
 
 # success, all done
index b1c1f97664cc711a77d1eb86beaa6a4469d9652b..83499827c911dc47213d9059b7da2dc8ed64aa96 100755 (executable)
@@ -25,8 +25,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+       _fail "could not find two-level rmapbt"
+
 echo "Fuzz rmapbt recs"
-_scratch_xfs_fuzz_metadata '' 'offline' 'agf 0' 'addr rmaproot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline' "$path" 'addr rmaproot' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing rmapbt recs"
 
 # success, all done
index 5e6d8d9be09f00e758d1af2c8215f9d43d4e0c25..3236b50e00a88f7f6f6a5c14f8f803c4d12bd87b 100755 (executable)
@@ -25,8 +25,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+       _fail "could not find two-level rmapbt"
+
 echo "Fuzz rmapbt recs"
-_scratch_xfs_fuzz_metadata '' 'online' 'agf 0' 'addr rmaproot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online' "$path" 'addr rmaproot' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing rmapbt recs"
 
 # success, all done
index 0a916242e23c6464a6bc9a0cda52268fceb967e7..891d5e2572b15d36f01600218a33a6af81413a99 100755 (executable)
@@ -26,8 +26,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+       _fail "could not find two-level rmapbt"
+
 echo "Fuzz rmapbt keyptr"
-_scratch_xfs_fuzz_metadata '' 'offline' 'agf 0' 'addr rmaproot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline' "$path" 'addr rmaproot' >> $seqres.full
 echo "Done fuzzing rmapbt keyptr"
 
 # success, all done
index a9b914d9f58a00b8749113bd84b189b35019c213..f7a336b170d02b53e4c63e7a09ff8e0eb00a5c61 100755 (executable)
@@ -26,8 +26,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+       _fail "could not find two-level rmapbt"
+
 echo "Fuzz rmapbt keyptr"
-_scratch_xfs_fuzz_metadata '' 'online' 'agf 0' 'addr rmaproot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online' "$path" 'addr rmaproot' >> $seqres.full
 echo "Done fuzzing rmapbt keyptr"
 
 # success, all done
index c39a917500961cc8b3cca87be762a354d6e9dfcc..22503225273cb281ca4dd19b319dbea7cde95ef8 100755 (executable)
@@ -26,8 +26,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+       _fail "could not find two-level refcountbt"
+
 echo "Fuzz refcountbt"
-_scratch_xfs_fuzz_metadata '' 'offline'  'agf 0' 'addr refcntroot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline'  "$path" 'addr refcntroot' >> $seqres.full
 echo "Done fuzzing refcountbt"
 
 # success, all done
index 324aa9fe7d2eafc19bff53b2ca44244e10ce530f..e0c20044ec8ab70bcbfb8dd92184fa4a5fa4ee20 100755 (executable)
@@ -4,7 +4,7 @@
 #
 # FS QA Test No. 373
 #
-# Populate a XFS filesystem and fuzz every refcountbt field.
+# Populate a XFS filesystem and fuzz every refcountbt key/pointer field.
 # Use xfs_scrub to fix the corruption.
 #
 . ./common/preamble
@@ -26,8 +26,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+       _fail "could not find two-level refcountbt"
+
 echo "Fuzz refcountbt"
-_scratch_xfs_fuzz_metadata '' 'online'  'agf 0' 'addr refcntroot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online'  "$path" 'addr refcntroot' >> $seqres.full
 echo "Done fuzzing refcountbt"
 
 # success, all done
index e98a63ebf512f710b47dc9cdadf51bad6755f03f..388ed7d190f515a3bb0b11d8d756cc610453bc00 100755 (executable)
@@ -26,8 +26,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+       _fail "could not find two-level refcountbt"
+
 echo "Fuzz refcountbt"
-_scratch_xfs_fuzz_metadata '' 'offline'  'agf 0' 'addr refcntroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'offline'  "$path" 'addr refcntroot' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing refcountbt"
 
 # success, all done
index cfe77961021d709c53088df18170625d0d8a5839..a9fc25ce7d916b5f3b31f14a1008cd9871920100 100755 (executable)
@@ -26,8 +26,11 @@ _require_scratch_xfs_fuzz_fields
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+       _fail "could not find two-level refcountbt"
+
 echo "Fuzz refcountbt"
-_scratch_xfs_fuzz_metadata '' 'online'  'agf 0' 'addr refcntroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'online'  "$path" 'addr refcntroot' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing refcountbt"
 
 # success, all done
index 77db492e26601338a72c983024ff45ef20a08233..339f12976a2f7003a78b238422d3f2f332fbf181 100755 (executable)
@@ -4,95 +4,34 @@
 #
 # FS QA Test No. 422
 #
-# Race freeze and rmapbt repair for a while to see if we crash or livelock.
-# rmapbt repair requires us to freeze the filesystem to stop all filesystem
-# activity, so we can't have userspace wandering in and thawing it.
+# Race fsstress and rmapbt repair for a while to see if we crash or livelock.
 #
 . ./common/preamble
-_begin_fstest dangerous_scrub dangerous_online_repair
+_begin_fstest online_repair dangerous_fsstress_repair freeze
 
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
 _register_cleanup "_cleanup" BUS
 
 # Import common functions.
 . ./common/filter
 . ./common/fuzzy
 . ./common/inject
-. ./common/xfs
 
 # real QA test starts here
 _supported_fs xfs
-_require_xfs_scratch_rmapbt
-_require_xfs_io_command "scrub"
-_require_xfs_io_error_injection "force_repair"
-_require_command "$KILLALL_PROG" killall
+_require_scratch
+_require_xfs_stress_online_repair
 
-echo "Format and populate"
 _scratch_mkfs > "$seqres.full" 2>&1
 _scratch_mount
-
-STRESS_DIR="$SCRATCH_MNT/testdir"
-mkdir -p $STRESS_DIR
-
-for i in $(seq 0 9); do
-       mkdir -p $STRESS_DIR/$i
-       for j in $(seq 0 9); do
-               mkdir -p $STRESS_DIR/$i/$j
-               for k in $(seq 0 9); do
-                       echo x > $STRESS_DIR/$i/$j/$k
-               done
-       done
-done
-
-cpus=$(( $($here/src/feature -o) * 4 * LOAD_FACTOR))
-
-echo "Concurrent repair"
-filter_output() {
-       egrep -v '(Device or resource busy|Invalid argument)'
-}
-freeze_loop() {
-       end="$1"
-
-       while [ "$(date +%s)" -lt $end ]; do
-               $XFS_IO_PROG -x -c 'freeze' -c 'thaw' $SCRATCH_MNT 2>&1 | filter_output
-       done
-}
-repair_loop() {
-       end="$1"
-
-       while [ "$(date +%s)" -lt $end ]; do
-               $XFS_IO_PROG -x -c 'repair rmapbt 0' -c 'repair rmapbt 1' $SCRATCH_MNT 2>&1 | filter_output
-       done
-}
-stress_loop() {
-       end="$1"
-
-       FSSTRESS_ARGS=$(_scale_fsstress_args -p 4 -d $SCRATCH_MNT -n 2000 $FSSTRESS_AVOID)
-       while [ "$(date +%s)" -lt $end ]; do
-               $FSSTRESS_PROG $FSSTRESS_ARGS >> $seqres.full
-       done
-}
-$XFS_IO_PROG -x -c 'inject force_repair' $SCRATCH_MNT
-
-start=$(date +%s)
-end=$((start + (30 * TIME_FACTOR) ))
-
-echo "Loop started at $(date --date="@${start}"), ending at $(date --date="@${end}")" >> $seqres.full
-stress_loop $end &
-freeze_loop $end &
-repair_loop $end &
-
-# Wait until 2 seconds after the loops should have finished...
-while [ "$(date +%s)" -lt $((end + 2)) ]; do
-       sleep 1
-done
-
-# ...and clean up after the loops in case they didn't do it themselves.
-$KILLALL_PROG -TERM xfs_io fsstress >> $seqres.full 2>&1
-$XFS_IO_PROG -x -c 'thaw' $SCRATCH_MNT >> $seqres.full 2>&1
-
-echo "Loop finished at $(date)" >> $seqres.full
-echo "Test done"
+_require_xfs_has_feature "$SCRATCH_MNT" rmapbt
+_scratch_xfs_stress_online_repair -s "repair rmapbt %agno%"
 
 # success, all done
+echo Silence is golden
 status=0
 exit
index 3818c48fa87bf139e14007db12b0f3417c6014db..f70693fde68ed5bb4052b26789502b1f0a1fb200 100644 (file)
@@ -1,4 +1,2 @@
 QA output created by 422
-Format and populate
-Concurrent repair
-Test done
+Silence is golden
index be56f311d106887e686111ad3b3b5c4d0e8bf60b..a94118cc7da8711b55b8ef79fd0e7fecd3f9245e 100755 (executable)
@@ -10,7 +10,7 @@
 # count them if the fork is in btree format.
 #
 . ./common/preamble
-_begin_fstest dangerous_scrub
+_begin_fstest dangerous_scrub prealloc
 
 _register_cleanup "_cleanup" BUS
 
@@ -18,7 +18,6 @@ _register_cleanup "_cleanup" BUS
 . ./common/filter
 . ./common/fuzzy
 . ./common/inject
-. ./common/xfs
 
 # real QA test starts here
 _supported_fs xfs
index 86012f0b0c34b284eb0ad86cf08101e1d005e154..4eae92e75b9947ef97ce3cace47cd40bc3a8b6ae 100755 (executable)
@@ -20,15 +20,19 @@ _begin_fstest auto quick dir metadata metadump
 _cleanup()
 {
        cd /
-       rm -f "$tmp".* $metadump_file $metadump_img
+       rm -f "$tmp".*
+       _xfs_cleanup_verify_metadump
 }
 
 # Import common functions.
 . ./common/filter
+. ./common/metadump
 
 # real QA test starts here
 _supported_fs xfs
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
 _require_scratch
+_xfs_setup_verify_metadump
 
 rm -f "$seqres.full"
 
@@ -49,12 +53,10 @@ echo "Format and mount"
 # block.  8187 hashes/dablk / 248 dirents/dirblock = ~33 dirblocks per
 # dablock.  33 dirblocks * 64k mean that we can expand a directory by
 # 2112k before we have to allocate another da btree block.
+
 _scratch_mkfs -b size=1k -n size=64k > "$seqres.full" 2>&1
 _scratch_mount >> "$seqres.full" 2>&1
 
-metadump_file="$TEST_DIR/meta-$seq"
-metadump_img="$TEST_DIR/img-$seq"
-rm -f $metadump_file $metadump_img
 testdir="$SCRATCH_MNT/test-$seq"
 max_fname_len=255
 blksz=$(_get_block_size $SCRATCH_MNT)
@@ -84,12 +86,8 @@ extlen="$(check_for_long_extent $dir_inum)"
 echo "qualifying extent: $extlen blocks" >> $seqres.full
 test -n "$extlen" || _notrun "could not create dir extent > 1000 blocks"
 
-echo "Try to metadump"
-_scratch_xfs_metadump $metadump_file -w
-xfs_mdrestore $metadump_file $metadump_img
-
-echo "Check restored metadump image"
-$XFS_REPAIR_PROG -n $metadump_img >> $seqres.full 2>&1
+echo "Try to metadump, restore and check restored metadump image"
+_xfs_verify_metadumps '-a -o -w'
 
 # success, all done
 status=0
index 1f135d16ef5047c72db59d7ff2d41d91c7ebe011..37bac90203392c24681faf2bb02553e2696119e9 100644 (file)
@@ -2,5 +2,4 @@ QA output created by 432
 Format and mount
 Create huge dir
 Check for > 1000 block extent?
-Try to metadump
-Check restored metadump image
+Try to metadump, restore and check restored metadump image
index 576f1b0e1e9caf6c533687cd4b7233da79403df9..ca80e127530d34e149038a59ed4cb0563f36bfa3 100755 (executable)
@@ -30,11 +30,11 @@ _begin_fstest auto quick clone fsr
 
 # real QA test starts here
 _supported_fs xfs
-_require_loadable_fs_module "xfs"
 _require_quota
 _require_scratch_reflink
 _require_cp_reflink
 _require_command "$XFS_FSR_PROG" "xfs_fsr"
+_require_xfs_io_command falloc # fsr requires support for preallocation
 _require_xfs_io_error_injection "bmap_finish_one"
 _require_xfs_scratch_rmapbt
 
@@ -64,7 +64,7 @@ $XFS_FSR_PROG -v -d $testdir/file1 >> $seqres.full 2>&1
 echo "FS should be shut down, touch will fail"
 touch $SCRATCH_MNT/badfs 2>&1 | _filter_scratch
 
-echo "Remount to replay log" | tee /dev/ttyprintk
+echo "Remount to replay log" | _tee_kernlog
 _scratch_unmount
 _scratch_dump_log >> $seqres.full
 _scratch_xfs_db -x -c 'agf 0' -c 'addr refcntroot' -c 'fuzz -d recs[1].startblock ones' >> $seqres.full
@@ -76,7 +76,7 @@ _scratch_unmount 2> /dev/null
 rm -f ${RESULT_DIR}/require_scratch
 
 echo "See if we leak"
-_reload_fs_module "xfs"
+_test_loadable_fs_module "xfs"
 
 # success, all done
 status=0
index ded942a12812b1da0a35103a9833978aa71bf635..b52e9287dfcb758dfbbebb0aae2cc7329c8a8888 100755 (executable)
@@ -24,7 +24,6 @@ _begin_fstest auto quick clone
 
 # real QA test starts here
 _supported_fs xfs
-_require_loadable_fs_module "xfs"
 _require_quota
 _require_scratch_reflink
 _require_cp_reflink
@@ -46,7 +45,7 @@ _pwrite_byte 0x62 0 $((blksz * blks)) $testdir/file1 >> $seqres.full
 _pwrite_byte 0x63 0 $blksz $testdir/file2 >> $seqres.full
 _reflink_range $testdir/file2 0 $testdir/file1 $blksz $blksz >> $seqres.full
 
-echo "Remount to check recovery" | tee /dev/ttyprintk
+echo "Remount to check recovery" | _tee_kernlog
 _scratch_unmount
 _scratch_xfs_db -x -c 'agf 0' -c 'addr refcntroot' -c 'fuzz -d recs[1].startblock ones' >> $seqres.full
 _scratch_xfs_db -x -c 'agf 0' -c 'addr refcntroot' -c p >> $seqres.full
@@ -55,7 +54,7 @@ _scratch_unmount 2> /dev/null
 rm -f ${RESULT_DIR}/require_scratch
 
 echo "See if we leak"
-_reload_fs_module "xfs"
+_test_loadable_fs_module "xfs"
 
 # success, all done
 status=0
index d99183cfcbfe2fbed1cbde80e121dafe1ccfc996..02bcd66900a179b58c3ef86b3c8149cb01a90491 100755 (executable)
@@ -27,9 +27,9 @@ _begin_fstest auto quick clone fsr
 
 # real QA test starts here
 _supported_fs xfs
-_require_loadable_fs_module "xfs"
 _require_scratch_reflink
 _require_cp_reflink
+_require_xfs_io_command falloc # fsr requires support for preallocation
 _require_command "$XFS_FSR_PROG" "xfs_fsr"
 _require_xfs_io_error_injection "bmap_finish_one"
 _require_xfs_scratch_rmapbt
@@ -42,6 +42,7 @@ _scratch_mount -o noquota >> "$seqres.full" 2>&1
 
 testdir="$SCRATCH_MNT/test-$seq"
 blksz=65536
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
 blks=3
 mkdir "$testdir"
 
@@ -60,7 +61,7 @@ $XFS_FSR_PROG -v -d $testdir/file1 >> $seqres.full 2>&1
 echo "FS should be shut down, touch will fail"
 touch $SCRATCH_MNT/badfs 2>&1 | _filter_scratch
 
-echo "Remount to replay log" | tee /dev/ttyprintk
+echo "Remount to replay log" | _tee_kernlog
 _scratch_unmount
 _scratch_dump_log >> $seqres.full
 _scratch_xfs_db -x -c 'agf 0' -c 'addr refcntroot' -c 'fuzz -d recs[1].startblock ones' >> $seqres.full
@@ -70,7 +71,7 @@ _scratch_unmount 2> /dev/null
 rm -f ${RESULT_DIR}/require_scratch
 
 echo "See if we leak"
-_reload_fs_module "xfs"
+_test_loadable_fs_module "xfs"
 
 # success, all done
 status=0
index c3008b1c9923e708ceba16791afe4ef5b8291a97..0425c5b1f640e72cd690bd737300636b4492dc91 100755 (executable)
 # Fixed by upstream commit 373b058 ("xfs: Properly retry failed dquot
 # items in case of error during buffer writeback")
 . ./common/preamble
-_begin_fstest auto quick quota
+_begin_fstest auto quick quota freeze
 
 # Override the default cleanup function.
 _cleanup()
 {
+       # Make sure $SCRATCH_MNT is unfreezed
+       xfs_freeze -u $SCRATCH_MNT 2>/dev/null
        [ -z "${interval}" ] || \
                sysctl -w fs.xfs.xfssyncd_centisecs=${interval} >/dev/null 2>&1
        cd /
index b7929493d168dfdd199409dcaff6fefb2d0706a8..3497e67cc12ad31050fa40cc936ea1af5e23fbcb 100755 (executable)
@@ -20,8 +20,16 @@ _begin_fstest auto quick fuzzers log
 # real QA test starts here
 _supported_fs xfs
 _require_scratch_nocheck
-# We corrupt XFS on purpose, and check if assert failures would crash system.
-_require_no_xfs_bug_on_assert
+
+# We corrupt XFS on purpose, and check if assert failures would crash the
+# system when trying to xfs_log_mount.  Hence this is a regression test for:
+_fixed_by_kernel_commit 9c92ee208b1f \
+       "xfs: validate sb_logsunit is a multiple of the fs blocksize"
+
+# This used to be _require_no_xfs_bug_on_assert, but now we've fixed the sb
+# verifier to reject this before xfs_log_mount gets to it:
+_fixed_by_kernel_commit f1e1765aad7d \
+       "xfs: journal geometry is not properly bounds checked"
 
 rm -f "$seqres.full"
 
@@ -33,7 +41,7 @@ blksz=$(_scratch_xfs_get_sb_field blocksize)
 _scratch_xfs_set_sb_field logsunit $((blksz - 1)) >> $seqres.full 2>&1
 
 # Check if logsunit is set correctly
-lsunit=$(_scratch_xfs_get_sb_field logsunit)
+lsunit=$(_scratch_xfs_get_sb_field logsunit 2>/dev/null)
 [ $lsunit -ne $((blksz - 1)) ] && _notrun "failed to set sb_logsunit"
 
 # Mount and writing log may trigger a crash on buggy kernel
index 496ee04edfa7bf5ce41383ea1e4efd54721407c3..368ee8a05d0b0b8ab9fabf07d757ff3fb84553e7 100755 (executable)
@@ -20,6 +20,7 @@ _begin_fstest auto quick clone quota
 _supported_fs xfs
 
 _require_quota
+_require_scratch_delalloc
 _require_scratch_reflink
 _require_cp_reflink
 _require_user
index f2390bf3823c67bfd25b6a2d894a3a70cc99f63b..56828decae41a4194a9bbf717abcf1b71a144a16 100755 (executable)
@@ -15,7 +15,7 @@
 # accounting inconsistency.
 #
 . ./common/preamble
-_begin_fstest auto quick ioctl fsr punch
+_begin_fstest auto quick ioctl fsr punch fiemap prealloc
 
 # Import common functions.
 . ./common/filter
@@ -30,6 +30,7 @@ _require_test_program "punch-alternating"
 _require_xfs_io_command "falloc"
 _require_xfs_io_command "fpunch"
 _require_xfs_io_command "swapext"
+_require_xfs_io_command "fiemap"
 
 _scratch_mkfs | _filter_mkfs >> $seqres.full 2> $tmp.mkfs
 _scratch_mount
index 69158f037b5ff2e9c5b0dcc7dd32f029111195cf..db7418c55deed315e18ce03a6d0bb47f7d65822f 100755 (executable)
@@ -11,7 +11,7 @@
 # about the fix.
 #
 . ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick prealloc
 
 _register_cleanup "_cleanup; rm -f $tmp.*"
 
@@ -62,7 +62,7 @@ runtest() {
        cmd="$1"
 
        # Format filesystem
-       echo "TEST $cmd" | tee /dev/ttyprintk
+       echo "TEST $cmd" | _tee_kernlog
        echo "TEST $cmd" >> $seqres.full
        _scratch_mkfs >> $seqres.full
 
index 9c55cac7d7e191d7bab55dc715d497c8c88a6653..ca956efc760eb2c2d2804bb37d4ce7e1145ea7ee 100755 (executable)
@@ -18,7 +18,7 @@
 # freed inodes in a partially initialized state.
 #
 . ./common/preamble
-_begin_fstest auto quick filestreams
+_begin_fstest auto quick filestreams prealloc
 
 # Import common functions.
 . ./common/filter
index d35e55cb00beeaf6bc1f15b2429bb099a2367010..a2ba49dccb6cc772cb199819925923b5b8081afe 100755 (executable)
@@ -8,7 +8,7 @@
 # after the rmapbt has grown in size.
 #
 . ./common/preamble
-_begin_fstest auto quick rmap
+_begin_fstest auto quick rmap prealloc
 
 # Import common functions.
 . ./common/filter
index 96820bc3b806f48374c248c96a2484995bd65f4a..9f06c71fa236534aaff5bd9928093c86926d45ce 100755 (executable)
@@ -29,8 +29,13 @@ echo "Fuzz AGFL"
 _scratch_xfs_fuzz_metadata '' 'none' 'agfl 0' >> $seqres.full
 echo "Done fuzzing AGFL"
 
-echo "Fuzz AGFL flfirst"
+# Restore a correct copy of the filesystem before we start the second round of
+# fuzzing.  This avoids corruption errors from xfs_db when we probe for flfirst
+# in the AGF and later when _scratch_xfs_fuzz_metadata probes the AGFL fields.
+__scratch_xfs_fuzz_mdrestore
 flfirst=$(_scratch_xfs_db -c 'agf 0' -c 'p flfirst' | sed -e 's/flfirst = //g')
+
+echo "Fuzz AGFL flfirst"
 SCRATCH_XFS_LIST_METADATA_FIELDS="bno[${flfirst}]" _scratch_xfs_fuzz_metadata '' 'none' 'agfl 0' >> $seqres.full
 echo "Done fuzzing AGFL flfirst"
 
index 332eeb983743f7ff2dbeaedbf1fb99d576b8cf5a..64cd6b4b8204e7534188b6dccfd2896ca97e39f9 100755 (executable)
@@ -25,8 +25,11 @@ _disable_dmesg_check
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+       _fail "could not find two-level bnobt"
+
 echo "Fuzz bnobt recs"
-_scratch_xfs_fuzz_metadata '' 'none'  'agf 0' 'addr bnoroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none'  "$path" 'addr bnoroot' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing bnobt recs"
 
 # success, all done
index ce03d687abf97b2f95f9348043ab7731267fef5c..8d87ec569f7170c557571c2f8cc8d98102ea5455 100755 (executable)
@@ -25,8 +25,11 @@ _disable_dmesg_check
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+       _fail "could not find two-level bnobt"
+
 echo "Fuzz bnobt keyptr"
-_scratch_xfs_fuzz_metadata '' 'none'  'agf 0' 'addr bnoroot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none'  "$path" 'addr bnoroot' >> $seqres.full
 echo "Done fuzzing bnobt keyptr"
 
 # success, all done
index d166160f8721488a59f8846526c2bb5bddae30b5..5989bc1e6eaa834294e5600b658d57d01a9474b3 100755 (executable)
@@ -25,8 +25,11 @@ _disable_dmesg_check
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'cnt' 2)" || \
+       _fail "could not find two-level cntbt"
+
 echo "Fuzz cntbt"
-_scratch_xfs_fuzz_metadata '' 'none'  'agf 0' 'addr cntroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none'  "$path" 'addr cntroot' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing cntbt"
 
 # success, all done
index 0daafa306612e85abd5a648fc0a724f93c37cb46..71174770113337a734d52c0bc0f856a38483bc09 100755 (executable)
@@ -25,8 +25,11 @@ _disable_dmesg_check
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+       _fail "could not find two-level inobt"
+
 echo "Fuzz inobt"
-_scratch_xfs_fuzz_metadata '' 'none'  'agi 1' 'addr root' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none'  "$path" 'addr root' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing inobt"
 
 # success, all done
index 2d20c69d872613154296dbde9c845a9fb2375baf..7c1327b05240e795b272679e1ade046bb9dbbdd9 100755 (executable)
@@ -26,8 +26,11 @@ _require_xfs_finobt
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'fino' 2)" || \
+       _fail "could not find two-level finobt"
+
 echo "Fuzz finobt"
-_scratch_xfs_fuzz_metadata '' 'none'  'agi 0' 'addr free_root' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none'  "$path" 'addr free_root' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing finobt"
 
 # success, all done
index 587facc03c47072e855369cf5360e419f01bf84a..1ee4d27e92577f6e9827e9e910a854962edc188b 100755 (executable)
@@ -26,8 +26,11 @@ _disable_dmesg_check
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+       _fail "could not find two-level rmapbt"
+
 echo "Fuzz rmapbt recs"
-_scratch_xfs_fuzz_metadata '' 'none' 'agf 0' 'addr rmaproot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none' "$path" 'addr rmaproot' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing rmapbt recs"
 
 # success, all done
index 7270f7017a93a040d631b8e80982a74b809868e1..7dd2d37deaeff2b03be06282f5279febe47f3e0a 100755 (executable)
@@ -26,8 +26,11 @@ _disable_dmesg_check
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+       _fail "could not find two-level rmapbt"
+
 echo "Fuzz rmapbt keyptr"
-_scratch_xfs_fuzz_metadata '' 'none' 'agf 0' 'addr rmaproot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none' "$path" 'addr rmaproot' >> $seqres.full
 echo "Done fuzzing rmapbt keyptr"
 
 # success, all done
index 59d25ae1c08c7dee5223c0bc69ce38c30ec99eca..719901e66ddb5acfd7c66424e91260f8987059b9 100755 (executable)
@@ -4,7 +4,7 @@
 #
 # FS QA Test No. 464
 #
-# Populate a XFS filesystem and fuzz every refcountbt field.
+# Populate a XFS filesystem and fuzz every refcountbt key/pointer field.
 # Do not fix the filesystem, to test metadata verifiers.
 
 . ./common/preamble
@@ -27,8 +27,11 @@ _disable_dmesg_check
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+       _fail "could not find two-level refcountbt"
+
 echo "Fuzz refcountbt"
-_scratch_xfs_fuzz_metadata '' 'none'  'agf 0' 'addr refcntroot' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none'  "$path" 'addr refcntroot' >> $seqres.full
 echo "Done fuzzing refcountbt"
 
 # success, all done
index d7b0101a8298ce73e425a62f4c8fb84e7a232774..56670ba1782cb05a5705a5729816da83081cefc3 100755 (executable)
@@ -27,8 +27,11 @@ _disable_dmesg_check
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+       _fail "could not find two-level refcountbt"
+
 echo "Fuzz refcountbt"
-_scratch_xfs_fuzz_metadata '' 'none'  'agf 0' 'addr refcntroot' 'addr ptrs[1]' >> $seqres.full
+_scratch_xfs_fuzz_metadata '' 'none'  "$path" 'addr refcntroot' 'addr ptrs[1]' >> $seqres.full
 echo "Done fuzzing refcountbt"
 
 # success, all done
index b5597708e6dc94d51a073a58ed106249489e7b1a..9672f95d566e5a590c36ad0e3082569923e4c11e 100755 (executable)
@@ -44,7 +44,7 @@ struct ftrace_chk {
        char                    *str;
 } syms[] = {
 ENDL
-egrep '(__print_flags|__print_symbolic)' $ftrace_dir*/*/format | \
+grep -E '(__print_flags|__print_symbolic)' $ftrace_dir*/*/format | \
        sed -f $sedprog | grep '^{' | sort | uniq >> $cprog
 cat >> $cprog << ENDL
 };
index 6c4bfd1c453ef44b5e350cb515812c8c39c3f916..01cff7b08da914ed53267bf99899c843e1c1c620 100755 (executable)
@@ -4,11 +4,11 @@
 #
 # FS QA Test No. 503
 #
-# Populate a XFS filesystem and ensure that metadump, mdrestore, and copy
-# all work properly.
+# Populate a XFS filesystem and ensure that metadump and mdrestore all work
+# properly.
 #
 . ./common/preamble
-_begin_fstest auto copy metadump
+_begin_fstest auto metadump
 
 _register_cleanup "_cleanup" BUS
 
@@ -17,76 +17,45 @@ _cleanup()
 {
        cd /
        rm -rf $tmp.* $testdir
+       _xfs_cleanup_verify_metadump
 }
 
 # Import common functions.
 . ./common/filter
 . ./common/populate
+. ./common/metadump
 
 testdir=$TEST_DIR/test-$seq
 
 # real QA test starts here
 _supported_fs xfs
 
-_require_xfs_copy
+_require_command "$XFS_MDRESTORE_PROG" "xfs_mdrestore"
+_require_loop
 _require_scratch_nocheck
 _require_populate_commands
+_xfs_skip_online_rebuild
+_xfs_skip_offline_rebuild
+_xfs_setup_verify_metadump
 
 echo "Format and populate"
 _scratch_populate_cached nofill > $seqres.full 2>&1
 
 mkdir -p $testdir
 metadump_file=$testdir/scratch.md
-metadump_file_a=${metadump_file}.a
-metadump_file_g=${metadump_file}.g
-metadump_file_ag=${metadump_file}.ag
 copy_file=$testdir/copy.img
 
-echo metadump
-_scratch_xfs_metadump $metadump_file >> $seqres.full
+echo "metadump and mdrestore"
+_xfs_verify_metadumps
 
-echo metadump a
-_scratch_xfs_metadump $metadump_file_a -a >> $seqres.full
+echo "metadump a and mdrestore"
+_xfs_verify_metadumps '-a'
 
-echo metadump g
-_scratch_xfs_metadump $metadump_file_g -g >> $seqres.full
+echo "metadump o and mdrestore"
+_xfs_verify_metadumps '-o'
 
-echo metadump ag
-_scratch_xfs_metadump $metadump_file_ag -a -g >> $seqres.full
-
-echo copy
-$XFS_COPY_PROG $SCRATCH_DEV $copy_file >> $seqres.full
-_check_scratch_fs $copy_file
-
-echo recopy
-$XFS_COPY_PROG $copy_file $SCRATCH_DEV >> $seqres.full
-_scratch_mount
-_check_scratch_fs
-_scratch_unmount
-
-echo mdrestore
-xfs_mdrestore $metadump_file $SCRATCH_DEV
-_scratch_mount
-_check_scratch_fs
-_scratch_unmount
-
-echo mdrestore a
-xfs_mdrestore $metadump_file_a $SCRATCH_DEV
-_scratch_mount
-_check_scratch_fs
-_scratch_unmount
-
-echo mdrestore g
-xfs_mdrestore $metadump_file_g $SCRATCH_DEV
-_scratch_mount
-_check_scratch_fs
-_scratch_unmount
-
-echo mdrestore ag
-xfs_mdrestore $metadump_file_ag $SCRATCH_DEV
-_scratch_mount
-_check_scratch_fs
-_scratch_unmount
+echo "metadump ao and mdrestore"
+_xfs_verify_metadumps '-a -o'
 
 # success, all done
 status=0
index 8ef31dbef3bef87b92977840c83295d816a3cc8a..7f3d3a5f246ef63db38bfa83ba2890032e11f125 100644 (file)
@@ -1,12 +1,6 @@
 QA output created by 503
 Format and populate
-metadump
-metadump a
-metadump g
-metadump ag
-copy
-recopy
-mdrestore
-mdrestore a
-mdrestore g
-mdrestore ag
+metadump and mdrestore
+metadump a and mdrestore
+metadump o and mdrestore
+metadump ao and mdrestore
index 71fb6e9d3c45c5c5ea3a3592e1f85d87aa255797..0601b1a85fb4cf589689497a033c9a374fe6f0b9 100755 (executable)
@@ -29,7 +29,7 @@ esac
 _require_command "$(type -P $CAT)" $CAT
 
 for COMMAND in `$XFS_SPACEMAN_PROG -c help $TEST_DIR | awk '{print $1}' | grep -v "^Use"`; do
-  $CAT "$MANPAGE" | egrep -q "^\.B.*$COMMAND" || \
+  $CAT "$MANPAGE" | grep -E -q "^\.B.*$COMMAND" || \
        echo "$COMMAND not documented in the xfs_spaceman manpage"
 done
 
index 157dac293a021bb20e16e6a4e923c3bc6eab3ff7..1c75fefa76712b6e56adfd1423571f2eee94e080 100755 (executable)
@@ -21,6 +21,9 @@ _require_xfs_spaceman_command "health"
 
 _scratch_mkfs > $seqres.full 2>&1
 _scratch_mount
+
+_require_scratch_xfs_scrub
+
 _scratch_cycle_mount   # make sure we haven't run quotacheck on this mount
 
 # Haven't checked anything, it should tell us to run scrub
index b9c9ab29d8dbb08c305b39b6cf7c1aa9f04b1921..8757152e5a370033cbd5cf496f2358424337b1cf 100755 (executable)
@@ -44,6 +44,9 @@ echo "Format and mount"
 _scratch_mkfs > "$seqres.full" 2>&1
 _scratch_mount
 
+fs_blksz=$(_get_block_size $SCRATCH_MNT)
+_require_congruent_file_oplen $SCRATCH_MNT $((MAXEXTLEN * fs_blksz))
+
 # Create a huge sparse filesystem on the scratch device because that's what
 # we're going to need to guarantee that we have enough blocks to overflow in
 # the first place.  We need to have at least enough free space on that huge fs
index bfdfd4f612991573e5851ead82fce7643dbe7242..ce2bb34916c90f73da066703d469b93d9920fa55 100755 (executable)
@@ -7,7 +7,7 @@
 # XFS mount options sanity check, refer to 'man 5 xfs'.
 #
 . ./common/preamble
-_begin_fstest auto mount
+_begin_fstest auto mount prealloc
 
 # Override the default cleanup function.
 _cleanup()
@@ -31,6 +31,9 @@ _cleanup()
 
 # real QA test starts here
 _supported_fs xfs
+_fixed_by_kernel_commit 237d7887ae72 \
+       "xfs: show the proper user quota options"
+
 _require_test
 _require_loop
 _require_xfs_io_command "falloc"
@@ -175,7 +178,7 @@ echo "** start xfs mount testing ..."
 # Test allocsize=size
 # Valid values for this option are page size (typically 4KiB) through to 1GiB
 do_mkfs
-pagesz=$(get_page_size)
+pagesz=$(_get_page_size)
 if [ $pagesz -ge 1024 ];then
        pagesz="$((pagesz / 1024))k"
 fi
index cf5588f25619ec28bc643b7fdcf2018be2fe06cb..94f98398f32dbc7de471b27e79e35275785e33f9 100755 (executable)
@@ -41,7 +41,7 @@ truncate -s 128m $file
 $MKFS_XFS_PROG $file >> /dev/null
 
 for COMMAND in `$XFS_DB_PROG -x -c help $file | awk '{print $1}' | grep -v "^Use"`; do
-  $CAT "$MANPAGE" | egrep -q "^\.B.*$COMMAND" || \
+  $CAT "$MANPAGE" | grep -E -q "^\.B.*$COMMAND" || \
        echo "$COMMAND not documented in the xfs_db manpage"
 done
 
index 2d7bbb357768d21b0f7ea2a65744d9d65368e005..adb2bd6f1795d56d34f1abe1a933aeba21174414 100755 (executable)
@@ -37,7 +37,7 @@ esac
 _require_command "$(type -P $CAT)" $CAT
 
 for COMMAND in `$XFS_QUOTA_PROG -x -c help $file | awk '{print $1}' | grep -v "^Use"`; do
-  $CAT "$MANPAGE" | egrep -q "^\.B.*$COMMAND" || \
+  $CAT "$MANPAGE" | grep -E -q "^\.B.*$COMMAND" || \
        echo "$COMMAND not documented in the xfs_quota manpage"
 done
 
index 9e1b993174a2fb25a9e1993aa2780fea7414352f..1bf6f858d5286d775f605f36757d1bd285f8bfa1 100755 (executable)
@@ -31,7 +31,7 @@ _supports_xfs_scrub $TEST_DIR $TEST_DEV && run_scrub=1
 
 log()
 {
-       echo "$@" | tee -a $seqres.full /dev/ttyprintk
+       echo "$*" | _tee_kernlog $seqres.full
 }
 
 __test_mount_opts()
index 512f795f525c194f1b5055f6315621d6c7f8a7dd..68438e544ea0893d89230fd5a5e519e802ac05de 100755 (executable)
@@ -15,7 +15,7 @@ _register_cleanup "_cleanup" BUS
 _cleanup()
 {
        cd /
-       $XFS_IO_PROG -x -c 'thaw' $SCRATCH_MNT > /dev/null 2>&1
+       _scratch_xfs_stress_scrub_cleanup
        rm -rf $tmp.*
 }
 
@@ -23,81 +23,18 @@ _cleanup()
 . ./common/filter
 . ./common/fuzzy
 . ./common/inject
-. ./common/xfs
 
 # real QA test starts here
 _supported_fs xfs
 _require_xfs_scratch_rmapbt
 _require_xfs_io_command "fsmap"
-_require_command "$KILLALL_PROG" killall
+_require_xfs_stress_scrub
 
-echo "Format and populate"
 _scratch_mkfs > "$seqres.full" 2>&1
 _scratch_mount
-
-STRESS_DIR="$SCRATCH_MNT/testdir"
-mkdir -p $STRESS_DIR
-
-for i in $(seq 0 9); do
-       mkdir -p $STRESS_DIR/$i
-       for j in $(seq 0 9); do
-               mkdir -p $STRESS_DIR/$i/$j
-               for k in $(seq 0 9); do
-                       echo x > $STRESS_DIR/$i/$j/$k
-               done
-       done
-done
-
-cpus=$(( $(src/feature -o) * 4 * LOAD_FACTOR))
-
-echo "Concurrent fsmap and freeze"
-filter_output() {
-       egrep -v '(Device or resource busy|Invalid argument)'
-}
-freeze_loop() {
-       end="$1"
-
-       while [ "$(date +%s)" -lt $end ]; do
-               $XFS_IO_PROG -x -c 'freeze' $SCRATCH_MNT 2>&1 | filter_output
-               $XFS_IO_PROG -x -c 'thaw' $SCRATCH_MNT 2>&1 | filter_output
-       done
-}
-fsmap_loop() {
-       end="$1"
-
-       while [ "$(date +%s)" -lt $end ]; do
-               $XFS_IO_PROG -c 'fsmap -v' $SCRATCH_MNT > /dev/null
-       done
-}
-stress_loop() {
-       end="$1"
-
-       FSSTRESS_ARGS=$(_scale_fsstress_args -p 4 -d $SCRATCH_MNT -n 2000 $FSSTRESS_AVOID)
-       while [ "$(date +%s)" -lt $end ]; do
-               $FSSTRESS_PROG $FSSTRESS_ARGS >> $seqres.full
-       done
-}
-
-start=$(date +%s)
-end=$((start + (30 * TIME_FACTOR) ))
-
-echo "Loop started at $(date --date="@${start}"), ending at $(date --date="@${end}")" >> $seqres.full
-stress_loop $end &
-freeze_loop $end &
-fsmap_loop $end &
-
-# Wait until 2 seconds after the loops should have finished...
-while [ "$(date +%s)" -lt $((end + 2)) ]; do
-       sleep 1
-done
-
-# ...and clean up after the loops in case they didn't do it themselves.
-$KILLALL_PROG -TERM xfs_io fsstress >> $seqres.full 2>&1
-$XFS_IO_PROG -x -c 'thaw' $SCRATCH_MNT >> $seqres.full 2>&1
-
-echo "Loop finished at $(date)" >> $seqres.full
-echo "Test done"
+_scratch_xfs_stress_scrub -f -i 'fsmap -v'
 
 # success, all done
+echo "Silence is golden"
 status=0
 exit
index da6366e52b287d91707a6823735955979676c403..49c53bcaa96938ebb6e71915089b06c9ddf2755c 100644 (file)
@@ -1,4 +1,2 @@
 QA output created by 517
-Format and populate
-Concurrent fsmap and freeze
-Test done
+Silence is golden
index 2fceb07c5e28598aa1ed09d38889ce3466e1f665..dd6d845e7af5a0f780f5613c0c55e10b5b8a357e 100755 (executable)
@@ -52,7 +52,7 @@ force_crafted_metadata() {
        fi
 
        _dmesg_since_test_start | tac | sed -ne "0,\#${kmsg}#p" | tac | \
-               egrep -q 'Metadata corruption detected at' && hasmsg=1
+               grep -E -q 'Metadata corruption detected at' && hasmsg=1
 
        _scratch_unmount > /dev/null 2>&1
        [ $mounted -eq 0 -o $hasmsg -eq 1 ] || \
index 29e812289b04af504d85ec0e37774332e4bb2840..2bd8c289f9cd6acf9e9bead917dd4e3a764d41ab 100755 (executable)
@@ -8,7 +8,7 @@
 # size is and isn't a power of 2.
 #
 . ./common/preamble
-_begin_fstest auto quick rw realtime
+_begin_fstest auto quick insert zero collapse punch rw realtime
 
 # Override the default cleanup function.
 _cleanup()
index 1cd0454d716cc97b5a1428f3b0708bac6a3edc5b..cd176877f52713c27aa58831a402dfb1c1212e33 100755 (executable)
@@ -9,7 +9,7 @@
 # mapping.
 
 . ./common/preamble
-_begin_fstest auto quick quota
+_begin_fstest auto quick quota prealloc
 
 # Import common functions.
 . ./common/filter
@@ -32,6 +32,10 @@ echo "Format and mount fs"
 _scratch_mkfs_sized $((512 * 1024 * 1024)) >> $seqres.full
 _scratch_mount -o uquota >> $seqres.full
 
+# bmap_alloc_minlen_extent only applies to the datadev space allocator, so
+# we force the filesystem not to use the realtime volume.
+_xfs_force_bdev data $SCRATCH_MNT
+
 bsize=$(_get_file_block_size $SCRATCH_MNT)
 
 echo "* Delalloc to written extent conversion"
index 9c6f44d76b81f42f5f4837449b514403d932711c..56f5e7ebdbdba34f309272bb0c5da7fd30e8e9b0 100755 (executable)
@@ -73,8 +73,7 @@ _try_scratch_mount || _notrun "Couldn't mount fs with synthetic rt volume"
 formatted_blksz="$(_get_block_size $SCRATCH_MNT)"
 test "$formatted_blksz" -ne "$dbsize" && \
        _notrun "Tried to format with $dbsize blocksize, got $formatted_blksz."
-$XFS_INFO_PROG $SCRATCH_MNT | egrep -q 'realtime.*blocks=0' && \
-       _notrun "Filesystem should have a realtime volume"
+_require_xfs_has_feature "$SCRATCH_MNT" realtime
 
 echo "Consume free space"
 fillerdir=$SCRATCH_MNT/fillerdir
diff --git a/tests/xfs/533 b/tests/xfs/533
new file mode 100755 (executable)
index 0000000..31858cc
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 FUJITSU LIMITED. All rights reserved.
+#
+# FS QA Test 533
+#
+# Regression test for xfsprogs commit
+# f4afdcb0ad11 ("xfs_db: clean up the salvage read callsites in set_cur()")
+#
+# This case test xfs_db whether can get the new magicnum field value even we
+# just have corrupted this field value.
+#
+
+. ./common/preamble
+_begin_fstest auto quick db
+
+# real QA test starts here
+_supported_fs xfs
+_fixed_by_git_commit xfsprogs f4afdcb0ad11 \
+       "xfs_db: clean up the salvage read callsites in set_cur()"
+#skip fs check because invalid superblock 1
+_require_scratch_nocheck
+
+# The error messages in the golden output come from the V5 superblock verifier
+# routines, so ignore V4 filesystems.
+_require_scratch_xfs_crc
+
+_scratch_mkfs_xfs >>$seqres.full 2>&1
+
+# write the bad magicnum field value(0) to the superblock 1
+_scratch_xfs_set_metadata_field "magicnum" "0" "sb 1"
+
+# Even magicnum field has been corrupted, we still can read this field value.
+# The error message changed in xfsprogs 5.19.
+_scratch_xfs_get_metadata_field "magicnum" "sb 1" 2>&1 | \
+       sed -e 's/Superblock has bad magic number 0x0. Not an XFS filesystem?/bad magic number/g'
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/533.out b/tests/xfs/533.out
new file mode 100644 (file)
index 0000000..7deb78a
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 533
+Allowing write of corrupted data with good CRC
+magicnum = 0
+bad magic number
+0
index 47c0dac9ced9f7a399e9c0f193529da0ee3c2d79..f17c45b8c48a8af7b3dc9e867d0e718aff965aee 100755 (executable)
@@ -7,7 +7,7 @@
 # Verify that XFS does not cause inode fork's extent count to overflow when
 # writing to an unwritten extent.
 . ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick prealloc
 
 # Import common functions.
 . ./common/filter
index 1a5da61b1c9e0dcb0db52cb5d874853f4d91f951..f76c17257f9e89c6e8ff50adf7a674818fc6483e 100755 (executable)
@@ -7,7 +7,7 @@
 # Verify that XFS does not cause inode fork's extent count to overflow when
 # writing to a shared extent.
 . ./common/preamble
-_begin_fstest auto quick clone
+_begin_fstest auto quick clone unshare
 
 # Import common functions.
 . ./common/filter
index 7d7776f76237848206821e68abba4415c5e909d9..6364db9b5dd176d84e9ddb4530cd7a723a431daf 100755 (executable)
@@ -7,7 +7,7 @@
 # Verify that XFS does not cause inode fork's extent count to overflow when
 # swapping forks between files
 . ./common/preamble
-_begin_fstest auto quick
+_begin_fstest auto quick collapse swapext
 
 # Import common functions.
 . ./common/filter
@@ -29,7 +29,7 @@ echo "Format and mount fs"
 _scratch_mkfs >> $seqres.full
 _scratch_mount >> $seqres.full
 
-bsize=$(_get_block_size $SCRATCH_MNT)
+bsize=$(_get_file_block_size $SCRATCH_MNT)
 
 srcfile=${SCRATCH_MNT}/srcfile
 donorfile=${SCRATCH_MNT}/donorfile
index e0102f48528d4795abdcda7ed9456499c54c587d..0b5772a1c9b4e4f95a934302f174bfad58c0e4bd 100755 (executable)
@@ -47,25 +47,23 @@ done
 echo "Inject bmap_alloc_minlen_extent error tag"
 _scratch_inject_error bmap_alloc_minlen_extent 1
 
-echo "Scale fsstress args"
-args=$(_scale_fsstress_args -p $((LOAD_FACTOR * 75)) -n $((TIME_FACTOR * 1000)))
-
-echo "Execute fsstress in background"
-$FSSTRESS_PROG -d $SCRATCH_MNT $args \
-                -f bulkstat=0 \
-                -f bulkstat1=0 \
-                -f fiemap=0 \
-                -f getattr=0 \
-                -f getdents=0 \
-                -f getfattr=0 \
-                -f listfattr=0 \
-                -f mread=0 \
-                -f read=0 \
-                -f readlink=0 \
-                -f readv=0 \
-                -f stat=0 \
-                -f aread=0 \
-                -f dread=0 > /dev/null 2>&1
+echo "Execute fsstress"
+$FSSTRESS_PROG -d $SCRATCH_MNT \
+               $(_scale_fsstress_args -p 75 -n 1000) \
+               -f bulkstat=0 \
+               -f bulkstat1=0 \
+               -f fiemap=0 \
+               -f getattr=0 \
+               -f getdents=0 \
+               -f getfattr=0 \
+               -f listfattr=0 \
+               -f mread=0 \
+               -f read=0 \
+               -f readlink=0 \
+               -f readv=0 \
+               -f stat=0 \
+               -f aread=0 \
+               -f dread=0 >> $seqres.full
 
 # success, all done
 status=0
index 85932c82634a00839e234991e41d9eabdb27c3fc..97ebc314e2e4d64150ad4f627b7f0e1f88e93686 100644 (file)
@@ -3,5 +3,4 @@ Format and mount fs
 Consume free space
 Create fragmented filesystem
 Inject bmap_alloc_minlen_extent error tag
-Scale fsstress args
-Execute fsstress in background
+Execute fsstress
index 4bc52c1ae8671635ec7ba87abd5bb642869dd106..778dce852cae48f2d0763b93a81083be9a8fbbdc 100755 (executable)
@@ -9,15 +9,18 @@
 # the same value as during the mount
 #
 # Regression test for commit:
-# xfs: Skip repetitive warnings about mount options
+# 92cf7d36384b xfs: Skip repetitive warnings about mount options
 
 . ./common/preamble
 _begin_fstest auto quick mount
 
 # Import common functions.
 
-_require_check_dmesg
 _supported_fs xfs
+_fixed_by_kernel_commit 92cf7d36384b \
+       "xfs: Skip repetitive warnings about mount options"
+
+_require_check_dmesg
 _require_scratch
 
 log_tag()
@@ -33,7 +36,7 @@ dmesg_since_test_tag()
 
 check_dmesg_for_since_tag()
 {
-       dmesg_since_test_tag | egrep -q "$1"
+       dmesg_since_test_tag | grep -E -q "$1"
 }
 
 echo "Silence is golden."
index 5c45eed7cdabfcb42ce1e797ac47f5f0cee55146..1540541ec4c942431ebb9f6dde73c493406ea5ef 100755 (executable)
@@ -26,6 +26,9 @@ _cleanup()
 
 # real QA test starts here
 _supported_fs xfs
+_fixed_by_kernel_commit 5ca5916b6bc9 \
+       "xfs: punch out data fork delalloc blocks on COW writeback failure"
+
 _require_scratch_reflink
 _require_cp_reflink
 _require_xfs_io_command "cowextsize"
index e3dd300ac0d8dbb0e35486c7d84aa1aa3ef674ea..57a650acab8701cb6a0758c8416c4f28d88ca9b1 100755 (executable)
@@ -8,41 +8,19 @@
 # than the root inode. Ensure that xfsdump/xfsrestore handles this.
 #
 . ./common/preamble
-_begin_fstest auto quick dump
+_begin_fstest auto quick dump prealloc
 
 # Import common functions.
 . ./common/dump
 
 _supported_fs xfs
+_require_xfs_io_command "falloc"
 _require_scratch
 
-# A large stripe unit will put the root inode out quite far
-# due to alignment, leaving free blocks ahead of it.
-_scratch_mkfs_xfs -d sunit=1024,swidth=1024 > $seqres.full 2>&1
-
-# Mounting /without/ a stripe should allow inodes to be allocated
-# in lower free blocks, without the stripe alignment.
-_scratch_mount -o sunit=0,swidth=0
-
-root_inum=$(stat -c %i $SCRATCH_MNT)
-
-# Consume space after the root inode so that the blocks before
-# root look "close" for the next inode chunk allocation
-$XFS_IO_PROG -f -c "falloc 0 16m" $SCRATCH_MNT/fillfile
-
-# And make a bunch of inodes until we (hopefully) get one lower
-# than root, in a new inode chunk.
-echo "root_inum: $root_inum" >> $seqres.full
-for i in $(seq 0 4096) ; do
-       fname=$SCRATCH_MNT/$(printf "FILE_%03d" $i)
-       touch $fname
-       inum=$(stat -c "%i" $fname)
-       [[ $inum -lt $root_inum ]] && break
-done
-
-echo "created: $inum" >> $seqres.full
-
-[[ $inum -lt $root_inum ]] || _notrun "Could not set up test"
+# Create a filesystem which contains a fake root inode
+inums=($(_scratch_xfs_create_fake_root))
+root_inum=${inums[0]}
+fake_inum=${inums[1]}
 
 # Now try a dump and restore. Cribbed from xfs/068
 _create_dumpdir_stress
diff --git a/tests/xfs/547 b/tests/xfs/547
new file mode 100755 (executable)
index 0000000..60121eb
--- /dev/null
@@ -0,0 +1,98 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test 547
+#
+# Verify that correct inode extent count fields are populated with and without
+# nrext64 feature.
+#
+. ./common/preamble
+_begin_fstest auto quick metadata
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+. ./common/inject
+. ./common/populate
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_nrext64
+_require_attrs
+_require_xfs_debug
+_require_xfs_db_command path
+_require_test_program "punch-alternating"
+_require_xfs_io_error_injection "bmap_alloc_minlen_extent"
+
+for nrext64 in 0 1; do
+       echo "* Verify extent counter fields with nrext64=${nrext64} option"
+
+       _scratch_mkfs -i nrext64=${nrext64} -d size=$((512 * 1024 * 1024)) \
+                     >> $seqres.full
+       _scratch_mount >> $seqres.full
+
+       # Force data device extents so that we can fragment the free space
+       # and force attr fork allocations to be non-contiguous
+       _xfs_force_bdev data $SCRATCH_MNT
+
+       bsize=$(_get_file_block_size $SCRATCH_MNT)
+
+       testfile=$SCRATCH_MNT/testfile
+
+       nr_blks=20
+
+       echo "Add blocks to test file's data fork"
+       $XFS_IO_PROG -f -c "pwrite 0 $((nr_blks * bsize))" $testfile \
+                    >> $seqres.full
+       $here/src/punch-alternating $testfile
+
+       echo "Consume free space"
+       fillerdir=$SCRATCH_MNT/fillerdir
+       nr_free_blks=$(stat -f -c '%f' $SCRATCH_MNT)
+       nr_free_blks=$((nr_free_blks * 90 / 100))
+
+       _fill_fs $((bsize * nr_free_blks)) $fillerdir $bsize 0 \
+                >> $seqres.full 2>&1
+
+       echo "Create fragmented filesystem"
+       for dentry in $(ls -1 $fillerdir/); do
+               $here/src/punch-alternating $fillerdir/$dentry >> $seqres.full
+       done
+
+       echo "Inject bmap_alloc_minlen_extent error tag"
+       _scratch_inject_error bmap_alloc_minlen_extent 1
+
+       echo "Add blocks to test file's attr fork"
+       attr_len=255
+       nr_attrs=$((nr_blks * bsize / attr_len))
+       for i in $(seq 1 $nr_attrs); do
+               attr="$(printf "trusted.%0247d" $i)"
+               $SETFATTR_PROG -n "$attr" $testfile >> $seqres.full 2>&1
+               [[ $? != 0 ]] && break
+       done
+
+       _scratch_unmount >> $seqres.full
+
+       dcnt=$(_scratch_xfs_get_metadata_field core.nextents \
+                                              "path /$(basename $testfile)")
+       acnt=$(_scratch_xfs_get_metadata_field core.naextents \
+                                              "path /$(basename $testfile)")
+
+       echo "nrext64: $nrext64 dcnt: $dcnt acnt: $acnt" >> $seqres.full
+
+       if [ -z "$dcnt" ] || (( $dcnt != 10 )); then
+               echo "Invalid data fork extent count: $dcnt"
+               exit 1
+       fi
+
+       if [ -z "$acnt" ] || (( $acnt < 10 )); then
+               echo "Invalid attr fork extent count: $acnt"
+               exit 1
+       fi
+done
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/547.out b/tests/xfs/547.out
new file mode 100644 (file)
index 0000000..49fcc3c
--- /dev/null
@@ -0,0 +1,13 @@
+QA output created by 547
+* Verify extent counter fields with nrext64=0 option
+Add blocks to test file's data fork
+Consume free space
+Create fragmented filesystem
+Inject bmap_alloc_minlen_extent error tag
+Add blocks to test file's attr fork
+* Verify extent counter fields with nrext64=1 option
+Add blocks to test file's data fork
+Consume free space
+Create fragmented filesystem
+Inject bmap_alloc_minlen_extent error tag
+Add blocks to test file's attr fork
diff --git a/tests/xfs/548 b/tests/xfs/548
new file mode 100755 (executable)
index 0000000..560c90f
--- /dev/null
@@ -0,0 +1,112 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test 548
+#
+# Test to verify upgrade of an existing V5 filesystem to support large extent
+# counters.
+#
+. ./common/preamble
+_begin_fstest auto quick metadata
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+. ./common/inject
+. ./common/populate
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_nrext64
+_require_attrs
+_require_xfs_debug
+_require_xfs_db_command path
+_require_test_program "punch-alternating"
+_require_xfs_io_error_injection "bmap_alloc_minlen_extent"
+
+_scratch_mkfs -d size=$((512 * 1024 * 1024)) >> $seqres.full
+_scratch_mount >> $seqres.full
+
+bsize=$(_get_file_block_size $SCRATCH_MNT)
+
+testfile=$SCRATCH_MNT/testfile
+
+nr_blks=20
+
+echo "Add blocks to file's data fork"
+$XFS_IO_PROG -f -c "pwrite 0 $((nr_blks * bsize))" $testfile \
+            >> $seqres.full
+$here/src/punch-alternating $testfile
+
+echo "Consume free space"
+fillerdir=$SCRATCH_MNT/fillerdir
+nr_free_blks=$(stat -f -c '%f' $SCRATCH_MNT)
+nr_free_blks=$((nr_free_blks * 90 / 100))
+
+_fill_fs $((bsize * nr_free_blks)) $fillerdir $bsize 0 \
+        >> $seqres.full 2>&1
+
+echo "Create fragmented filesystem"
+for dentry in $(ls -1 $fillerdir/); do
+       $here/src/punch-alternating $fillerdir/$dentry >> $seqres.full
+done
+
+echo "Inject bmap_alloc_minlen_extent error tag"
+_scratch_inject_error bmap_alloc_minlen_extent 1
+
+echo "Add blocks to file's attr fork"
+nr_blks=10
+attr_len=255
+nr_attrs=$((nr_blks * bsize / attr_len))
+for i in $(seq 1 $nr_attrs); do
+       attr="$(printf "trusted.%0247d" $i)"
+       $SETFATTR_PROG -n "$attr" $testfile >> $seqres.full 2>&1
+       [[ $? != 0 ]] && break
+done
+
+echo "Unmount filesystem"
+_scratch_unmount >> $seqres.full
+
+orig_dcnt=$(_scratch_xfs_get_metadata_field core.nextents \
+                                           "path /$(basename $testfile)")
+orig_acnt=$(_scratch_xfs_get_metadata_field core.naextents \
+                                           "path /$(basename $testfile)")
+
+echo "Upgrade filesystem to support large extent counters"
+_scratch_xfs_admin -O nrext64=1 >> $seqres.full 2>&1
+if [[ $? != 0 ]]; then
+       _notrun "Filesystem geometry is not suitable for upgrading"
+fi
+
+
+echo "Mount filesystem"
+_scratch_mount >> $seqres.full
+
+echo "Modify inode core"
+touch $testfile
+
+echo "Unmount filesystem"
+_scratch_unmount >> $seqres.full
+
+dcnt=$(_scratch_xfs_get_metadata_field core.nextents \
+                                      "path /$(basename $testfile)")
+acnt=$(_scratch_xfs_get_metadata_field core.naextents \
+                                      "path /$(basename $testfile)")
+
+echo "Verify inode extent counter values after fs upgrade"
+
+if [[ $orig_dcnt != $dcnt ]]; then
+       echo "Corrupt data extent counter"
+       exit 1
+fi
+
+if [[ $orig_acnt != $acnt ]]; then
+       echo "Corrupt attr extent counter"
+       exit 1
+fi
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/548.out b/tests/xfs/548.out
new file mode 100644 (file)
index 0000000..19a7f90
--- /dev/null
@@ -0,0 +1,12 @@
+QA output created by 548
+Add blocks to file's data fork
+Consume free space
+Create fragmented filesystem
+Inject bmap_alloc_minlen_extent error tag
+Add blocks to file's attr fork
+Unmount filesystem
+Upgrade filesystem to support large extent counters
+Mount filesystem
+Modify inode core
+Unmount filesystem
+Verify inode extent counter values after fs upgrade
diff --git a/tests/xfs/549 b/tests/xfs/549
new file mode 100755 (executable)
index 0000000..925ca99
--- /dev/null
@@ -0,0 +1,29 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 FUJITSU LIMITED. All rights reserved.
+#
+# FS QA Test No. 549
+#
+# Regression test for xfsprogs commit
+# 50dba8189b1f ("mkfs: terminate getsubopt arrays properly")
+#
+# This case test mkfs.xfs whether can terminate getsubopt arrays properly.
+# If not, it will trigger segmentation fault.
+#
+
+. ./common/preamble
+_begin_fstest auto quick mkfs
+
+# real QA test starts here
+_supported_fs xfs
+_fixed_by_git_commit xfsprogs 50dba8189b1f \
+       "mkfs: terminate getsubopt arrays properly"
+_require_test
+
+$MKFS_XFS_PROG -f -d agcount=4 -d garbage=0 >> $seqres.full  2>&1
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/549.out b/tests/xfs/549.out
new file mode 100644 (file)
index 0000000..4e3acd3
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 549
+Silence is golden
diff --git a/tests/xfs/550 b/tests/xfs/550
new file mode 100755 (executable)
index 0000000..87ae411
--- /dev/null
@@ -0,0 +1,50 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Fujitsu Limited.  All Rights Reserved.
+#
+# FS QA Test No. 550
+#
+# Test memory failure mechanism when dax enabled
+#
+. ./common/preamble
+_begin_fstest auto quick dax
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_check_dmesg
+_require_scratch_reflink
+_require_cp_reflink
+_require_xfs_scratch_rmapbt
+_require_scratch_dax_mountopt "dax"
+_require_test_program "t_mmap_cow_memory_failure"
+
+echo "Format and mount"
+_scratch_mkfs > $seqres.full 2>&1
+_scratch_mount "-o dax" >> $seqres.full 2>&1
+
+testdir=$SCRATCH_MNT/test-$seq
+mkdir $testdir
+
+echo "Create the original files"
+filesize=65536
+_pwrite_byte 0x61 0 $filesize $testdir/testfile >> $seqres.full
+_scratch_cycle_mount "dax"
+
+echo "Inject memory failure (1 page)"
+# create two processes:
+#  process1: mread 1 page to cause page fault, and wait
+#  process2: mread 1 page to cause page fault, then inject poison on this page
+$here/src/t_mmap_cow_memory_failure -s1 -S1 -R $testdir/testfile -P $testdir/testfile
+
+echo "Inject memory failure (2 pages)"
+$here/src/t_mmap_cow_memory_failure -s2 -S2 -R $testdir/testfile -P $testdir/testfile
+
+_check_dmesg_for "Sending SIGBUS to t_mmap_cow_memo" || echo "Memory failure didn't kill the process"
+_check_dmesg_for "recovery action for dax page: Recovered" || echo "Failured page didn't recovered"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/550.out b/tests/xfs/550.out
new file mode 100644 (file)
index 0000000..80e3223
--- /dev/null
@@ -0,0 +1,9 @@
+QA output created by 550
+Format and mount
+Create the original files
+Inject memory failure (1 page)
+Inject poison...
+Process is killed by signal: 7
+Inject memory failure (2 pages)
+Inject poison...
+Process is killed by signal: 7
diff --git a/tests/xfs/551 b/tests/xfs/551
new file mode 100755 (executable)
index 0000000..f4af72e
--- /dev/null
@@ -0,0 +1,51 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Fujitsu Limited.  All Rights Reserved.
+#
+# FS QA Test No. 551
+#
+# Test memory failure mechanism when dax and reflink working together
+#
+. ./common/preamble
+_begin_fstest auto quick clone dax
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_check_dmesg
+_require_scratch_reflink
+_require_cp_reflink
+_require_xfs_scratch_rmapbt
+_require_scratch_dax_mountopt "dax"
+_require_test_program "t_mmap_cow_memory_failure"
+
+echo "Format and mount"
+_scratch_mkfs > $seqres.full 2>&1
+_scratch_mount "-o dax" >> $seqres.full 2>&1
+
+testdir=$SCRATCH_MNT/test-$seq
+mkdir $testdir
+
+echo "Create the original files"
+filesize=65536
+_pwrite_byte 0x61 0 $filesize $testdir/testfile >> $seqres.full
+_cp_reflink $testdir/testfile $testdir/poisonfile >> $seqres.full
+_scratch_cycle_mount "dax"
+
+echo "Inject memory failure (1 page)"
+# create two processes:
+#  process1: mread 1 page to cause page fault, and wait
+#  process2: mread 1 page to cause page fault, then inject poison on this page
+$here/src/t_mmap_cow_memory_failure -s1 -S1 -R $testdir/testfile -P $testdir/poisonfile
+
+echo "Inject memory failure (2 pages)"
+$here/src/t_mmap_cow_memory_failure -s2 -S2 -R $testdir/testfile -P $testdir/poisonfile
+
+_check_dmesg_for "Sending SIGBUS to t_mmap_cow_memo" || echo "Memory failure didn't kill the process"
+_check_dmesg_for "recovery action for dax page: Recovered" || echo "Failured page didn't recovered"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/551.out b/tests/xfs/551.out
new file mode 100644 (file)
index 0000000..6dc8533
--- /dev/null
@@ -0,0 +1,9 @@
+QA output created by 551
+Format and mount
+Create the original files
+Inject memory failure (1 page)
+Inject poison...
+Process is killed by signal: 7
+Inject memory failure (2 pages)
+Inject poison...
+Process is killed by signal: 7
diff --git a/tests/xfs/552 b/tests/xfs/552
new file mode 100755 (executable)
index 0000000..cb97b2f
--- /dev/null
@@ -0,0 +1,54 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Fujitsu Limited.  All Rights Reserved.
+#
+# FS QA Test No. 552
+#
+# Test memory failure mechanism when dax and reflink working together
+#   test for partly reflinked file
+#
+. ./common/preamble
+_begin_fstest auto quick clone dax
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+
+# real QA test starts here
+_require_check_dmesg
+_require_scratch_reflink
+_require_xfs_scratch_rmapbt
+_require_scratch_dax_mountopt "dax"
+_require_test_program "t_mmap_cow_memory_failure"
+
+echo "Format and mount"
+_scratch_mkfs > $seqres.full 2>&1
+_scratch_mount "-o dax" >> $seqres.full 2>&1
+
+testdir=$SCRATCH_MNT/test-$seq
+mkdir $testdir
+
+echo "Create the original files"
+nr=16
+blksz=$(_get_page_size)
+_pwrite_byte 0x61 0 $((blksz * nr)) $testdir/testfile >> $seqres.full
+_pwrite_byte 0x62 0 $((blksz * nr)) $testdir/poisonfile >> $seqres.full
+seq 0 2 $((nr - 1)) | while read i; do
+       _reflink_range $testdir/testfile $((blksz * i)) \
+               $testdir/poisonfile $((blksz * i)) $blksz >> $seqres.full
+done
+_scratch_cycle_mount "dax"
+
+echo "Inject memory failure (1 page)"
+$here/src/t_mmap_cow_memory_failure -s1 -S1 -R $testdir/testfile -P $testdir/poisonfile
+
+echo "Inject memory failure (2 pages)"
+# poison on reflinked page and not reflinked page
+$here/src/t_mmap_cow_memory_failure -s2 -S2 -R $testdir/testfile -P $testdir/poisonfile
+
+_check_dmesg_for "Sending SIGBUS to t_mmap_cow_memo" || echo "Memory failure didn't kill the process"
+_check_dmesg_for "recovery action for dax page: Recovered" || echo "Failured page didn't recovered"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/552.out b/tests/xfs/552.out
new file mode 100644 (file)
index 0000000..fac3680
--- /dev/null
@@ -0,0 +1,9 @@
+QA output created by 552
+Format and mount
+Create the original files
+Inject memory failure (1 page)
+Inject poison...
+Process is killed by signal: 7
+Inject memory failure (2 pages)
+Inject poison...
+Process is killed by signal: 7
diff --git a/tests/xfs/553 b/tests/xfs/553
new file mode 100755 (executable)
index 0000000..e98c04e
--- /dev/null
@@ -0,0 +1,67 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test 553
+#
+# Test to check if a direct write on a delalloc extent present in CoW fork can
+# result in an ENOSPC error.
+#
+. ./common/preamble
+_begin_fstest auto quick clone
+
+# Import common functions.
+. ./common/reflink
+. ./common/inject
+
+# real QA test starts here
+_supported_fs xfs
+_fixed_by_kernel_commit d62113303d691 \
+       "xfs: Fix false ENOSPC when performing direct write on a delalloc extent in cow fork"
+_require_scratch_reflink
+_require_xfs_debug
+_require_test_program "punch-alternating"
+_require_xfs_io_error_injection "bmap_alloc_minlen_extent"
+_require_xfs_io_command "reflink"
+_require_xfs_io_command "cowextsize"
+
+source=${SCRATCH_MNT}/source
+destination=${SCRATCH_MNT}/destination
+fragmented_file=${SCRATCH_MNT}/fragmented_file
+
+echo "Format and mount fs"
+_scratch_mkfs >> $seqres.full
+_scratch_mount >> $seqres.full
+
+blksz=$(_get_file_block_size $SCRATCH_MNT)
+
+echo "Create source file"
+$XFS_IO_PROG -f -c "pwrite 0 $((blksz * 8192))" $source >> $seqres.full
+
+echo "Reflink destination file with source file"
+$XFS_IO_PROG -f -c "reflink $source" $destination >> $seqres.full
+
+echo "Set destination file's cowextsize to 4096 blocks"
+$XFS_IO_PROG -c "cowextsize $((blksz * 4096))" $destination >> $seqres.full
+
+echo "Fragment FS"
+$XFS_IO_PROG -f -c "pwrite 0 $((blksz * 16384))" $fragmented_file \
+            >> $seqres.full
+sync
+$here/src/punch-alternating $fragmented_file >> $seqres.full
+
+echo "Inject bmap_alloc_minlen_extent error tag"
+_scratch_inject_error bmap_alloc_minlen_extent 1
+
+echo "Create delalloc extent of length 4096 blocks in destination file's CoW fork"
+$XFS_IO_PROG -c "pwrite 0 $blksz" $destination >> $seqres.full
+
+sync
+
+echo "Direct I/O write at 3rd block in destination file"
+$XFS_IO_PROG -d -c "pwrite $((blksz * 3)) $((blksz * 2))" $destination \
+            >> $seqres.full
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/553.out b/tests/xfs/553.out
new file mode 100644 (file)
index 0000000..9f5679d
--- /dev/null
@@ -0,0 +1,9 @@
+QA output created by 553
+Format and mount fs
+Create source file
+Reflink destination file with source file
+Set destination file's cowextsize to 4096 blocks
+Fragment FS
+Inject bmap_alloc_minlen_extent error tag
+Create delalloc extent of length 4096 blocks in destination file's CoW fork
+Direct I/O write at 3rd block in destination file
diff --git a/tests/xfs/554 b/tests/xfs/554
new file mode 100755 (executable)
index 0000000..16fc052
--- /dev/null
@@ -0,0 +1,52 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 554
+#
+# Create a filesystem which contains an inode with a lower number
+# than the root inode. Set the lower number to a dump file as the root inode
+# and ensure that 'xfsrestore -x' handles this wrong inode.
+#
+. ./common/preamble
+_begin_fstest auto quick dump prealloc
+
+# Import common functions.
+. ./common/dump
+
+_supported_fs xfs
+_fixed_by_git_commit xfsdump \
+       "XXXXXXXXXXXX xfsrestore: fix rootdir due to xfsdump bulkstat misuse"
+_require_xfs_io_command "falloc"
+_require_scratch
+_require_xfsrestore_xflag
+
+# Create a filesystem which contains a fake root inode
+inums=($(_scratch_xfs_create_fake_root))
+root_inum=${inums[0]}
+fake_inum=${inums[1]}
+
+# Now try a dump and restore. Cribbed from xfs/068
+_create_dumpdir_stress
+
+echo -n "Before: " >> $seqres.full
+_count_dumpdir_files | tee $tmp.before >> $seqres.full
+
+_do_dump_file
+
+# Set the wrong root inode number to the dump file
+# as problematic xfsdump used to do.
+$here/src/fake-dump-rootino $dump_file $fake_inum
+
+_do_restore_file -x | \
+sed -e "s/rootino #${fake_inum}/rootino #FAKENO/g" \
+       -e "s/# to ${root_inum}/# to ROOTNO/g" \
+       -e "/entries processed$/s/[0-9][0-9]*/NUM/g"
+
+echo -n "After: " >> $seqres.full
+_count_restoredir_files | tee $tmp.after >> $seqres.full
+diff -u $tmp.before $tmp.after
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/554.out b/tests/xfs/554.out
new file mode 100644 (file)
index 0000000..c5e8c4c
--- /dev/null
@@ -0,0 +1,40 @@
+QA output created by 554
+Creating directory system to dump using fsstress.
+
+-----------------------------------------------
+fsstress : -f link=10 -f creat=10 -f mkdir=10 -f truncate=5 -f symlink=10
+-----------------------------------------------
+Dumping to file...
+xfsdump  -f DUMP_FILE -M stress_tape_media -L stress_554 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 0 dump of HOSTNAME:SCRATCH_MNT
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_554"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: /var/xfsdump/inventory created
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Restoring from file...
+xfsrestore  -x -f DUMP_FILE  -L stress_554 RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: examining media file 0
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: NUM directories and NUM entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
diff --git a/tests/xfs/555 b/tests/xfs/555
new file mode 100755 (executable)
index 0000000..a402450
--- /dev/null
@@ -0,0 +1,32 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Red Hat, Inc.  All Rights Reserved.
+#
+# FS QA Test 555
+#
+# Corrupt xfs sb_inopblock, make sure no crash. This's a test coverage of
+# 392c6de98af1 ("xfs: sanitize sb_inopblock in xfs_mount_validate_sb")
+#
+. ./common/preamble
+_begin_fstest auto quick
+
+# real QA test starts here
+_supported_fs xfs
+_fixed_by_kernel_commit 392c6de98af1 \
+       "xfs: sanitize sb_inopblock in xfs_mount_validate_sb"
+_require_scratch
+
+_scratch_mkfs >>$seqres.full
+echo "corrupt inopblock of sb 0"
+_scratch_xfs_set_metadata_field "inopblock" "500" "sb 0" >> $seqres.full
+echo "try to mount ..."
+_try_scratch_mount 2>> $seqres.full && _fail "mount should not succeed!"
+echo "no crash or hang"
+echo "repair corrupted sb 0"
+_scratch_xfs_repair >> $seqres.full 2>&1
+echo "check fs"
+_scratch_xfs_repair -n >> $seqres.full 2>&1 || echo "fs isn't fixed!"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/555.out b/tests/xfs/555.out
new file mode 100644 (file)
index 0000000..4f1b01a
--- /dev/null
@@ -0,0 +1,6 @@
+QA output created by 555
+corrupt inopblock of sb 0
+try to mount ...
+no crash or hang
+repair corrupted sb 0
+check fs
diff --git a/tests/xfs/556 b/tests/xfs/556
new file mode 100755 (executable)
index 0000000..2f8cad1
--- /dev/null
@@ -0,0 +1,155 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 556
+#
+# Check xfs_scrub's media scan can actually return diagnostic information for
+# media errors in file data extents.
+
+. ./common/preamble
+_begin_fstest auto quick scrub eio
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -f $tmp.*
+       _dmerror_cleanup
+}
+
+# Import common functions.
+. ./common/fuzzy
+. ./common/filter
+. ./common/dmerror
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_scratch_xfs_crc
+_require_scrub
+_require_dm_target error
+
+filter_scrub_errors() {
+       _filter_scratch | sed \
+               -e "s/offset $((fs_blksz * 2)) /offset 2FSB /g" \
+               -e "s/length $fs_blksz.*/length 1FSB./g"
+}
+
+_scratch_mkfs >> $seqres.full
+_dmerror_init
+_dmerror_mount >> $seqres.full 2>&1
+
+_require_scratch_xfs_scrub
+
+# Write a file with 4 file blocks worth of data
+victim=$SCRATCH_MNT/a
+file_blksz=$(_get_file_block_size $SCRATCH_MNT)
+$XFS_IO_PROG -f -c "pwrite -S 0x58 0 $((4 * file_blksz))" -c "fsync" $victim >> $seqres.full
+unset errordev
+_xfs_is_realtime_file $victim && errordev="RT"
+bmap_str="$($XFS_IO_PROG -c "bmap -elpv" $victim | grep "^[[:space:]]*0:")"
+echo "$errordev:$bmap_str" >> $seqres.full
+
+phys="$(echo "$bmap_str" | $AWK_PROG '{print $3}')"
+if [ "$errordev" = "RT" ]; then
+       len="$(echo "$bmap_str" | $AWK_PROG '{print $4}')"
+else
+       len="$(echo "$bmap_str" | $AWK_PROG '{print $6}')"
+fi
+fs_blksz=$(_get_block_size $SCRATCH_MNT)
+echo "file_blksz:$file_blksz:fs_blksz:$fs_blksz" >> $seqres.full
+kernel_sectors_per_fs_block=$((fs_blksz / 512))
+
+# Did we get at least 4 fs blocks worth of extent?
+min_len_sectors=$(( 4 * kernel_sectors_per_fs_block ))
+test "$len" -lt $min_len_sectors && \
+       _fail "could not format a long enough extent on an empty fs??"
+
+phys_start=$(echo "$phys" | sed -e 's/\.\..*//g')
+
+echo "$errordev:$phys:$len:$fs_blksz:$phys_start" >> $seqres.full
+echo "victim file:" >> $seqres.full
+od -tx1 -Ad -c $victim >> $seqres.full
+
+# Set the dmerror table so that all IO will pass through.
+_dmerror_reset_table
+
+cat >> $seqres.full << ENDL
+dmerror before:
+$DMERROR_TABLE
+$DMERROR_RTTABLE
+<end table>
+ENDL
+
+# All sector numbers that we feed to the kernel must be in units of 512b, but
+# they also must be aligned to the device's logical block size.
+logical_block_size=$(_min_dio_alignment $SCRATCH_DEV)
+kernel_sectors_per_device_lba=$((logical_block_size / 512))
+
+# Mark as bad one of the device LBAs in the middle of the extent.  Target the
+# second LBA of the third block of the four-block file extent that we allocated
+# earlier, but without overflowing into the fourth file block.
+bad_sector=$(( phys_start + (2 * kernel_sectors_per_fs_block) ))
+bad_len=$kernel_sectors_per_device_lba
+if (( kernel_sectors_per_device_lba < kernel_sectors_per_fs_block )); then
+       bad_sector=$((bad_sector + kernel_sectors_per_device_lba))
+fi
+if (( (bad_sector % kernel_sectors_per_device_lba) != 0)); then
+       echo "bad_sector $bad_sector not congruent with device logical block size $logical_block_size"
+fi
+_dmerror_mark_range_bad $bad_sector $bad_len $errordev
+
+cat >> $seqres.full << ENDL
+dmerror after marking bad:
+$DMERROR_TABLE
+$DMERROR_RTTABLE
+<end table>
+ENDL
+
+_dmerror_load_error_table
+
+# See if the media scan picks it up.
+echo "Scrub for injected media error (single threaded)"
+
+# Once in single-threaded mode
+_scratch_scrub -b -x >> $seqres.full 2> $tmp.error
+cat $tmp.error | filter_scrub_errors
+
+# Once in parallel mode
+echo "Scrub for injected media error (multi threaded)"
+_scratch_scrub -x >> $seqres.full 2> $tmp.error
+cat $tmp.error | filter_scrub_errors
+
+# Remount to flush the page cache and reread to see the IO error
+_dmerror_unmount
+_dmerror_mount
+echo "victim file:" >> $seqres.full
+od -tx1 -Ad -c $victim >> $seqres.full 2> $tmp.error
+cat $tmp.error | sed -e 's/read error: //g' | _filter_scratch
+
+# Scrub again to re-confirm the media error across a remount
+echo "Scrub for injected media error (after remount)"
+_scratch_scrub -x >> $seqres.full 2> $tmp.error
+cat $tmp.error | filter_scrub_errors
+
+# Now mark the bad range good so that a retest shows no media failure.
+_dmerror_mark_range_good $bad_sector $bad_len $errordev
+_dmerror_load_error_table
+
+cat >> $seqres.full << ENDL
+dmerror after marking good:
+$DMERROR_TABLE
+$DMERROR_RTTABLE
+<end table>
+ENDL
+
+echo "Scrub after removing injected media error"
+
+# Scrub one last time to make sure the error's gone.
+_scratch_scrub -x >> $seqres.full 2> $tmp.error
+cat $tmp.error | filter_scrub_errors
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/556.out b/tests/xfs/556.out
new file mode 100644 (file)
index 0000000..8b9e956
--- /dev/null
@@ -0,0 +1,12 @@
+QA output created by 556
+Scrub for injected media error (single threaded)
+Unfixable Error: SCRATCH_MNT/a: media error at data offset 2FSB length 1FSB.
+SCRATCH_MNT: unfixable errors found: 1
+Scrub for injected media error (multi threaded)
+Unfixable Error: SCRATCH_MNT/a: media error at data offset 2FSB length 1FSB.
+SCRATCH_MNT: unfixable errors found: 1
+od: SCRATCH_MNT/a: Input/output error
+Scrub for injected media error (after remount)
+Unfixable Error: SCRATCH_MNT/a: media error at data offset 2FSB length 1FSB.
+SCRATCH_MNT: unfixable errors found: 1
+Scrub after removing injected media error
diff --git a/tests/xfs/557 b/tests/xfs/557
new file mode 100755 (executable)
index 0000000..0120537
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 557
+#
+# This is a test for:
+#   bf3cb3944792 (xfs: allow single bulkstat of special inodes)
+# Create a filesystem which contains an inode with a lower number
+# than the root inode. Then verify that XFS_BULK_IREQ_SPECIAL_ROOT gets
+# the correct root inode number.
+#
+. ./common/preamble
+_begin_fstest auto quick prealloc
+
+_supported_fs xfs
+_require_xfs_io_command "falloc"
+_require_xfs_io_command "bulkstat_single"
+_require_scratch
+
+_fixed_by_kernel_commit 817644fa4525 \
+       "xfs: get root inode correctly at bulkstat"
+
+# Create a filesystem which contains a fake root inode
+inums=($(_scratch_xfs_create_fake_root))
+root_inum=${inums[0]}
+fake_inum=${inums[1]}
+
+# Get root ino with XFS_BULK_IREQ_SPECIAL_ROOT
+bulkstat_root_inum=$($XFS_IO_PROG -c 'bulkstat_single root' $SCRATCH_MNT | grep bs_ino | awk '{print $3;}')
+echo "bulkstat_root_inum: $bulkstat_root_inum" >> $seqres.full
+if [ $root_inum -ne $bulkstat_root_inum ]; then
+       echo "root ino mismatch: expected:${root_inum}, actual:${bulkstat_root_inum}"
+fi
+
+echo "Silence is golden"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/557.out b/tests/xfs/557.out
new file mode 100644 (file)
index 0000000..1f1ae1d
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 557
+Silence is golden
diff --git a/tests/xfs/558 b/tests/xfs/558
new file mode 100755 (executable)
index 0000000..270f458
--- /dev/null
@@ -0,0 +1,221 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 558
+#
+# This is a regression test for a data corruption bug that existed in XFS' copy
+# on write code between 4.9 and 4.19.  The root cause is a concurrency bug
+# wherein we would drop ILOCK_SHARED after querying the CoW fork in xfs_map_cow
+# and retake it before querying the data fork in xfs_map_blocks.  If a second
+# thread changes the CoW fork mappings between the two calls, it's possible for
+# xfs_map_blocks to return a zero-block mapping, which results in writeback
+# being elided for that block.  Elided writeback of dirty data results in
+# silent loss of writes.
+#
+# Worse yet, kernels from that era still used buffer heads, which means that an
+# elided writeback leaves the page clean but the bufferheads dirty.  Due to a
+# naïve optimization in mark_buffer_dirty, the SetPageDirty call is elided if
+# the bufferhead is dirty, which means that a subsequent rewrite of the data
+# block will never result in the page being marked dirty, and all subsequent
+# writes are lost.
+#
+# It turns out that Christoph Hellwig unwittingly fixed the race in commit
+# 5c665e5b5af6 ("xfs: remove xfs_map_cow"), and no testcase was ever written.
+# Four years later, we hit it on a production 4.14 kernel.  This testcase
+# relies on a debugging knob that introduces artificial delays into writeback.
+#
+# Before the race, the file blocks 0-1 are not shared and blocks 2-5 are
+# shared.  There are no extents in CoW fork.
+#
+# Two threads race like this:
+#
+# Thread 1 (writeback block 0)     | Thread 2  (write to block 2)
+# ---------------------------------|--------------------------------
+#                                  |
+# 1. Check if block 0 in CoW fork  |
+#    from xfs_map_cow.             |
+#                                  |
+# 2. Block 0 not found in CoW      |
+#    fork; the block is considered |
+#    not shared.                   |
+#                                  |
+# 3. xfs_map_blocks looks up data  |
+#    fork to get a map covering    |
+#    block 0.                      |
+#                                  |
+# 4. It gets a data fork mapping   |
+#    for block 0 with length 2.    |
+#                                  |
+#                                  | 1. A buffered write to block 2 sees
+#                                  |    that it is a shared block and no
+#                                  |    extent covers block 2 in CoW fork.
+#                                  |
+#                                  |    It creates a new CoW fork mapping.
+#                                  |    Due to the cowextsize, the new
+#                                  |    extent starts at block 0 with
+#                                  |    length 128.
+#                                  |
+#                                  |
+# 5. It lookup CoW fork again to   |
+#    trim the map (0, 2) to a      |
+#    shared block boundary.        |
+#                                  |
+# 5a. It finds (0, 128) in CoW fork|
+# 5b. It trims the data fork map   |
+#     from (0, 1) to (0, 0) (!!!)  |
+#                                  |
+# 6. The xfs_imap_valid call after |
+#    the xfs_map_blocks call checks|
+#    if the mapping (0, 0) covers  |
+#    block 0.  The result is "NO". |
+#                                  |
+# 7. Since block 0 has no physical |
+#    block mapped, it's not added  |
+#    to the ioend.  This is the    |
+#    first problem.                |
+#                                  |
+# 8. xfs_add_to_ioend usually      |
+#    clears the bufferhead dirty   |
+#    flag  Because this is skipped,|
+#    we leave the page clean with  |
+#    the associated buffer head(s) |
+#    dirty (the second problem).   |
+#    Now the dirty state is        |
+#    inconsistent.
+#
+# On newer kernels, this is also a functionality test for the ifork sequence
+# counter because the writeback completions will change the data fork and force
+# revalidations of the wb mapping.
+#
+. ./common/preamble
+_begin_fstest auto quick clone
+
+# Import common functions.
+. ./common/reflink
+. ./common/inject
+. ./common/tracing
+
+# real QA test starts here
+_cleanup()
+{
+       test -n "$sentryfile" && rm -f $sentryfile
+       wait
+       _ftrace_cleanup
+       cd /
+       rm -r -f $tmp.* $sentryfile $tracefile
+}
+
+# Modify as appropriate.
+_supported_fs xfs
+_fixed_by_kernel_commit 5c665e5b5af6 "xfs: remove xfs_map_cow"
+_require_ftrace
+_require_xfs_io_error_injection "wb_delay_ms"
+_require_scratch_reflink
+_require_cp_reflink
+
+# This test races writeback of a pure overwrite of a data fork extent against
+# the creation of a speculative COW preallocation.  In alwayscow mode, there
+# are no pure overwrites, which means that a precondition of the test is not
+# satisfied, and this test should be skipped.
+_require_no_xfs_always_cow
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount >> $seqres.full
+
+# This is a pagecache test, so try to disable fsdax mode.
+$XFS_IO_PROG -c 'chattr -x' $SCRATCH_MNT &> $seqres.full
+_require_pagecache_access $SCRATCH_MNT
+
+min_blksz=65536
+file_blksz=$(_get_file_block_size "$SCRATCH_MNT")
+blksz=$(( 8 * $file_blksz ))
+
+blksz=$(( blksz > min_blksz ? blksz : min_blksz ))
+
+_require_congruent_file_oplen $SCRATCH_MNT $blksz
+
+# Make sure we have sufficient extent size to create speculative CoW
+# preallocations.
+$XFS_IO_PROG -c 'cowextsize 1m' $SCRATCH_MNT
+
+# Write out a file with the first two blocks unshared and the rest shared.
+_pwrite_byte 0x59 0 $((160 * blksz)) $SCRATCH_MNT/file >> $seqres.full
+_pwrite_byte 0x59 0 $((160 * blksz)) $SCRATCH_MNT/file.compare >> $seqres.full
+sync
+
+_cp_reflink $SCRATCH_MNT/file $SCRATCH_MNT/file.reflink
+
+_pwrite_byte 0x58 0 $((2 * blksz)) $SCRATCH_MNT/file >> $seqres.full
+_pwrite_byte 0x58 0 $((2 * blksz)) $SCRATCH_MNT/file.compare >> $seqres.full
+sync
+
+# Avoid creation of large folios on newer kernels by cycling the mount and
+# immediately writing to the page cache.
+_scratch_cycle_mount
+
+# Write the same data to file.compare as we're about to do to file.  Do this
+# before slowing down writeback to avoid unnecessary delay.
+_pwrite_byte 0x57 0 $((2 * blksz)) $SCRATCH_MNT/file.compare >> $seqres.full
+_pwrite_byte 0x56 $((2 * blksz)) $((2 * blksz)) $SCRATCH_MNT/file.compare >> $seqres.full
+sync
+
+# Introduce a half-second wait to each writeback block mapping call.  This
+# gives us a chance to race speculative cow prealloc with writeback.
+_scratch_inject_error "wb_delay_ms" 500
+
+_ftrace_setup
+_ftrace_record_events 'xfs_wb*iomap_invalid'
+
+# Start thread 1 + writeback above
+$XFS_IO_PROG -c "pwrite -S 0x57 0 $((2 * blksz))" \
+       -c 'fsync' $SCRATCH_MNT/file >> $seqres.full &
+sleep 1
+
+# Start a sentry to look for evidence of invalidation tracepoint tripping.  If
+# we see that, we know we've forced writeback to revalidate a mapping.  The
+# test has been successful, so turn off the delay.
+sentryfile=$TEST_DIR/$seq.sentry
+tracefile=$TEST_DIR/$seq.ftrace
+wait_for_errortag() {
+       while [ -e "$sentryfile" ]; do
+               _ftrace_dump | grep iomap_invalid >> "$tracefile"
+               if grep -q iomap_invalid "$tracefile"; then
+                       _scratch_inject_error "wb_delay_ms" 0
+                       _ftrace_ignore_events
+                       break;
+               fi
+               sleep 0.5
+       done
+}
+touch $sentryfile
+wait_for_errortag &
+
+# Start thread 2 to create the cowextsize reservation
+$XFS_IO_PROG -c "pwrite -S 0x56 $((2 * blksz)) $((2 * blksz))" \
+       -c 'fsync' $SCRATCH_MNT/file >> $seqres.full
+rm -f $sentryfile
+
+cat "$tracefile" >> $seqres.full
+grep -q iomap_invalid "$tracefile"
+saw_invalidation=$?
+
+# Flush everything to disk.  If the bug manifests, then after the cycle,
+# file should have stale 0x58 in block 0 because we silently dropped a write.
+_scratch_cycle_mount
+
+if ! cmp -s $SCRATCH_MNT/file $SCRATCH_MNT/file.compare; then
+       echo file and file.compare do not match
+       $XFS_IO_PROG -c 'bmap -celpv' -c 'bmap -elpv' $SCRATCH_MNT/file &>> $seqres.full
+       echo file.compare
+       od -tx1 -Ad -c $SCRATCH_MNT/file.compare
+       echo file
+       od -tx1 -Ad -c $SCRATCH_MNT/file
+elif [ $saw_invalidation -ne 0 ]; then
+       # The files matched, but nothing got logged about the revalidation?
+       echo "Expected to hear about writeback iomap invalidations?"
+fi
+
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/558.out b/tests/xfs/558.out
new file mode 100644 (file)
index 0000000..9a6c4e7
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 558
+Silence is golden
diff --git a/tests/xfs/559 b/tests/xfs/559
new file mode 100755 (executable)
index 0000000..64fc16e
--- /dev/null
@@ -0,0 +1,153 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 559
+#
+# This is a regression test for a data corruption bug that existed in iomap's
+# buffered write routines.
+#
+. ./common/preamble
+_begin_fstest auto quick rw
+
+# Import common functions.
+. ./common/inject
+. ./common/tracing
+
+# real QA test starts here
+_cleanup()
+{
+       test -n "$sentryfile" && rm -f $sentryfile
+       wait
+       _ftrace_cleanup
+       cd /
+       rm -r -f $tmp.* $sentryfile $tracefile
+}
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_ftrace
+_require_xfs_io_command "falloc"
+_require_xfs_io_error_injection "write_delay_ms"
+_require_scratch
+
+_fixed_by_kernel_commit 304a68b9c63b \
+       "xfs: use iomap_valid method to detect stale cached iomaps"
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount >> $seqres.full
+
+# This is a pagecache test, so try to disable fsdax mode.
+$XFS_IO_PROG -c 'chattr -x' $SCRATCH_MNT &> $seqres.full
+_require_pagecache_access $SCRATCH_MNT
+
+blocks=10
+
+# If this kernel advertises huge page support, it's possible that it could be
+# using large folios for the page cache writes.  It is necessary to write
+# multiple folios (large or regular) to triggering the write invalidation,
+# so we'll scale the test write size accordingly.
+blksz=$(_get_hugepagesize)
+base_pagesize=$(_get_page_size)
+test -z "$blksz" && blksz=${base_pagesize}
+filesz=$((blocks * blksz))
+dirty_offset=$(( filesz - 1 ))
+write_len=$(( ( (blocks - 1) * blksz) + 1 ))
+
+# The write invalidation that we're testing below can only occur as part of
+# a single large write.  The kernel limits writes to one base page less than
+# 2GiB to prevent lengthy IOs and integer overflows.  If the block size is so
+# huge (e.g. 512M huge pages on arm64) that we'd exceed that, reduce the number
+# of blocks to get us under the limit.
+max_writesize=$((2147483647 - base_pagesize))
+if ((write_len > max_writesize)); then
+       blocks=$(( ( (max_writesize - 1) / blksz) + 1))
+       # We need at least three blocks in the file to test invalidation
+       # between writes to multiple folios.  If we drop below that,
+       # reconfigure ourselves with base pages and hope for the best.
+       if ((blocks < 3)); then
+               blksz=$base_pagesize
+               blocks=10
+       fi
+       filesz=$((blocks * blksz))
+       dirty_offset=$(( filesz - 1 ))
+       write_len=$(( ( (blocks - 1) * blksz) + 1 ))
+fi
+
+# Create a large file with a large unwritten range.
+$XFS_IO_PROG -f -c "falloc 0 $filesz" $SCRATCH_MNT/file >> $seqres.full
+
+# Write the same data to file.compare as we're about to do to file.  Do this
+# before slowing down writeback to avoid unnecessary delay.
+_pwrite_byte 0x58 $dirty_offset 1 $SCRATCH_MNT/file.compare >> $seqres.full
+_pwrite_byte 0x57 0 $write_len $SCRATCH_MNT/file.compare >> $seqres.full
+
+# Reinitialize the page cache for this file.
+_scratch_cycle_mount
+
+# Dirty the last page in the range and immediately set the write delay so that
+# any subsequent writes have to wait.
+$XFS_IO_PROG -c "pwrite -S 0x58 $dirty_offset 1" $SCRATCH_MNT/file >> $seqres.full
+_scratch_inject_error "write_delay_ms" 500
+
+_ftrace_setup
+_ftrace_record_events 'xfs_iomap_invalid'
+
+# Start a sentry to look for evidence of invalidation tracepoint tripping.  If
+# we see that, we know we've forced writeback to revalidate a mapping.  The
+# test has been successful, so turn off the delay.
+sentryfile=$TEST_DIR/$seq.sentry
+tracefile=$TEST_DIR/$seq.ftrace
+wait_for_errortag() {
+       while [ -e "$sentryfile" ]; do
+               _ftrace_dump | grep iomap_invalid >> "$tracefile"
+               if grep -q iomap_invalid "$tracefile"; then
+                       _scratch_inject_error "write_delay_ms" 0
+                       _ftrace_ignore_events
+                       break;
+               fi
+               sleep 0.5
+       done
+}
+touch $sentryfile
+wait_for_errortag &
+
+# Start thread 1 + writeback above
+($XFS_IO_PROG -c "pwrite -S 0x57 -b $write_len 0 $write_len" \
+       $SCRATCH_MNT/file >> $seqres.full; rm -f $sentryfile) &
+sleep 1
+
+# Start thread 2 to simulate reclaim writeback via sync_file_range and fadvise
+# to drop the page cache.
+#      -c "fadvise -d $dirty_offset 1" \
+dirty_pageoff=$((filesz - blksz))
+$XFS_IO_PROG -c "sync_range -a -w $dirty_pageoff $blksz" \
+       -c "mmap -r 0 $filesz" \
+       -c "madvise -d 0 $filesz" \
+       $SCRATCH_MNT/file >> $seqres.full
+wait
+rm -f $sentryfile
+
+cat "$tracefile" >> $seqres.full
+grep -q iomap_invalid "$tracefile"
+saw_invalidation=$?
+
+# Flush everything to disk.  If the bug manifests, then after the cycle,
+# file should have stale 0x58 in block 0 because we silently dropped a write.
+_scratch_cycle_mount
+
+if ! cmp -s $SCRATCH_MNT/file $SCRATCH_MNT/file.compare; then
+       echo file and file.compare do not match
+       $XFS_IO_PROG -c 'bmap -celpv' -c 'bmap -elpv' $SCRATCH_MNT/file &>> $seqres.full
+       echo file.compare
+       od -tx1 -Ad -c $SCRATCH_MNT/file.compare
+       echo file
+       od -tx1 -Ad -c $SCRATCH_MNT/file
+elif [ $saw_invalidation -ne 0 ]; then
+       # The files matched, but nothing got logged about the revalidation?
+       echo "Expected to hear about write iomap invalidation?"
+fi
+
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/559.out b/tests/xfs/559.out
new file mode 100644 (file)
index 0000000..4985bb7
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 559
+Silence is golden
diff --git a/tests/xfs/560 b/tests/xfs/560
new file mode 100755 (executable)
index 0000000..28b45d5
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 560
+#
+# Race GETFSMAP and ro remount for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest auto quick fsmap remount
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       _scratch_xfs_stress_scrub_cleanup
+       rm -rf $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_scratch_rmapbt
+_require_xfs_io_command "fsmap"
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -r 5 -i 'fsmap -v'
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/xfs/560.out b/tests/xfs/560.out
new file mode 100644 (file)
index 0000000..841fd90
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 560
+Silence is golden
diff --git a/tests/xfs/561 b/tests/xfs/561
new file mode 100755 (executable)
index 0000000..c1d68c6
--- /dev/null
@@ -0,0 +1,39 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 561
+#
+# Race xfs_scrub in check-only mode and ro remount for a while to see if we
+# crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       _scratch_remount rw
+       rm -rf $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -r 5 -S '-n'
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/xfs/561.out b/tests/xfs/561.out
new file mode 100644 (file)
index 0000000..5e46f61
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 561
+Silence is golden
diff --git a/tests/xfs/562 b/tests/xfs/562
new file mode 100755 (executable)
index 0000000..a5c6e88
--- /dev/null
@@ -0,0 +1,39 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 562
+#
+# Race xfs_scrub in check-only mode and freeze for a while to see if we crash
+# or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       _scratch_remount rw
+       rm -rf $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -f -S '-n'
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/xfs/562.out b/tests/xfs/562.out
new file mode 100644 (file)
index 0000000..6203d9a
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 562
+Silence is golden
diff --git a/tests/xfs/563 b/tests/xfs/563
new file mode 100755 (executable)
index 0000000..409a42b
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 563
+#
+# Race xfs_scrub in force-repair mdoe and freeze for a while to see if we crash
+# or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       _scratch_remount rw
+       rm -rf $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/xfs
+. ./common/inject
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -f -S '-k'
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/xfs/563.out b/tests/xfs/563.out
new file mode 100644 (file)
index 0000000..76aea88
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 563
+Silence is golden
diff --git a/tests/xfs/564 b/tests/xfs/564
new file mode 100755 (executable)
index 0000000..11b3fec
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 564
+#
+# Race xfs_scrub in force-repair mode and ro remount for a while to see if we
+# crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       _scratch_remount rw
+       rm -rf $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/xfs
+. ./common/inject
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -r 5 -S '-k'
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/xfs/564.out b/tests/xfs/564.out
new file mode 100644 (file)
index 0000000..4012aca
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 564
+Silence is golden
diff --git a/tests/xfs/565 b/tests/xfs/565
new file mode 100755 (executable)
index 0000000..826bc53
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 565
+#
+# Race fsx and xfs_scrub in read-only mode for a while to see if we crash
+# or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       cd /
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -S '-n' -X 'fsx'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/565.out b/tests/xfs/565.out
new file mode 100644 (file)
index 0000000..5aa3a93
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 565
+Silence is golden
diff --git a/tests/xfs/566 b/tests/xfs/566
new file mode 100755 (executable)
index 0000000..4003ff3
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 566
+#
+# Race fsx and xfs_scrub in force-repair mode for a while to see if we
+# crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       cd /
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -S '-k' -X 'fsx'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/566.out b/tests/xfs/566.out
new file mode 100644 (file)
index 0000000..31df95a
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 566
+Silence is golden
diff --git a/tests/xfs/567 b/tests/xfs/567
new file mode 100755 (executable)
index 0000000..b19eca2
--- /dev/null
@@ -0,0 +1,125 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 567
+#
+# Tests `xfsrestore -x` which handles an wrong inode in a dump, with the
+# multi-level dumps where we hit an issue during development.
+# This procedure is cribbed from:
+#     xfs/064: test multilevel dump and restores with hardlinks
+
+. ./common/preamble
+_begin_fstest auto dump
+
+# Import common functions.
+. ./common/dump
+
+_supported_fs xfs
+_fixed_by_git_commit xfsdump \
+       "XXXXXXXXXXXX xfsrestore: fix rootdir due to xfsdump bulkstat misuse"
+_require_xfs_io_command "falloc"
+_require_scratch
+_require_xfsrestore_xflag
+
+# Create a filesystem which contains a fake root inode
+inums=($(_scratch_xfs_create_fake_root))
+root_inum=${inums[0]}
+fake_inum=${inums[1]}
+
+# Remove unnecessary files
+find $SCRATCH_MNT -not -inum $fake_inum -type f -delete
+# Rename a file root file to the static filename
+find $SCRATCH_MNT -inum $fake_inum -exec mv {} $SCRATCH_MNT/fakeroot \;
+
+# Override the default cleanup function.
+_cleanup()
+{
+       _cleanup_dump
+       cd /
+       rm -f $tmp.*
+}
+
+_ls_size_filter()
+{
+       #
+       # Print size ($5) and fname ($9).
+       # The size is significant since we add to the file as part
+       # of a file change for the incremental.
+       #
+       # Filter out the housekeeping files of xfsrestore
+       #
+       $AWK_PROG 'NF == 9 { print $5, $9 }' |\
+       grep -E -v 'dumpdir|housekeeping|dirattr|dirextattr|namreg|state|tree'
+}
+
+
+_create_dumpdir_hardlinks 9
+
+echo "Do the incremental dumps"
+i=0
+while [ $i -le 9 ]; do
+       if [ $i -gt 0 ]; then
+               sleep 2
+               _modify_level $i
+       fi
+
+       _stable_fs
+       sleep 2
+
+       echo "********* level $i ***********" >>$seqres.full
+       date >>$seqres.full
+       find $SCRATCH_MNT -exec $here/src/lstat64 {} \; | sed 's/(00.*)//' >$tmp.dates.$i
+       if [ $i -gt 0 ]; then
+               let level_1=$i-1
+               diff -c $tmp.dates.$level_1 $tmp.dates.$i >>$seqres.full
+       else
+               cat $tmp.dates.$i >>$seqres.full
+       fi
+
+       dumpfile=$tmp.df.level$i
+       _do_dump_file -f $dumpfile -l $i
+       # Set the wrong root inode number to the dump file
+       # as problematic xfsdump used to do.
+       $here/src/fake-dump-rootino $dumpfile $fake_inum
+
+       let i=$i+1
+done
+
+echo "Listing of what files we start with:"
+ls -l $dump_dir | _ls_size_filter
+
+echo "Look at what files are contained in the inc. dump"
+i=0
+while [ $i -le 9 ]; do
+       echo ""
+       echo "restoring from df.level$i"
+       _do_restore_toc -x -f $tmp.df.level$i | \
+               sed -e "s/rootino #${fake_inum}/rootino #FAKENO/g" \
+               -e "s/# to ${root_inum}/# to ROOTNO/g"
+       let i=$i+1
+done
+
+echo "Do the cumulative restores"
+_prepare_restore_dir
+i=0
+while [ $i -le 9 ]; do
+       if [ $i -eq 0 ]; then
+               # The root inode is fixed at the first restore
+               opt='-x'
+       else
+               opt=
+       fi
+       echo ""
+       echo "restoring from df.level$i"
+       _do_restore_file_cum $opt -f $tmp.df.level$i | \
+               sed -e "s/rootino #${fake_inum}/rootino #FAKENO/g" \
+               -e "s/# to ${root_inum}/# to ROOTNO/g"
+       echo "ls -l restore_dir"
+       ls -lR $restore_dir | _ls_size_filter | _check_quota_file
+       let i=$i+1
+done
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/567.out b/tests/xfs/567.out
new file mode 100644 (file)
index 0000000..7b5beea
--- /dev/null
@@ -0,0 +1,1517 @@
+QA output created by 567
+Creating directory system of hardlinks to incrementally dump.
+creating hardlink file1_h1 to file1
+creating hardlink file1_h2 to file1
+creating hardlink file1_h3 to file1
+creating hardlink file1_h4 to file1
+creating hardlink file1_h5 to file1
+creating hardlink file2_h1 to file2
+creating hardlink file2_h2 to file2
+creating hardlink file2_h3 to file2
+creating hardlink file2_h4 to file2
+creating hardlink file2_h5 to file2
+creating hardlink file3_h1 to file3
+creating hardlink file3_h2 to file3
+creating hardlink file3_h3 to file3
+creating hardlink file3_h4 to file3
+creating hardlink file3_h5 to file3
+creating hardlink file4_h1 to file4
+creating hardlink file4_h2 to file4
+creating hardlink file4_h3 to file4
+creating hardlink file4_h4 to file4
+creating hardlink file4_h5 to file4
+creating hardlink file5_h1 to file5
+creating hardlink file5_h2 to file5
+creating hardlink file5_h3 to file5
+creating hardlink file5_h4 to file5
+creating hardlink file5_h5 to file5
+creating hardlink file6_h1 to file6
+creating hardlink file6_h2 to file6
+creating hardlink file6_h3 to file6
+creating hardlink file6_h4 to file6
+creating hardlink file6_h5 to file6
+creating hardlink file7_h1 to file7
+creating hardlink file7_h2 to file7
+creating hardlink file7_h3 to file7
+creating hardlink file7_h4 to file7
+creating hardlink file7_h5 to file7
+creating hardlink file8_h1 to file8
+creating hardlink file8_h2 to file8
+creating hardlink file8_h3 to file8
+creating hardlink file8_h4 to file8
+creating hardlink file8_h5 to file8
+creating hardlink file9_h1 to file9
+creating hardlink file9_h2 to file9
+creating hardlink file9_h3 to file9
+creating hardlink file9_h4 to file9
+creating hardlink file9_h5 to file9
+Do the incremental dumps
+Dumping to file...
+xfsdump  -l0 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 0 dump of HOSTNAME:SCRATCH_MNT
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: /var/xfsdump/inventory created
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump  -l1 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 1 incremental dump of HOSTNAME:SCRATCH_MNT based on level 0 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump  -l2 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 2 incremental dump of HOSTNAME:SCRATCH_MNT based on level 1 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump  -l3 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 3 incremental dump of HOSTNAME:SCRATCH_MNT based on level 2 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump  -l4 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 4 incremental dump of HOSTNAME:SCRATCH_MNT based on level 3 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump  -l5 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 5 incremental dump of HOSTNAME:SCRATCH_MNT based on level 4 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump  -l6 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 6 incremental dump of HOSTNAME:SCRATCH_MNT based on level 5 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump  -l7 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 7 incremental dump of HOSTNAME:SCRATCH_MNT based on level 6 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump  -l8 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 8 incremental dump of HOSTNAME:SCRATCH_MNT based on level 7 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Dumping to file...
+xfsdump  -l9 -f DUMP_FILE -M stress_tape_media -L stress_567 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 9 incremental dump of HOSTNAME:SCRATCH_MNT based on level 8 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_567"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we start with:
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+12 file4
+12 file4_h1
+12 file4_h2
+12 file4_h3
+12 file4_h4
+12 file4_h5
+12 file5
+12 file5_h1
+12 file5_h2
+12 file5_h3
+12 file5_h4
+12 file5_h5
+12 file6
+12 file6_h1
+12 file6_h2
+12 file6_h3
+12 file6_h4
+12 file6_h5
+12 file7
+12 file7_h1
+12 file7_h2
+12 file7_h3
+12 file7_h4
+12 file7_h5
+12 file8
+12 file8_h1
+12 file8_h2
+12 file8_h3
+12 file8_h4
+12 file8_h5
+12 file9
+12 file9_h1
+12 file9_h2
+12 file9_h3
+12 file9_h4
+12 file9_h5
+Look at what files are contained in the inc. dump
+
+restoring from df.level0
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 0
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file1
+dumpdir/file1_h1
+dumpdir/file1_h2
+dumpdir/file1_h3
+dumpdir/file1_h4
+dumpdir/file1_h5
+dumpdir/file2
+dumpdir/file2_h1
+dumpdir/file2_h2
+dumpdir/file2_h3
+dumpdir/file2_h4
+dumpdir/file2_h5
+dumpdir/file3
+dumpdir/file3_h1
+dumpdir/file3_h2
+dumpdir/file3_h3
+dumpdir/file3_h4
+dumpdir/file3_h5
+dumpdir/file4
+dumpdir/file4_h1
+dumpdir/file4_h2
+dumpdir/file4_h3
+dumpdir/file4_h4
+dumpdir/file4_h5
+dumpdir/file5
+dumpdir/file5_h1
+dumpdir/file5_h2
+dumpdir/file5_h3
+dumpdir/file5_h4
+dumpdir/file5_h5
+dumpdir/file6
+dumpdir/file6_h1
+dumpdir/file6_h2
+dumpdir/file6_h3
+dumpdir/file6_h4
+dumpdir/file6_h5
+dumpdir/file7
+dumpdir/file7_h1
+dumpdir/file7_h2
+dumpdir/file7_h3
+dumpdir/file7_h4
+dumpdir/file7_h5
+dumpdir/file8
+dumpdir/file8_h1
+dumpdir/file8_h2
+dumpdir/file8_h3
+dumpdir/file8_h4
+dumpdir/file8_h5
+dumpdir/file9
+dumpdir/file9_h1
+dumpdir/file9_h2
+dumpdir/file9_h3
+dumpdir/file9_h4
+dumpdir/file9_h5
+fakeroot
+
+restoring from df.level1
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 1
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file1
+dumpdir/file1_h1
+dumpdir/file1_h2
+dumpdir/file1_h3
+dumpdir/file1_h4
+dumpdir/file1_h5
+
+restoring from df.level2
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 2
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file2
+dumpdir/file2_h1
+dumpdir/file2_h2
+dumpdir/file2_h3
+dumpdir/file2_h4
+dumpdir/file2_h5
+
+restoring from df.level3
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 3
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file3
+dumpdir/file3_h1
+dumpdir/file3_h2
+dumpdir/file3_h3
+dumpdir/file3_h4
+dumpdir/file3_h5
+
+restoring from df.level4
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 4
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file4
+dumpdir/file4_h1
+dumpdir/file4_h2
+dumpdir/file4_h3
+dumpdir/file4_h4
+dumpdir/file4_h5
+
+restoring from df.level5
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 5
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file5
+dumpdir/file5_h1
+dumpdir/file5_h2
+dumpdir/file5_h3
+dumpdir/file5_h4
+dumpdir/file5_h5
+
+restoring from df.level6
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 6
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file6
+dumpdir/file6_h1
+dumpdir/file6_h2
+dumpdir/file6_h3
+dumpdir/file6_h4
+dumpdir/file6_h5
+
+restoring from df.level7
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 7
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file7
+dumpdir/file7_h1
+dumpdir/file7_h2
+dumpdir/file7_h3
+dumpdir/file7_h4
+dumpdir/file7_h5
+
+restoring from df.level8
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 8
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file8
+dumpdir/file8_h1
+dumpdir/file8_h2
+dumpdir/file8_h3
+dumpdir/file8_h4
+dumpdir/file8_h5
+
+restoring from df.level9
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 9
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/file9
+dumpdir/file9_h1
+dumpdir/file9_h2
+dumpdir/file9_h3
+dumpdir/file9_h4
+dumpdir/file9_h5
+Do the cumulative restores
+
+restoring from df.level0
+Restoring cumumlative from file...
+xfsrestore  -x -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 0
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+0 file1
+0 file1_h1
+0 file1_h2
+0 file1_h3
+0 file1_h4
+0 file1_h5
+0 file2
+0 file2_h1
+0 file2_h2
+0 file2_h3
+0 file2_h4
+0 file2_h5
+0 file3
+0 file3_h1
+0 file3_h2
+0 file3_h3
+0 file3_h4
+0 file3_h5
+0 file4
+0 file4_h1
+0 file4_h2
+0 file4_h3
+0 file4_h4
+0 file4_h5
+0 file5
+0 file5_h1
+0 file5_h2
+0 file5_h3
+0 file5_h4
+0 file5_h5
+0 file6
+0 file6_h1
+0 file6_h2
+0 file6_h3
+0 file6_h4
+0 file6_h5
+0 file7
+0 file7_h1
+0 file7_h2
+0 file7_h3
+0 file7_h4
+0 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level1
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 1
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+0 file2
+0 file2_h1
+0 file2_h2
+0 file2_h3
+0 file2_h4
+0 file2_h5
+0 file3
+0 file3_h1
+0 file3_h2
+0 file3_h3
+0 file3_h4
+0 file3_h5
+0 file4
+0 file4_h1
+0 file4_h2
+0 file4_h3
+0 file4_h4
+0 file4_h5
+0 file5
+0 file5_h1
+0 file5_h2
+0 file5_h3
+0 file5_h4
+0 file5_h5
+0 file6
+0 file6_h1
+0 file6_h2
+0 file6_h3
+0 file6_h4
+0 file6_h5
+0 file7
+0 file7_h1
+0 file7_h2
+0 file7_h3
+0 file7_h4
+0 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level2
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 2
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+0 file3
+0 file3_h1
+0 file3_h2
+0 file3_h3
+0 file3_h4
+0 file3_h5
+0 file4
+0 file4_h1
+0 file4_h2
+0 file4_h3
+0 file4_h4
+0 file4_h5
+0 file5
+0 file5_h1
+0 file5_h2
+0 file5_h3
+0 file5_h4
+0 file5_h5
+0 file6
+0 file6_h1
+0 file6_h2
+0 file6_h3
+0 file6_h4
+0 file6_h5
+0 file7
+0 file7_h1
+0 file7_h2
+0 file7_h3
+0 file7_h4
+0 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level3
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 3
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+0 file4
+0 file4_h1
+0 file4_h2
+0 file4_h3
+0 file4_h4
+0 file4_h5
+0 file5
+0 file5_h1
+0 file5_h2
+0 file5_h3
+0 file5_h4
+0 file5_h5
+0 file6
+0 file6_h1
+0 file6_h2
+0 file6_h3
+0 file6_h4
+0 file6_h5
+0 file7
+0 file7_h1
+0 file7_h2
+0 file7_h3
+0 file7_h4
+0 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level4
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 4
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+12 file4
+12 file4_h1
+12 file4_h2
+12 file4_h3
+12 file4_h4
+12 file4_h5
+0 file5
+0 file5_h1
+0 file5_h2
+0 file5_h3
+0 file5_h4
+0 file5_h5
+0 file6
+0 file6_h1
+0 file6_h2
+0 file6_h3
+0 file6_h4
+0 file6_h5
+0 file7
+0 file7_h1
+0 file7_h2
+0 file7_h3
+0 file7_h4
+0 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level5
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 5
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+12 file4
+12 file4_h1
+12 file4_h2
+12 file4_h3
+12 file4_h4
+12 file4_h5
+12 file5
+12 file5_h1
+12 file5_h2
+12 file5_h3
+12 file5_h4
+12 file5_h5
+0 file6
+0 file6_h1
+0 file6_h2
+0 file6_h3
+0 file6_h4
+0 file6_h5
+0 file7
+0 file7_h1
+0 file7_h2
+0 file7_h3
+0 file7_h4
+0 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level6
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 6
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+12 file4
+12 file4_h1
+12 file4_h2
+12 file4_h3
+12 file4_h4
+12 file4_h5
+12 file5
+12 file5_h1
+12 file5_h2
+12 file5_h3
+12 file5_h4
+12 file5_h5
+12 file6
+12 file6_h1
+12 file6_h2
+12 file6_h3
+12 file6_h4
+12 file6_h5
+0 file7
+0 file7_h1
+0 file7_h2
+0 file7_h3
+0 file7_h4
+0 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level7
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 7
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+12 file4
+12 file4_h1
+12 file4_h2
+12 file4_h3
+12 file4_h4
+12 file4_h5
+12 file5
+12 file5_h1
+12 file5_h2
+12 file5_h3
+12 file5_h4
+12 file5_h5
+12 file6
+12 file6_h1
+12 file6_h2
+12 file6_h3
+12 file6_h4
+12 file6_h5
+12 file7
+12 file7_h1
+12 file7_h2
+12 file7_h3
+12 file7_h4
+12 file7_h5
+0 file8
+0 file8_h1
+0 file8_h2
+0 file8_h3
+0 file8_h4
+0 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level8
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 8
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+12 file4
+12 file4_h1
+12 file4_h2
+12 file4_h3
+12 file4_h4
+12 file4_h5
+12 file5
+12 file5_h1
+12 file5_h2
+12 file5_h3
+12 file5_h4
+12 file5_h5
+12 file6
+12 file6_h1
+12 file6_h2
+12 file6_h3
+12 file6_h4
+12 file6_h5
+12 file7
+12 file7_h1
+12 file7_h2
+12 file7_h3
+12 file7_h4
+12 file7_h5
+12 file8
+12 file8_h1
+12 file8_h2
+12 file8_h3
+12 file8_h4
+12 file8_h5
+0 file9
+0 file9_h1
+0 file9_h2
+0 file9_h3
+0 file9_h4
+0 file9_h5
+
+restoring from df.level9
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 9
+xfsrestore: session label: "stress_567"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 56 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+ls -l restore_dir
+0 fakeroot
+12 file1
+12 file1_h1
+12 file1_h2
+12 file1_h3
+12 file1_h4
+12 file1_h5
+12 file2
+12 file2_h1
+12 file2_h2
+12 file2_h3
+12 file2_h4
+12 file2_h5
+12 file3
+12 file3_h1
+12 file3_h2
+12 file3_h3
+12 file3_h4
+12 file3_h5
+12 file4
+12 file4_h1
+12 file4_h2
+12 file4_h3
+12 file4_h4
+12 file4_h5
+12 file5
+12 file5_h1
+12 file5_h2
+12 file5_h3
+12 file5_h4
+12 file5_h5
+12 file6
+12 file6_h1
+12 file6_h2
+12 file6_h3
+12 file6_h4
+12 file6_h5
+12 file7
+12 file7_h1
+12 file7_h2
+12 file7_h3
+12 file7_h4
+12 file7_h5
+12 file8
+12 file8_h1
+12 file8_h2
+12 file8_h3
+12 file8_h4
+12 file8_h5
+12 file9
+12 file9_h1
+12 file9_h2
+12 file9_h3
+12 file9_h4
+12 file9_h5
diff --git a/tests/xfs/568 b/tests/xfs/568
new file mode 100755 (executable)
index 0000000..017e17a
--- /dev/null
@@ -0,0 +1,213 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Fujitsu Limited. All Rights Reserved.
+#
+# FS QA Test No. 568
+#
+# Tests `xfsrestore -x` which handles an wrong inode in a dump, with the
+# multi-level dumps where we hit an issue during development.
+# This procedure is cribbed from:
+#   xfs/065: Testing incremental dumps and cumulative restores with
+#            different operations for each level
+
+. ./common/preamble
+_begin_fstest auto dump
+
+# Override the default cleanup function.
+_cleanup()
+{
+       _cleanup_dump
+       cd /
+       rm -f $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/dump
+. ./common/quota
+
+#
+# list recursively the directory
+#
+# e.g. lstat output: src/lstat64 31056 -rwxr-xr-x 38403,0
+# Don't print out sizes of directories - which can vary - overwrite with XXX.
+#
+_list_dir()
+{
+       __dir=$1
+       find $__dir  -exec $here/src/lstat64 -t {} \; |\
+       sed -e 's/.*dumpdir/dumpdir/' -e '/^dumpdir /d' |\
+       sed -e 's/.*restoredir/restoredir/' -e '/^restoredir /d' |\
+       grep -E -v 'housekeeping|dirattr|dirextattr|namreg|state|tree|fakeroot' |\
+       awk '$3 ~ /^d/ { $2 = "XXX" } {print}' |\
+       LC_COLLATE=POSIX sort
+}
+
+# real QA test starts here
+_supported_fs xfs
+_fixed_by_git_commit xfsdump \
+       "XXXXXXXXXXXX xfsrestore: fix rootdir due to xfsdump bulkstat misuse"
+_require_xfs_io_command "falloc"
+_require_scratch
+_require_xfsrestore_xflag
+
+#
+# too much hassle to get output matching with quotas turned on
+# so don't run it
+#
+_scratch_mkfs_xfs >> $seqres.full
+_qmount_option noquota
+_scratch_mount
+$here/src/feature -U $SCRATCH_DEV && \
+       _notrun "UQuota enabled, test needs controlled xfsdump output"
+$here/src/feature -G $SCRATCH_DEV && \
+       _notrun "GQuota enabled, test needs controlled xfsdump output"
+$here/src/feature -P $SCRATCH_DEV && \
+       _notrun "PQuota enabled, test needs controlled xfsdump output"
+_scratch_unmount
+
+#
+# adding      - touch/echo, mkdir
+# deleting    - rm, rmdir
+# renaming    - mv
+# linking     - ln
+# unlinking   - rm
+# files and directories
+#
+
+# Create a filesystem which contains a fake root inode
+inums=($(_scratch_xfs_create_fake_root))
+root_inum=${inums[0]}
+fake_inum=${inums[1]}
+
+# Remove unnecessary files
+find $SCRATCH_MNT -not -inum $fake_inum -type f -delete
+# Rename a file root file to the static filename
+find $SCRATCH_MNT -inum $fake_inum -exec mv {} $SCRATCH_MNT/fakeroot \;
+
+mkdir -p $dump_dir || _fail "cannot mkdir \"$dump_dir\""
+cd $dump_dir
+
+echo "Do the incremental dumps"
+i=0
+num_dumps=8 # do some extra to ensure nothing changes
+while [ $i -le $num_dumps ]; do
+       cd $dump_dir
+       case $i in
+       0)
+               # adding
+               echo 'add0' >addedfile0
+               echo 'add1' >addedfile1
+               echo 'add2' >addedfile2
+               echo 'add3' >addedfile3
+               mkdir addeddir1
+               mkdir addeddir2
+               mkdir addeddir3
+               mkdir addeddir4
+               echo 'add4' >addeddir3/addedfile4
+               echo 'add5' >addeddir4/addedfile5
+               ;;
+       1)
+               # deleting
+               rm addedfile2
+               rmdir addeddir2
+               rm -rf addeddir3
+               ;;
+       2)
+               # renaming
+               mv addedfile1 addedfile2 # rename to previous existing file
+               mv addeddir4/addedfile5 addeddir4/addedfile4
+               mv addeddir4 addeddir6
+               mv addeddir1 addeddir2 # rename to previous existing dir
+               ;;
+       3)
+               # linking
+               ln addedfile0 linkfile0
+               ln addedfile0 linkfile0_1  # have a 2nd link to file
+               ln addedfile2 linkfile2
+               ln addeddir6/addedfile4 linkfile64
+               ;;
+       4)
+               # unlinking
+               rm linkfile0  # remove a link
+               rm addedfile2 # remove original link
+               rm linkfile64  # remove link
+               rm addeddir6/addedfile4 # remove last link
+               ;;
+       5)  # link first - then onto 6)
+               rm -rf *
+               echo 'add6' >addedfile6
+               ln addedfile6 linkfile6_1
+               ln addedfile6 linkfile6_2
+               ln addedfile6 linkfile6_3
+               ;;
+       6)  # then move the inode that the links point to
+                       mv addedfile6 addedfile6_mv
+               rm linkfile6_1
+               rm linkfile6_2
+               rm linkfile6_3
+               ln addedfile6_mv linkfile6_mv_1
+               ln addedfile6_mv linkfile6_mv_2
+               ln addedfile6_mv linkfile6_mv_3
+               ;;
+       esac
+       cd $here
+       sleep 2
+       _stable_fs
+
+       echo "Listing of what files we have at level $i:"
+       _list_dir $dump_dir     | tee $tmp.ls.$i
+
+       dumpfile=$tmp.df.level$i
+       _do_dump_file -f $dumpfile -l $i
+       # Set the wrong root inode number to the dump file
+       # as problematic xfsdump used to do.
+       $here/src/fake-dump-rootino $dumpfile $fake_inum
+
+       let i=$i+1
+done
+
+echo "Look at what files are contained in the inc. dump"
+i=0
+while [ $i -le $num_dumps ]; do
+       echo ""
+       echo "restoring from df.level$i"
+       _do_restore_toc -x -f $tmp.df.level$i | \
+               sed -e "s/rootino #${fake_inum}/rootino #FAKENO/g" \
+               -e "s/# to ${root_inum}/# to ROOTNO/g"
+       let i=$i+1
+done
+
+echo "Do the cumulative restores"
+_prepare_restore_dir
+i=0
+while [ $i -le $num_dumps ]; do
+       if [ $i -eq 0 ]; then
+               # The root inode is fixed at the first restore
+               opt='-x'
+       else
+               opt=
+       fi
+       echo ""
+       echo "restoring from df.level$i"
+       _do_restore_file_cum $opt -f $tmp.df.level$i | \
+               sed -e "s/rootino #${fake_inum}/rootino #FAKENO/g" \
+               -e "s/# to ${root_inum}/# to ROOTNO/g"
+       echo "list restore_dir"
+       _list_dir $restore_dir | _check_quota_file | tee $tmp.restorals.$i
+       let i=$i+1
+done
+
+echo ""
+echo "Do the ls comparison"
+i=0
+while [ $i -le $num_dumps ]; do
+       echo "Comparing ls of FS with restored FS at level $i"
+       diff -s $tmp.ls.$i $tmp.restorals.$i | sed "s#$tmp#TMP#g"
+       echo ""
+       let i=$i+1
+done
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/568.out b/tests/xfs/568.out
new file mode 100644 (file)
index 0000000..4268588
--- /dev/null
@@ -0,0 +1,849 @@
+QA output created by 568
+Do the incremental dumps
+Listing of what files we have at level 0:
+dumpdir/addeddir1 XXX drwxr-xr-x 0,0
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir3 XXX drwxr-xr-x 0,0
+dumpdir/addeddir3/addedfile4 5 -rw-r--r-- 0,0
+dumpdir/addeddir4 XXX drwxr-xr-x 0,0
+dumpdir/addeddir4/addedfile5 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile1 5 -rw-r--r-- 0,0
+dumpdir/addedfile2 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump  -l0 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 0 dump of HOSTNAME:SCRATCH_MNT
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: /var/xfsdump/inventory created
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 1:
+dumpdir/addeddir1 XXX drwxr-xr-x 0,0
+dumpdir/addeddir4 XXX drwxr-xr-x 0,0
+dumpdir/addeddir4/addedfile5 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile1 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump  -l1 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 1 incremental dump of HOSTNAME:SCRATCH_MNT based on level 0 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 2:
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6/addedfile4 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile2 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump  -l2 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 2 incremental dump of HOSTNAME:SCRATCH_MNT based on level 1 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 3:
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6/addedfile4 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile2 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+dumpdir/linkfile0 5 -rw-r--r-- 0,0
+dumpdir/linkfile0_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile2 5 -rw-r--r-- 0,0
+dumpdir/linkfile64 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump  -l3 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 3 incremental dump of HOSTNAME:SCRATCH_MNT based on level 2 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 4:
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6 XXX drwxr-xr-x 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+dumpdir/linkfile0_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile2 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump  -l4 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 4 incremental dump of HOSTNAME:SCRATCH_MNT based on level 3 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 5:
+dumpdir/addedfile6 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_3 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump  -l5 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 5 incremental dump of HOSTNAME:SCRATCH_MNT based on level 4 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 6:
+dumpdir/addedfile6_mv 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_3 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump  -l6 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 6 incremental dump of HOSTNAME:SCRATCH_MNT based on level 5 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 7:
+dumpdir/addedfile6_mv 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_3 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump  -l7 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 7 incremental dump of HOSTNAME:SCRATCH_MNT based on level 6 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Listing of what files we have at level 8:
+dumpdir/addedfile6_mv 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_3 5 -rw-r--r-- 0,0
+Dumping to file...
+xfsdump  -l8 -f DUMP_FILE -M stress_tape_media -L stress_568 SCRATCH_MNT
+xfsdump: using file dump (drive_simple) strategy
+xfsdump: level 8 incremental dump of HOSTNAME:SCRATCH_MNT based on level 7 dump begun DATE
+xfsdump: dump date: DATE
+xfsdump: session id: ID
+xfsdump: session label: "stress_568"
+xfsdump: ino map <PHASES>
+xfsdump: ino map construction complete
+xfsdump: estimated dump size: NUM bytes
+xfsdump: creating dump session media file 0 (media 0, file 0)
+xfsdump: dumping ino map
+xfsdump: dumping directories
+xfsdump: dumping non-directory files
+xfsdump: ending media file
+xfsdump: media file size NUM bytes
+xfsdump: dump size (non-dir files) : NUM bytes
+xfsdump: dump complete: SECS seconds elapsed
+xfsdump: Dump Status: SUCCESS
+Look at what files are contained in the inc. dump
+
+restoring from df.level0
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 0
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 6 directories and 12 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/addeddir3/addedfile4
+dumpdir/addeddir4/addedfile5
+dumpdir/addedfile0
+dumpdir/addedfile1
+dumpdir/addedfile2
+dumpdir/addedfile3
+fakeroot
+
+restoring from df.level1
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 1
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 7 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+
+restoring from df.level2
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 2
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 4 directories and 8 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/addeddir6/addedfile4
+dumpdir/addedfile2
+
+restoring from df.level3
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 3
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 3 directories and 12 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/addeddir6/addedfile4
+dumpdir/addedfile0
+dumpdir/addedfile2
+dumpdir/linkfile0
+dumpdir/linkfile0_1
+dumpdir/linkfile2
+dumpdir/linkfile64
+
+restoring from df.level4
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 4
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 3 directories and 8 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/addedfile0
+dumpdir/linkfile0_1
+dumpdir/linkfile2
+
+restoring from df.level5
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 5
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 6 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/addedfile6
+dumpdir/linkfile6_1
+dumpdir/linkfile6_2
+dumpdir/linkfile6_3
+
+restoring from df.level6
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 6
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 2 directories and 6 entries processed
+xfsrestore: directory post-processing
+xfsrestore: reading non-directory files
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+dumpdir/addedfile6_mv
+dumpdir/linkfile6_mv_1
+dumpdir/linkfile6_mv_2
+dumpdir/linkfile6_mv_3
+
+restoring from df.level7
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 7
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 0 directories and 0 entries processed
+xfsrestore: directory post-processing
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+
+restoring from df.level8
+Contents of dump ...
+xfsrestore  -x -f DUMP_FILE -t
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 8
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 0 directories and 0 entries processed
+xfsrestore: directory post-processing
+xfsrestore: table of contents display complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+
+Do the cumulative restores
+
+restoring from df.level0
+Restoring cumumlative from file...
+xfsrestore  -x -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 0
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: found fake rootino #FAKENO, will fix.
+xfsrestore: fix root # to ROOTNO (bind mount?)
+xfsrestore: 6 directories and 12 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addeddir1 XXX drwxr-xr-x 0,0
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir3 XXX drwxr-xr-x 0,0
+dumpdir/addeddir3/addedfile4 5 -rw-r--r-- 0,0
+dumpdir/addeddir4 XXX drwxr-xr-x 0,0
+dumpdir/addeddir4/addedfile5 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile1 5 -rw-r--r-- 0,0
+dumpdir/addedfile2 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+
+restoring from df.level1
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 1
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 7 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addeddir1 XXX drwxr-xr-x 0,0
+dumpdir/addeddir4 XXX drwxr-xr-x 0,0
+dumpdir/addeddir4/addedfile5 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile1 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+
+restoring from df.level2
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 2
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 4 directories and 8 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6/addedfile4 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile2 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+
+restoring from df.level3
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 3
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 3 directories and 12 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6/addedfile4 5 -rw-r--r-- 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile2 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+dumpdir/linkfile0 5 -rw-r--r-- 0,0
+dumpdir/linkfile0_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile2 5 -rw-r--r-- 0,0
+dumpdir/linkfile64 5 -rw-r--r-- 0,0
+
+restoring from df.level4
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 4
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 3 directories and 8 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addeddir2 XXX drwxr-xr-x 0,0
+dumpdir/addeddir6 XXX drwxr-xr-x 0,0
+dumpdir/addedfile0 5 -rw-r--r-- 0,0
+dumpdir/addedfile3 5 -rw-r--r-- 0,0
+dumpdir/linkfile0_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile2 5 -rw-r--r-- 0,0
+
+restoring from df.level5
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 5
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 6 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addedfile6 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_3 5 -rw-r--r-- 0,0
+
+restoring from df.level6
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 6
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 2 directories and 6 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addedfile6_mv 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_3 5 -rw-r--r-- 0,0
+
+restoring from df.level7
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 7
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 0 directories and 0 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addedfile6_mv 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_3 5 -rw-r--r-- 0,0
+
+restoring from df.level8
+Restoring cumumlative from file...
+xfsrestore  -f DUMP_FILE -r RESTORE_DIR
+xfsrestore: using file dump (drive_simple) strategy
+xfsrestore: searching media for dump
+xfsrestore: examining media file 0
+xfsrestore: dump description: 
+xfsrestore: hostname: HOSTNAME
+xfsrestore: mount point: SCRATCH_MNT
+xfsrestore: volume: SCRATCH_DEV
+xfsrestore: session time: TIME
+xfsrestore: level: 8
+xfsrestore: session label: "stress_568"
+xfsrestore: media label: "stress_tape_media"
+xfsrestore: file system ID: ID
+xfsrestore: session id: ID
+xfsrestore: media ID: ID
+xfsrestore: using online session inventory
+xfsrestore: searching media for directory dump
+xfsrestore: reading directories
+xfsrestore: 0 directories and 0 entries processed
+xfsrestore: directory post-processing
+xfsrestore: restoring non-directory files
+xfsrestore: restore complete: SECS seconds elapsed
+xfsrestore: Restore Status: SUCCESS
+list restore_dir
+dumpdir/addedfile6_mv 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_1 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_2 5 -rw-r--r-- 0,0
+dumpdir/linkfile6_mv_3 5 -rw-r--r-- 0,0
+
+Do the ls comparison
+Comparing ls of FS with restored FS at level 0
+Files TMP.ls.0 and TMP.restorals.0 are identical
+
+Comparing ls of FS with restored FS at level 1
+Files TMP.ls.1 and TMP.restorals.1 are identical
+
+Comparing ls of FS with restored FS at level 2
+Files TMP.ls.2 and TMP.restorals.2 are identical
+
+Comparing ls of FS with restored FS at level 3
+Files TMP.ls.3 and TMP.restorals.3 are identical
+
+Comparing ls of FS with restored FS at level 4
+Files TMP.ls.4 and TMP.restorals.4 are identical
+
+Comparing ls of FS with restored FS at level 5
+Files TMP.ls.5 and TMP.restorals.5 are identical
+
+Comparing ls of FS with restored FS at level 6
+Files TMP.ls.6 and TMP.restorals.6 are identical
+
+Comparing ls of FS with restored FS at level 7
+Files TMP.ls.7 and TMP.restorals.7 are identical
+
+Comparing ls of FS with restored FS at level 8
+Files TMP.ls.8 and TMP.restorals.8 are identical
+
diff --git a/tests/xfs/569 b/tests/xfs/569
new file mode 100755 (executable)
index 0000000..b6d5798
--- /dev/null
@@ -0,0 +1,32 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Red Hat, Inc.  All Rights Reserved.
+#
+# FS QA Test 569
+#
+# Check for any installed example mkfs config files and validate that
+# mkfs.xfs can properly use them.
+#
+. ./common/preamble
+_begin_fstest mkfs
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_scratch_nocheck
+
+ls /usr/share/xfsprogs/mkfs/*.conf &>/dev/null || \
+       _notrun "No mkfs.xfs config files installed"
+
+# We only fail if mkfs.xfs fails outright, ignoring warnings etc
+echo "Silence is golden"
+
+for CONFIG in /usr/share/xfsprogs/mkfs/*.conf; do
+       $MKFS_XFS_PROG -c options=$CONFIG -f $SCRATCH_DEV &>>$seqres.full || \
+               echo "mkfs.xfs config file $CONFIG failed"
+done
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/569.out b/tests/xfs/569.out
new file mode 100644 (file)
index 0000000..c7aaf10
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 569
+Silence is golden
diff --git a/tests/xfs/570 b/tests/xfs/570
new file mode 100755 (executable)
index 0000000..9f3ba87
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 570
+#
+# Race fsstress and superblock scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub sb %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/570.out b/tests/xfs/570.out
new file mode 100644 (file)
index 0000000..3a81a47
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 570
+Silence is golden
diff --git a/tests/xfs/571 b/tests/xfs/571
new file mode 100755 (executable)
index 0000000..9d22de8
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 571
+#
+# Race fsstress and AGF scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub agf %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/571.out b/tests/xfs/571.out
new file mode 100644 (file)
index 0000000..71f15d6
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 571
+Silence is golden
diff --git a/tests/xfs/572 b/tests/xfs/572
new file mode 100755 (executable)
index 0000000..b0e352a
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 572
+#
+# Race fsstress and AGFL scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub agfl %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/572.out b/tests/xfs/572.out
new file mode 100644 (file)
index 0000000..5fa46a3
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 572
+Silence is golden
diff --git a/tests/xfs/573 b/tests/xfs/573
new file mode 100755 (executable)
index 0000000..a2b6bef
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 573
+#
+# Race fsstress and AGI scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub agi %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/573.out b/tests/xfs/573.out
new file mode 100644 (file)
index 0000000..e8779a4
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 573
+Silence is golden
diff --git a/tests/xfs/574 b/tests/xfs/574
new file mode 100755 (executable)
index 0000000..5a4bad0
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 574
+#
+# Race fsstress and freespace by block btree scrub for a while to see if we
+# crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub bnobt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/574.out b/tests/xfs/574.out
new file mode 100644 (file)
index 0000000..ec97306
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 574
+Silence is golden
diff --git a/tests/xfs/575 b/tests/xfs/575
new file mode 100755 (executable)
index 0000000..3d29620
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 575
+#
+# Race fsstress and free space by length btree scrub for a while to see if we
+# crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub cntbt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/575.out b/tests/xfs/575.out
new file mode 100644 (file)
index 0000000..8bbb2ca
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 575
+Silence is golden
diff --git a/tests/xfs/576 b/tests/xfs/576
new file mode 100755 (executable)
index 0000000..e11476d
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 576
+#
+# Race fsstress and inode btree scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -x 'dir' -s "scrub inobt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/576.out b/tests/xfs/576.out
new file mode 100644 (file)
index 0000000..e102aa2
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 576
+Silence is golden
diff --git a/tests/xfs/577 b/tests/xfs/577
new file mode 100755 (executable)
index 0000000..d1abe6f
--- /dev/null
@@ -0,0 +1,39 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 577
+#
+# Race fsstress and free inode btree scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" finobt
+_scratch_xfs_stress_scrub -x 'dir' -s "scrub finobt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/577.out b/tests/xfs/577.out
new file mode 100644 (file)
index 0000000..5869eaf
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 577
+Silence is golden
diff --git a/tests/xfs/578 b/tests/xfs/578
new file mode 100755 (executable)
index 0000000..8160b7e
--- /dev/null
@@ -0,0 +1,39 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 578
+#
+# Race fsstress and reverse mapping btree scrub for a while to see if we crash
+# or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" rmapbt
+_scratch_xfs_stress_scrub -s "scrub rmapbt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/578.out b/tests/xfs/578.out
new file mode 100644 (file)
index 0000000..7b85353
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 578
+Silence is golden
diff --git a/tests/xfs/579 b/tests/xfs/579
new file mode 100755 (executable)
index 0000000..a00ae02
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 579
+#
+# Race fsstress and reference count btree scrub for a while to see if we crash
+# or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/reflink
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" reflink
+_scratch_xfs_stress_scrub -s "scrub refcountbt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/579.out b/tests/xfs/579.out
new file mode 100644 (file)
index 0000000..06f4633
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 579
+Silence is golden
diff --git a/tests/xfs/580 b/tests/xfs/580
new file mode 100755 (executable)
index 0000000..f49cba6
--- /dev/null
@@ -0,0 +1,44 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 580
+#
+# Race fsstress and fscounter scrub on the realtime device for a while to see
+# if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" realtime
+
+# Force all files to be allocated on the realtime device
+_xfs_force_bdev realtime $SCRATCH_MNT
+
+_scratch_xfs_stress_scrub -s 'scrub fscounters'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/580.out b/tests/xfs/580.out
new file mode 100644 (file)
index 0000000..b51684f
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 580
+Silence is golden
diff --git a/tests/xfs/581 b/tests/xfs/581
new file mode 100755 (executable)
index 0000000..1d08bc7
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 581
+#
+# Race fsstress and realtime bitmap scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" realtime
+_scratch_xfs_stress_scrub -s "scrub rtbitmap"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/581.out b/tests/xfs/581.out
new file mode 100644 (file)
index 0000000..12db4d1
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 581
+Silence is golden
diff --git a/tests/xfs/582 b/tests/xfs/582
new file mode 100755 (executable)
index 0000000..7a8c330
--- /dev/null
@@ -0,0 +1,47 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 582
+#
+# Race fsstress and realtime summary scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" realtime
+
+# XXX the realtime summary scrubber isn't currently implemented upstream.
+# Don't bother trying to test it on those kernels
+$XFS_IO_PROG -c 'scrub rtsummary' -c 'scrub rtsummary' "$SCRATCH_MNT" 2>&1 | \
+       grep -q 'Scan was not complete' && \
+       _notrun "rtsummary scrub is incomplete"
+
+_scratch_xfs_stress_scrub -s "scrub rtsummary"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/582.out b/tests/xfs/582.out
new file mode 100644 (file)
index 0000000..100f399
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 582
+Silence is golden
diff --git a/tests/xfs/583 b/tests/xfs/583
new file mode 100755 (executable)
index 0000000..a6121a8
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 583
+#
+# Race fsstress and user quota scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" usrquota
+_scratch_xfs_stress_scrub -s "scrub usrquota"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/583.out b/tests/xfs/583.out
new file mode 100644 (file)
index 0000000..a2e0382
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 583
+Silence is golden
diff --git a/tests/xfs/584 b/tests/xfs/584
new file mode 100755 (executable)
index 0000000..c80ba57
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 584
+#
+# Race fsstress and group quota scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" grpquota
+_scratch_xfs_stress_scrub -s "scrub grpquota"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/584.out b/tests/xfs/584.out
new file mode 100644 (file)
index 0000000..c642da8
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 584
+Silence is golden
diff --git a/tests/xfs/585 b/tests/xfs/585
new file mode 100755 (executable)
index 0000000..ea47dad
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 585
+#
+# Race fsstress and project quota scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" prjquota
+_scratch_xfs_stress_scrub -s "scrub prjquota"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/585.out b/tests/xfs/585.out
new file mode 100644 (file)
index 0000000..e4dd43b
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 585
+Silence is golden
diff --git a/tests/xfs/586 b/tests/xfs/586
new file mode 100755 (executable)
index 0000000..e802ee7
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 586
+#
+# Race fsstress and summary counters scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub fscounters"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/586.out b/tests/xfs/586.out
new file mode 100644 (file)
index 0000000..3d36442
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 586
+Silence is golden
diff --git a/tests/xfs/587 b/tests/xfs/587
new file mode 100755 (executable)
index 0000000..71e1ce6
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 587
+#
+# Race fsstress and inode record scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub inode" -t "%file%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/587.out b/tests/xfs/587.out
new file mode 100644 (file)
index 0000000..2d94c72
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 587
+Silence is golden
diff --git a/tests/xfs/588 b/tests/xfs/588
new file mode 100755 (executable)
index 0000000..f56c50a
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 588
+#
+# Race fsstress and data fork scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub bmapbtd" -t "%datafile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/588.out b/tests/xfs/588.out
new file mode 100644 (file)
index 0000000..5c2c4b2
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 588
+Silence is golden
diff --git a/tests/xfs/589 b/tests/xfs/589
new file mode 100755 (executable)
index 0000000..d9cd81e
--- /dev/null
@@ -0,0 +1,39 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 589
+#
+# Race fsstress and attr fork scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/attr
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_attrs
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -x 'xattr' -s "scrub bmapbta" -t "%attrfile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/589.out b/tests/xfs/589.out
new file mode 100644 (file)
index 0000000..5ab6ab1
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 589
+Silence is golden
diff --git a/tests/xfs/590 b/tests/xfs/590
new file mode 100755 (executable)
index 0000000..4e39109
--- /dev/null
@@ -0,0 +1,39 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 590
+#
+# Race fsstress and cow fork scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/reflink
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" reflink
+_scratch_xfs_stress_scrub -s "scrub bmapbtc" -t "%cowfile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/590.out b/tests/xfs/590.out
new file mode 100644 (file)
index 0000000..c8f8be6
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 590
+Silence is golden
diff --git a/tests/xfs/591 b/tests/xfs/591
new file mode 100755 (executable)
index 0000000..00d5114
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 591
+#
+# Race fsstress and directory scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -x 'dir' -s "scrub directory" -t "%dir%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/591.out b/tests/xfs/591.out
new file mode 100644 (file)
index 0000000..e570723
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 591
+Silence is golden
diff --git a/tests/xfs/592 b/tests/xfs/592
new file mode 100755 (executable)
index 0000000..02ac456
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 592
+#
+# Race fsstress and extended attributes scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/attr
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_attrs
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -x 'xattr' -s "scrub xattr" -t "%attrfile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/592.out b/tests/xfs/592.out
new file mode 100644 (file)
index 0000000..f4ace92
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 592
+Silence is golden
diff --git a/tests/xfs/593 b/tests/xfs/593
new file mode 100755 (executable)
index 0000000..cf2ac50
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 593
+#
+# Race fsstress and parent pointers scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -s "scrub parent" -t "%dir%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/593.out b/tests/xfs/593.out
new file mode 100644 (file)
index 0000000..bac4d7d
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 593
+Silence is golden
diff --git a/tests/xfs/594 b/tests/xfs/594
new file mode 100755 (executable)
index 0000000..323b191
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 594
+#
+# Race fsstress and symlink scrub for a while to see if we crash or livelock.
+# We can't open symlink files directly for scrubbing, so we use xfs_scrub(8).
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+XFS_SCRUB_PHASE=3 _scratch_xfs_stress_scrub -x 'symlink' -S '-n'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/594.out b/tests/xfs/594.out
new file mode 100644 (file)
index 0000000..2f67d68
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 594
+Silence is golden
diff --git a/tests/xfs/595 b/tests/xfs/595
new file mode 100755 (executable)
index 0000000..fc2a89e
--- /dev/null
@@ -0,0 +1,39 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 595
+#
+# Race fsstress and special file scrub for a while to see if we crash or
+# livelock.  We can't open special files directly for scrubbing, so we use
+# xfs_scrub(8).
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+XFS_SCRUB_PHASE=3 _scratch_xfs_stress_scrub -x 'mknod' -S '-n'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/595.out b/tests/xfs/595.out
new file mode 100644 (file)
index 0000000..6040b9a
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 595
+Silence is golden
diff --git a/tests/xfs/596 b/tests/xfs/596
new file mode 100755 (executable)
index 0000000..98659e8
--- /dev/null
@@ -0,0 +1,79 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 596
+#
+# growfs QA tests - repeatedly fill/grow the rt volume of the filesystem check
+# the filesystem contents after each operation.  This is the rt equivalent of
+# xfs/041.
+#
+. ./common/preamble
+_begin_fstest growfs ioctl auto
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       _scratch_unmount
+       rm -f $tmp.*
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_supported_fs xfs
+
+_require_scratch
+_require_realtime
+_require_no_large_scratch_dev
+_scratch_unmount 2>/dev/null
+
+_fill()
+{
+    if [ $# -ne 1 ]; then echo "Usage: _fill \"path\"" 1>&2 ; exit 1; fi
+    _do "Fill filesystem" \
+       "$here/src/fill2fs --verbose --dir=$1 --seed=0 --filesize=65536 --stddev=32768 --list=- >>$tmp.manifest"
+}
+
+_do_die_on_error=message_only
+rtsize=32
+echo -n "Make $rtsize megabyte rt filesystem on SCRATCH_DEV and mount... "
+_scratch_mkfs_xfs -rsize=${rtsize}m | _filter_mkfs 2> "$tmp.mkfs" >> $seqres.full
+test "${PIPESTATUS[0]}" -eq 0 || _fail "mkfs failed"
+
+. $tmp.mkfs
+onemeginblocks=`expr 1048576 / $dbsize`
+_scratch_mount
+
+# We're growing the realtime device, so force new file creation there
+_xfs_force_bdev realtime $SCRATCH_MNT
+
+echo "done"
+
+# full allocation group -> partial; partial -> expand partial + new partial;
+# partial -> expand partial; partial -> full
+# cycle through 33m -> 67m -> 75m -> 96m
+for size in 33 67 75 96
+do
+    grow_size=`expr $size \* $onemeginblocks`
+    _fill $SCRATCH_MNT/fill_$size
+    _do "Grow filesystem to ${size}m" "xfs_growfs -R $grow_size $SCRATCH_MNT"
+    echo -n "Flush filesystem... "
+    _do "_scratch_unmount"
+    _do "_try_scratch_mount"
+    echo "done"
+    echo -n "Check files... "
+    if ! _do "$here/src/fill2fs_check $tmp.manifest"; then
+      echo "fail (see $seqres.full)"
+      _do "cat $tmp.manifest"
+      _do "ls -altrR $SCRATCH_MNT"
+      status=1 ; exit
+    fi
+    echo "done"
+done
+
+# success, all done
+echo "Growfs tests passed."
+status=0 ; exit
diff --git a/tests/xfs/596.out b/tests/xfs/596.out
new file mode 100644 (file)
index 0000000..01480c2
--- /dev/null
@@ -0,0 +1,19 @@
+QA output created by 596
+Make 32 megabyte rt filesystem on SCRATCH_DEV and mount... done
+Fill filesystem... done
+Grow filesystem to 33m... done
+Flush filesystem... done
+Check files... done
+Fill filesystem... done
+Grow filesystem to 67m... done
+Flush filesystem... done
+Check files... done
+Fill filesystem... done
+Grow filesystem to 75m... done
+Flush filesystem... done
+Check files... done
+Fill filesystem... done
+Grow filesystem to 96m... done
+Flush filesystem... done
+Check files... done
+Growfs tests passed.
diff --git a/tests/xfs/597 b/tests/xfs/597
new file mode 100755 (executable)
index 0000000..3536372
--- /dev/null
@@ -0,0 +1,56 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 597
+#
+# Make sure that the kernel and userspace agree on which byte sequences are
+# ASCII uppercase letters, and how to convert them.
+#
+. ./common/preamble
+_begin_fstest auto ci dir
+
+# Import common functions.
+. ./common/filter
+
+_fixed_by_kernel_commit a9248538facc \
+       "xfs: stabilize the dirent name transformation function used for ascii-ci dir hash computation"
+_fixed_by_kernel_commit 9dceccc5822f \
+       "xfs: use the directory name hash function for dir scrubbing"
+
+_supported_fs xfs
+_require_scratch
+_require_xfs_mkfs_ciname
+
+_scratch_mkfs -n version=ci > $seqres.full
+_scratch_mount
+
+# Create a two-block directory to force leaf format
+mkdir "$SCRATCH_MNT/lol"
+touch "$SCRATCH_MNT/lol/autoexec.bat"
+i=0
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
+nr_dirents=$((dblksz * 2 / 256))
+
+for ((i = 0; i < nr_dirents; i++)); do
+       name="$(printf "y%0254d" $i)"
+       ln "$SCRATCH_MNT/lol/autoexec.bat" "$SCRATCH_MNT/lol/$name"
+done
+
+dirsz=$(stat -c '%s' $SCRATCH_MNT/lol)
+test $dirsz -gt $dblksz || echo "dir size $dirsz, expected at least $dblksz?"
+stat $SCRATCH_MNT/lol >> $seqres.full
+
+# Create names with extended ascii characters in them to exploit the fact
+# that the Linux kernel will transform extended ASCII uppercase characters
+# but libc won't.  Need to force LANG=C here so that awk doesn't spit out utf8
+# sequences.
+test "$LANG" = "C" || _notrun "LANG=C required"
+awk 'END { for (i = 192; i < 247; i++) printf("%c\n", i); }' < /dev/null | while read name; do
+       ln "$SCRATCH_MNT/lol/autoexec.bat" "$SCRATCH_MNT/lol/$name" 2>&1 | _filter_scratch
+done
+
+# Now just let repair run
+
+status=0
+exit
diff --git a/tests/xfs/597.out b/tests/xfs/597.out
new file mode 100644 (file)
index 0000000..a4fd480
--- /dev/null
@@ -0,0 +1,24 @@
+QA output created by 597
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\340': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\341': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\342': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\343': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\344': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\345': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\346': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\347': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\350': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\351': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\352': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\353': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\354': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\355': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\356': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\357': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\360': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\361': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\362': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\363': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\364': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\365': File exists
+ln: failed to create hard link 'SCRATCH_MNT/lol/'$'\366': File exists
diff --git a/tests/xfs/598 b/tests/xfs/598
new file mode 100755 (executable)
index 0000000..760cd86
--- /dev/null
@@ -0,0 +1,106 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 598
+#
+# Make sure that metadump obfuscation works for filesystems with ascii-ci
+# enabled.
+#
+. ./common/preamble
+_begin_fstest auto dir ci
+
+_cleanup()
+{
+      cd /
+      rm -r -f $tmp.* $testdir
+}
+
+_fixed_by_git_commit xfsprogs 10a01bcd \
+       "xfs_db: fix metadump name obfuscation for ascii-ci filesystems"
+
+_fixed_by_kernel_commit a9248538facc \
+        "xfs: stabilize the dirent name transformation function used for ascii-ci dir hash computation"
+_fixed_by_kernel_commit 9dceccc5822f \
+        "xfs: use the directory name hash function for dir scrubbing"
+
+_supported_fs xfs
+_require_test
+_require_scratch
+_require_xfs_mkfs_ciname
+
+_scratch_mkfs -n version=ci > $seqres.full
+_scratch_mount
+
+# Create a two-block directory to force leaf format
+mkdir "$SCRATCH_MNT/lol"
+touch "$SCRATCH_MNT/lol/autoexec.bat"
+i=0
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
+nr_dirents=$((dblksz * 2 / 256))
+
+for ((i = 0; i < nr_dirents; i++)); do
+       name="$(printf "y%0254d" $i)"
+       ln "$SCRATCH_MNT/lol/autoexec.bat" "$SCRATCH_MNT/lol/$name"
+done
+
+dirsz=$(stat -c '%s' $SCRATCH_MNT/lol)
+test $dirsz -gt $dblksz || echo "dir size $dirsz, expected at least $dblksz?"
+stat $SCRATCH_MNT/lol >> $seqres.full
+
+# Create a two-block attr to force leaf format
+i=0
+for ((i = 0; i < nr_dirents; i++)); do
+       name="$(printf "user.%0250d" $i)"
+       $SETFATTR_PROG -n "$name" -v 1 "$SCRATCH_MNT/lol/autoexec.bat"
+done
+stat $SCRATCH_MNT/lol/autoexec.bat >> $seqres.full
+
+_scratch_unmount
+
+testdir=$TEST_DIR/$seq.metadumps
+mkdir -p $testdir
+metadump_file=$testdir/scratch.md
+metadump_file_a=${metadump_file}.a
+metadump_file_o=${metadump_file}.o
+metadump_file_ao=${metadump_file}.ao
+
+echo metadump
+_scratch_xfs_metadump $metadump_file >> $seqres.full
+
+echo metadump a
+_scratch_xfs_metadump $metadump_file_a -a >> $seqres.full
+
+echo metadump o
+_scratch_xfs_metadump $metadump_file_o -o >> $seqres.full
+
+echo metadump ao
+_scratch_xfs_metadump $metadump_file_ao -a -o >> $seqres.full
+
+echo mdrestore
+_scratch_xfs_mdrestore $metadump_file
+_scratch_mount
+_check_scratch_fs
+_scratch_unmount
+
+echo mdrestore a
+_scratch_xfs_mdrestore $metadump_file_a
+_scratch_mount
+_check_scratch_fs
+_scratch_unmount
+
+echo mdrestore o
+_scratch_xfs_mdrestore $metadump_file_o
+_scratch_mount
+_check_scratch_fs
+_scratch_unmount
+
+echo mdrestore ao
+_scratch_xfs_mdrestore $metadump_file_ao
+_scratch_mount
+_check_scratch_fs
+_scratch_unmount
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/598.out b/tests/xfs/598.out
new file mode 100644 (file)
index 0000000..7f5fc2f
--- /dev/null
@@ -0,0 +1,9 @@
+QA output created by 598
+metadump
+metadump a
+metadump o
+metadump ao
+mdrestore
+mdrestore a
+mdrestore o
+mdrestore ao
diff --git a/tests/xfs/599 b/tests/xfs/599
new file mode 100755 (executable)
index 0000000..57a797f
--- /dev/null
@@ -0,0 +1,90 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 599
+#
+# Make sure that the kernel and utilities can handle large numbers of dirhash
+# collisions in both the directory and extended attribute structures.
+#
+# This started as a regression test for the new 'hashcoll' function in xfs_db,
+# but became a regression test for an xfs_repair bug affecting hashval checks
+# applied to the second and higher node levels of a dabtree.
+#
+. ./common/preamble
+_begin_fstest auto dir
+
+_fixed_by_git_commit xfsprogs b7b81f336ac \
+       "xfs_repair: fix incorrect dabtree hashval comparison"
+
+_supported_fs xfs
+_require_xfs_db_command "hashcoll"
+_require_xfs_db_command "path"
+_require_scratch
+
+_scratch_mkfs > $seqres.full
+_scratch_mount
+
+crash_dir=$SCRATCH_MNT/lol/
+crash_attrs=$SCRATCH_MNT/hah
+
+mkdir -p "$crash_dir"
+touch "$crash_attrs"
+
+# Create enough dirents to fill two dabtree node blocks with names that all
+# hash to the same value.  Each dirent gets its own record in the dabtree,
+# so we must create enough dirents to get a dabtree of at least height 2.
+dblksz=$(_xfs_get_dir_blocksize "$SCRATCH_MNT")
+
+da_records_per_block=$((dblksz / 8))   # 32-bit hash and 32-bit before
+nr_dirents=$((da_records_per_block * 2))
+
+longname="$(mktemp --dry-run "$(perl -e 'print "X" x 255;')" | tr ' ' 'X')"
+echo "creating $nr_dirents dirents from '$longname'" >> $seqres.full
+_scratch_xfs_db -r -c "hashcoll -n $nr_dirents -p $crash_dir $longname"
+
+# Create enough xattrs to fill two dabtree nodes.  Each attribute entry gets
+# its own record in the dabtree, so we have to create enough attributes to get
+# a dabtree of at least height 2.
+blksz=$(_get_block_size "$SCRATCH_MNT")
+
+da_records_per_block=$((blksz / 8))    # 32-bit hash and 32-bit before
+nr_attrs=$((da_records_per_block * 2))
+
+longname="$(mktemp --dry-run "$(perl -e 'print "X" x 249;')" | tr ' ' 'X')"
+echo "creating $nr_attrs attrs from '$longname'" >> $seqres.full
+_scratch_xfs_db -r -c "hashcoll -a -n $nr_attrs -p $crash_attrs $longname"
+
+_scratch_unmount
+
+# Make sure that there's one hash value dominating the dabtree block.
+# We don't require 100% because directories create dabtree records for dot
+# and dotdot.
+filter_hashvals() {
+       uniq -c | awk -v seqres_full="$seqres.full" \
+               '{print $0 >> seqres_full; tot += $1; if ($1 > biggest) biggest = $1;} END {if (biggest >= (tot - 2)) exit(0); exit(1);}'
+       test "${PIPESTATUS[1]}" -eq 0 || \
+               echo "Scattered dabtree hashes?  See seqres.full"
+}
+
+# Did we actually get a two-level dabtree for the directory?  Does it contain a
+# long run of hashes?
+echo "dir check" >> $seqres.full
+da_node_block_offset=$(( (2 ** 35) / blksz ))
+dir_db_args=(-c 'path /lol/' -c "dblock $da_node_block_offset" -c 'addr nbtree[0].before')
+dir_count="$(_scratch_xfs_db "${dir_db_args[@]}" -c 'print lhdr.count' | awk '{print $3}')"
+_scratch_xfs_db "${dir_db_args[@]}" -c "print lents[0-$((dir_count - 1))].hashval" | sed -e 's/lents\[[0-9]*\]/lents[NN]/g' | filter_hashvals
+
+# Did we actually get a two-level dabtree for the attrs?  Does it contain a
+# long run of hashes?
+echo "attr check" >> $seqres.full
+attr_db_args=(-c 'path /hah' -c "ablock 0" -c 'addr btree[0].before')
+attr_count="$(_scratch_xfs_db "${attr_db_args[@]}" -c 'print hdr.count' | awk '{print $3}')"
+_scratch_xfs_db "${attr_db_args[@]}" -c "print btree[0-$((attr_count - 1))].hashval" | sed -e 's/btree\[[0-9]*\]/btree[NN]/g' | filter_hashvals
+
+# Remount to get some coverage of xfs_scrub before seeing if xfs_repair
+# will trip over the large dabtrees.
+echo Silence is golden
+_scratch_mount
+status=0
+exit
diff --git a/tests/xfs/599.out b/tests/xfs/599.out
new file mode 100644 (file)
index 0000000..71a9cc5
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 599
+Silence is golden
diff --git a/tests/xfs/600 b/tests/xfs/600
new file mode 100755 (executable)
index 0000000..e6997c5
--- /dev/null
@@ -0,0 +1,55 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 600
+#
+# Regression test for an agbno overflow bug in XFS GETFSMAP involving an
+# fsmap_advance call.  Userspace can indicate that a GETFSMAP call is actually
+# a continuation of a previous call by setting the "low" key to the last record
+# returned by the previous call.
+#
+# If the last record returned by GETFSMAP is a non-shareable extent at the end
+# of an AG and the AG size is exactly a power of two, the startblock in the low
+# key of the rmapbt query can be set to a value larger than EOAG.  When this
+# happens, GETFSMAP will return EINVAL instead of returning records for the
+# next AG.
+#
+. ./common/preamble
+_begin_fstest auto quick fsmap
+
+. ./common/filter
+
+_fixed_by_git_commit kernel cfa2df68b7ce \
+       "xfs: fix an agbno overflow in __xfs_getfsmap_datadev"
+
+# Modify as appropriate.
+_supported_fs generic
+_require_xfs_io_command fsmap
+_require_xfs_scratch_rmapbt
+
+_scratch_mkfs | _filter_mkfs 2> $tmp.mkfs >> $seqres.full
+source $tmp.mkfs
+
+# Find the next power of two agsize smaller than whatever the default is.
+for ((p = 31; p > 0; p--)); do
+       desired_agsize=$((2 ** p))
+       test "$desired_agsize" -lt "$agsize" && break
+done
+
+echo "desired asize=$desired_agsize" >> $seqres.full
+_scratch_mkfs -d "agsize=${desired_agsize}b" | _filter_mkfs 2> $tmp.mkfs >> $seqres.full
+source $tmp.mkfs
+
+test "$desired_agsize" -eq "$agsize" || _notrun "wanted agsize=$desired_agsize, got $agsize"
+
+_scratch_mount
+$XFS_IO_PROG -c 'fsmap -n 1024 -v' $SCRATCH_MNT >> $tmp.big
+$XFS_IO_PROG -c 'fsmap -n 1 -v' $SCRATCH_MNT >> $tmp.small
+
+diff -Naurpw $tmp.big $tmp.small
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/600.out b/tests/xfs/600.out
new file mode 100644 (file)
index 0000000..f1a959c
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 600
+Silence is golden
diff --git a/tests/xfs/601 b/tests/xfs/601
new file mode 100755 (executable)
index 0000000..e1e94ca
--- /dev/null
@@ -0,0 +1,54 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019 Oracle, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 601
+#
+# Populate a XFS filesystem and ensure that xfs_copy works properly.
+#
+. ./common/preamble
+_begin_fstest auto copy
+
+_register_cleanup "_cleanup" BUS
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -rf $tmp.* $testdir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+
+testdir=$TEST_DIR/test-$seq
+
+# real QA test starts here
+_supported_fs xfs
+
+_require_xfs_copy
+_require_scratch_nocheck
+_require_populate_commands
+_xfs_skip_online_rebuild
+_xfs_skip_offline_rebuild
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+mkdir -p $testdir
+copy_file=$testdir/copy.img
+
+echo copy
+$XFS_COPY_PROG $SCRATCH_DEV $copy_file >> $seqres.full
+_check_scratch_fs $copy_file
+
+echo recopy
+$XFS_COPY_PROG $copy_file $SCRATCH_DEV >> $seqres.full
+_scratch_mount
+_check_scratch_fs
+_scratch_unmount
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/601.out b/tests/xfs/601.out
new file mode 100755 (executable)
index 0000000..0cbe0c0
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 601
+Format and populate
+copy
+recopy
diff --git a/tests/xfs/602 b/tests/xfs/602
new file mode 100755 (executable)
index 0000000..3bc2484
--- /dev/null
@@ -0,0 +1,111 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 602
+#
+# Test using runtime code to fix unlinked inodes on a clean filesystem that
+# never got cleaned up.
+#
+. ./common/preamble
+_begin_fstest auto quick unlink
+
+. ./common/filter
+. ./common/fuzzy
+. ./common/quota
+
+# real QA test starts here
+
+_supported_fs xfs
+_require_xfs_db_command iunlink
+_require_scratch_nocheck       # we'll run repair ourselves
+
+# From the AGI definition
+XFS_AGI_UNLINKED_BUCKETS=64
+
+# Try to make each iunlink bucket have this many inodes in it.
+IUNLINK_BUCKETLEN=5
+
+# Disable quota since quotacheck will break this test
+orig_mount_options="$MOUNT_OPTIONS"
+_qmount_option 'noquota'
+
+format_scratch() {
+       _scratch_mkfs -d agcount=1 | _filter_mkfs 2> "${tmp}.mkfs" >> $seqres.full
+       source "${tmp}.mkfs"
+       test "${agcount}" -eq 1 || _notrun "test requires 1 AG for error injection"
+
+       local nr_iunlinks="$((IUNLINK_BUCKETLEN * XFS_AGI_UNLINKED_BUCKETS))"
+       readarray -t BADINODES < <(_scratch_xfs_db -x -c "iunlink -n $nr_iunlinks" | awk '{print $4}')
+}
+
+__repair_check_scratch() {
+       _scratch_xfs_repair -o force_geometry -n 2>&1 | \
+               tee -a $seqres.full | \
+               grep -E '(disconnected inode.*would move|next_unlinked in inode|unlinked bucket.*is.*in ag)'
+       return "${PIPESTATUS[0]}"
+}
+
+exercise_scratch() {
+       # Create a bunch of files...
+       declare -A inums
+       for ((i = 0; i < (XFS_AGI_UNLINKED_BUCKETS * 2); i++)); do
+               touch "${SCRATCH_MNT}/${i}" || break
+               inums["${i}"]="$(stat -c %i "${SCRATCH_MNT}/${i}")"
+       done
+
+       # ...then delete them to exercise the unlinked buckets
+       for ((i = 0; i < (XFS_AGI_UNLINKED_BUCKETS * 2); i++)); do
+               if ! rm -f "${SCRATCH_MNT}/${i}"; then
+                       echo "rm failed on inum ${inums[$i]}"
+                       break
+               fi
+       done
+}
+
+# Offline repair should not find anything
+final_check_scratch() {
+       __repair_check_scratch
+       res=$?
+       if [ $res -eq 2 ]; then
+               echo "scratch fs went offline?"
+               _scratch_mount
+               _scratch_unmount
+               __repair_check_scratch
+       fi
+       test "$res" -ne 0 && echo "repair returned $res?"
+}
+
+echo "+ Part 0: See if runtime can recover the unlinked list" | tee -a $seqres.full
+format_scratch
+_kernlog "part 0"
+_scratch_mount
+exercise_scratch
+_scratch_unmount
+final_check_scratch
+
+echo "+ Part 1: See if bulkstat can recover the unlinked list" | tee -a $seqres.full
+format_scratch
+_kernlog "part 1"
+_scratch_mount
+$XFS_IO_PROG -c 'bulkstat' $SCRATCH_MNT > /dev/null
+exercise_scratch
+_scratch_unmount
+final_check_scratch
+
+echo "+ Part 2: See if quotacheck can recover the unlinked list" | tee -a $seqres.full
+if [ -f /proc/fs/xfs/xqmstat ]; then
+       MOUNT_OPTIONS="$orig_mount_options"
+       _qmount_option 'quota'
+       format_scratch
+       _kernlog "part 2"
+       _scratch_mount
+       exercise_scratch
+       _scratch_unmount
+       final_check_scratch
+fi
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/602.out b/tests/xfs/602.out
new file mode 100644 (file)
index 0000000..086c671
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 602
++ Part 0: See if runtime can recover the unlinked list
++ Part 1: See if bulkstat can recover the unlinked list
++ Part 2: See if quotacheck can recover the unlinked list
+Silence is golden
diff --git a/tests/xfs/603 b/tests/xfs/603
new file mode 100755 (executable)
index 0000000..444ebf4
--- /dev/null
@@ -0,0 +1,215 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 603
+#
+# Functional test of using online repair to fix unlinked inodes on a clean
+# filesystem that never got cleaned up.
+#
+. ./common/preamble
+_begin_fstest auto online_repair
+
+. ./common/filter
+. ./common/fuzzy
+. ./common/quota
+
+# real QA test starts here
+
+_supported_fs xfs
+_require_xfs_db_command iunlink
+# The iunlink bucket repair code wasn't added to the AGI repair code
+# until after the directory repair code was merged
+_require_xfs_io_command repair -R directory
+_require_scratch_nocheck       # repair doesn't like single-AG fs
+
+# From the AGI definition
+XFS_AGI_UNLINKED_BUCKETS=64
+
+# Try to make each iunlink bucket have this many inodes in it.
+IUNLINK_BUCKETLEN=5
+
+# Disable quota since quotacheck will break this test
+_qmount_option 'noquota'
+
+format_scratch() {
+       _scratch_mkfs -d agcount=1 | _filter_mkfs 2> "${tmp}.mkfs" >> $seqres.full
+       source "${tmp}.mkfs"
+       test "${agcount}" -eq 1 || _notrun "test requires 1 AG for error injection"
+
+       local nr_iunlinks="$((IUNLINK_BUCKETLEN * XFS_AGI_UNLINKED_BUCKETS))"
+       readarray -t BADINODES < <(_scratch_xfs_db -x -c "iunlink -n $nr_iunlinks" | awk '{print $4}')
+}
+
+__repair_check_scratch() {
+       _scratch_xfs_repair -o force_geometry -n 2>&1 | \
+               tee -a $seqres.full | \
+               grep -E '(disconnected inode.*would move|next_unlinked in inode|unlinked bucket.*is.*in ag)'
+       return "${PIPESTATUS[0]}"
+}
+
+corrupt_scratch() {
+       # How far into the iunlink bucket chain do we target inodes for corruption?
+       # 1 = target the inode pointed to by the AGI
+       # 3 = middle of bucket list
+       # 5 = last element in bucket
+       local corruption_bucket_depth="$1"
+       if ((corruption_bucket_depth < 1 || corruption_bucket_depth > IUNLINK_BUCKETLEN)); then
+               echo "${corruption_bucket_depth}: Value must be between 1 and ${IUNLINK_BUCKETLEN}."
+               return 1
+       fi
+
+       # Index of the inode numbers within BADINODES
+       local bad_ino1_idx=$(( (IUNLINK_BUCKETLEN - corruption_bucket_depth) * XFS_AGI_UNLINKED_BUCKETS))
+       local bad_ino2_idx=$((bad_ino1_idx + 1))
+
+       # Inode numbers to target
+       local bad_ino1="${BADINODES[bad_ino1_idx]}"
+       local bad_ino2="${BADINODES[bad_ino2_idx]}"
+       printf "bad: 0x%x 0x%x\n" "${bad_ino1}" "${bad_ino2}" | _tee_kernlog >> $seqres.full
+
+       # Bucket within AGI 0's iunlinked array.
+       local ino1_bucket="$((bad_ino1 % XFS_AGI_UNLINKED_BUCKETS))"
+       local ino2_bucket="$((bad_ino2 % XFS_AGI_UNLINKED_BUCKETS))"
+
+       # The first bad inode stays on the unlinked list but gets a nonzero
+       # nlink; the second bad inode is removed from the unlinked list but
+       # keeps its zero nlink
+       _scratch_xfs_db -x \
+               -c "inode ${bad_ino1}" -c "write -d core.nlinkv2 5555" \
+               -c "agi 0" -c "fuzz -d unlinked[${ino2_bucket}] ones" -c "print unlinked" >> $seqres.full
+
+       local iwatch=()
+       local idx
+
+       # Make a list of the adjacent iunlink bucket inodes for the first inode
+       # that we targeted.
+       if [ "${corruption_bucket_depth}" -gt 1 ]; then
+               # Previous ino in bucket
+               idx=$(( (IUNLINK_BUCKETLEN - corruption_bucket_depth + 1) * XFS_AGI_UNLINKED_BUCKETS))
+               iwatch+=("${BADINODES[idx]}")
+       fi
+       iwatch+=("${bad_ino1}")
+       if [ "$((corruption_bucket_depth + 1))" -lt "${IUNLINK_BUCKETLEN}" ]; then
+               # Next ino in bucket
+               idx=$(( (IUNLINK_BUCKETLEN - corruption_bucket_depth - 1) * XFS_AGI_UNLINKED_BUCKETS))
+               iwatch+=("${BADINODES[idx]}")
+       fi
+
+       # Make a list of the adjacent iunlink bucket inodes for the second
+       # inode that we targeted.
+       if [ "${corruption_bucket_depth}" -gt 1 ]; then
+               # Previous ino in bucket
+               idx=$(( (IUNLINK_BUCKETLEN - corruption_bucket_depth + 1) * XFS_AGI_UNLINKED_BUCKETS))
+               iwatch+=("${BADINODES[idx + 1]}")
+       fi
+       iwatch+=("${bad_ino2}")
+       if [ "$((corruption_bucket_depth + 1))" -lt "${IUNLINK_BUCKETLEN}" ]; then
+               # Next ino in bucket
+               idx=$(( (IUNLINK_BUCKETLEN - corruption_bucket_depth - 1) * XFS_AGI_UNLINKED_BUCKETS))
+               iwatch+=("${BADINODES[idx + 1]}")
+       fi
+
+       # Construct a grep string for tracepoints.
+       GREP_STR="(xrep_attempt|xrep_done|bucket ${ino1_bucket} |bucket ${ino2_bucket} |bucket ${fuzz_bucket} "
+       GREP_STR="(xrep_attempt|xrep_done|bucket ${ino1_bucket} |bucket ${ino2_bucket} "
+       for ino in "${iwatch[@]}"; do
+               f="$(printf "|ino 0x%x" "${ino}")"
+               GREP_STR="${GREP_STR}${f}"
+       done
+       GREP_STR="${GREP_STR})"
+       echo "grep -E \"${GREP_STR}\"" >> $seqres.full
+
+       # Dump everything we did to to the full file.
+       local db_dump=(-c 'agi 0' -c 'print unlinked')
+       db_dump+=(-c 'addr root' -c 'print')
+       test "${ino1_bucket}" -gt 0 && \
+               db_dump+=(-c "dump_iunlinked -a 0 -b $((ino1_bucket - 1))")
+       db_dump+=(-c "dump_iunlinked -a 0 -b ${ino1_bucket}")
+       db_dump+=(-c "dump_iunlinked -a 0 -b ${ino2_bucket}")
+       test "${ino2_bucket}" -lt 63 && \
+               db_dump+=(-c "dump_iunlinked -a 0 -b $((ino2_bucket + 1))")
+       db_dump+=(-c "inode $bad_ino1" -c 'print core.nlinkv2 v3.inumber next_unlinked')
+       db_dump+=(-c "inode $bad_ino2" -c 'print core.nlinkv2 v3.inumber next_unlinked')
+       _scratch_xfs_db "${db_dump[@]}" >> $seqres.full
+
+       # Test run of repair to make sure we find disconnected inodes
+       __repair_check_scratch | \
+               sed -e 's/disconnected inode \([0-9]*\)/disconnected inode XXXXXX/g' \
+                   -e 's/next_unlinked in inode \([0-9]*\)/next_unlinked in inode XXXXXX/g' \
+                   -e 's/unlinked bucket \([0-9]*\) is \([0-9]*\) in ag \([0-9]*\) .inode=\([0-9]*\)/unlinked bucket YY is XXXXXX in ag Z (inode=AAAAAA/g' | \
+               uniq -c >> $seqres.full
+       res=${PIPESTATUS[0]}
+       test "$res" -ne 0 || echo "repair returned $res after corruption?"
+}
+
+exercise_scratch() {
+       # Create a bunch of files...
+       declare -A inums
+       for ((i = 0; i < (XFS_AGI_UNLINKED_BUCKETS * 2); i++)); do
+               touch "${SCRATCH_MNT}/${i}" || break
+               inums["${i}"]="$(stat -c %i "${SCRATCH_MNT}/${i}")"
+       done
+
+       # ...then delete them to exercise the unlinked buckets
+       for ((i = 0; i < (XFS_AGI_UNLINKED_BUCKETS * 2); i++)); do
+               if ! rm -f "${SCRATCH_MNT}/${i}"; then
+                       echo "rm failed on inum ${inums[$i]}"
+                       break
+               fi
+       done
+}
+
+# Offline repair should not find anything
+final_check_scratch() {
+       __repair_check_scratch
+       res=$?
+       if [ $res -eq 2 ]; then
+               echo "scratch fs went offline?"
+               _scratch_mount
+               _scratch_unmount
+               __repair_check_scratch
+       fi
+       test "$res" -ne 0 && echo "repair returned $res?"
+}
+
+echo "+ Part 1: See if scrub can recover the unlinked list" | tee -a $seqres.full
+format_scratch
+_kernlog "no bad inodes"
+_scratch_mount
+_scratch_scrub >> $seqres.full
+exercise_scratch
+_scratch_unmount
+final_check_scratch
+
+echo "+ Part 2: Corrupt the first inode in the bucket" | tee -a $seqres.full
+format_scratch
+corrupt_scratch 1
+_scratch_mount
+_scratch_scrub >> $seqres.full
+exercise_scratch
+_scratch_unmount
+final_check_scratch
+
+echo "+ Part 3: Corrupt the middle inode in the bucket" | tee -a $seqres.full
+format_scratch
+corrupt_scratch 3
+_scratch_mount
+_scratch_scrub >> $seqres.full
+exercise_scratch
+_scratch_unmount
+final_check_scratch
+
+echo "+ Part 4: Corrupt the last inode in the bucket" | tee -a $seqres.full
+format_scratch
+corrupt_scratch 5
+_scratch_mount
+_scratch_scrub >> $seqres.full
+exercise_scratch
+_scratch_unmount
+final_check_scratch
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/603.out b/tests/xfs/603.out
new file mode 100644 (file)
index 0000000..0c2f40b
--- /dev/null
@@ -0,0 +1,6 @@
+QA output created by 603
++ Part 1: See if scrub can recover the unlinked list
++ Part 2: Corrupt the first inode in the bucket
++ Part 3: Corrupt the middle inode in the bucket
++ Part 4: Corrupt the last inode in the bucket
+Silence is golden
diff --git a/tests/xfs/604 b/tests/xfs/604
new file mode 100755 (executable)
index 0000000..fdc444c
--- /dev/null
@@ -0,0 +1,72 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+#
+# FS QA Test No. 604
+#
+# Regression test for patch "xfs: fix internal error from AGFL exhaustion".
+#
+. ./common/preamble
+_begin_fstest auto prealloc punch
+
+. ./common/filter
+
+_supported_fs xfs
+_require_scratch
+_require_xfs_io_command "falloc"
+_require_xfs_io_command "fpunch"
+_require_test_program punch-alternating
+_fixed_by_kernel_commit f63a5b3769ad "xfs: fix internal error from AGFL exhaustion"
+
+# Disable the rmapbt so we only need to worry about splitting the bnobt and
+# cntbt at the same time.
+opts=
+if $MKFS_XFS_PROG |& grep -q rmapbt; then
+       opts="-m rmapbt=0"
+fi
+_scratch_mkfs $opts | _filter_mkfs > /dev/null 2> "$tmp.mkfs"
+. "$tmp.mkfs"
+_scratch_mount
+
+alloc_block_len=$((_fs_has_crcs ? 56 : 16))
+allocbt_leaf_maxrecs=$(((dbsize - alloc_block_len) / 8))
+allocbt_node_maxrecs=$(((dbsize - alloc_block_len) / 12))
+
+# Create a big file with a size such that the punches below create the exact
+# free extents we want.
+num_holes=$((allocbt_leaf_maxrecs * allocbt_node_maxrecs - 1))
+falloc_size=$((9 * dbsize + num_holes * dbsize * 2))
+$XFS_IO_PROG -c "falloc 0 $falloc_size" -f "$SCRATCH_MNT/big" ||
+       _notrun "Not enough space on device for falloc_size=$(echo "scale=2; $falloc_size / 1073741824" | $BC -q)GB and bs=$dbsize"
+
+# Fill in any small free extents in AG 0. After this, there should be only one,
+# large free extent.
+_scratch_unmount
+mapfile -t gaps < <(_scratch_xfs_db -c 'agf 0' -c 'addr cntroot' -c btdump |
+       $SED_PROG -rn 's/^[0-9]+:\[[0-9]+,([0-9]+)\].*/\1/p' |
+       tac | tail -n +2)
+_scratch_mount
+for gap_i in "${!gaps[@]}"; do
+       gap=${gaps[$gap_i]}
+       $XFS_IO_PROG -c "falloc 0 $((gap * dbsize))" -f "$SCRATCH_MNT/gap$gap_i"
+done
+
+# Create enough free space records to make the bnobt and cntbt both full,
+# 2-level trees, plus one more record to make them split all the way to the
+# root and become 3-level trees. After this, there is a 7-block free extent in
+# the rightmost leaf of the cntbt, and all of the leaves of the cntbt other
+# than the rightmost two are full. Without the fix, the free list is also
+# empty.
+$XFS_IO_PROG -c "fpunch $dbsize $((7 * dbsize))" "$SCRATCH_MNT/big"
+"$here/src/punch-alternating" -o 9 "$SCRATCH_MNT/big"
+
+# Do an arbitrary operation that refills the free list. Without the fix, this
+# will allocate 6 blocks from the 7-block free extent in the rightmost leaf of
+# the cntbt, then try to insert the remaining 1 block free extent in the
+# leftmost leaf of the cntbt. But that leaf is full, so this tries to split the
+# leaf and fails because the free list is empty, returning EFSCORRUPTED.
+$XFS_IO_PROG -c "fpunch 0 $dbsize" "$SCRATCH_MNT/big"
+
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/xfs/604.out b/tests/xfs/604.out
new file mode 100644 (file)
index 0000000..c06a1c7
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 604
+Silence is golden
diff --git a/tests/xfs/605 b/tests/xfs/605
new file mode 100755 (executable)
index 0000000..78458c7
--- /dev/null
@@ -0,0 +1,94 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 605
+#
+# Test metadump/mdrestore's ability to dump a dirty log and restore it
+# correctly.
+#
+. ./common/preamble
+_begin_fstest auto quick metadump log logprint punch
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.*
+       _xfs_cleanup_verify_metadump
+}
+
+# Import common functions.
+. ./common/dmflakey
+. ./common/inject
+. ./common/metadump
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_test
+_require_loop
+_require_xfs_debug
+_require_xfs_io_error_injection log_item_pin
+_require_dm_target flakey
+_require_xfs_io_command "pwrite"
+_require_test_program "punch-alternating"
+_xfs_setup_verify_metadump
+
+testfile=${SCRATCH_MNT}/testfile
+
+echo "Format filesystem on scratch device"
+_scratch_mkfs >> $seqres.full 2>&1
+
+echo "Initialize and mount filesystem on flakey device"
+_init_flakey
+_load_flakey_table $FLAKEY_ALLOW_WRITES
+_mount_flakey
+
+echo "Create test file"
+$XFS_IO_PROG -s -f -c "pwrite 0 5M" $testfile >> $seqres.full
+
+echo "Punch alternative blocks of test file"
+$here/src/punch-alternating $testfile
+
+echo "Mount cycle the filesystem on flakey device"
+_unmount_flakey
+_mount_flakey
+
+device=$(readlink -f $FLAKEY_DEV)
+device=$(_short_dev $device)
+
+echo "Pin log items in the AIL"
+echo 1 > /sys/fs/xfs/${device}/errortag/log_item_pin
+
+echo "Create two checkpoint transactions on ondisk log"
+for ct in $(seq 1 2); do
+       offset=$($XFS_IO_PROG -c 'fiemap' $testfile | tac |  grep -v hole | \
+                        head -n 1 | awk -F '[\\[.]' '{ print $2 * 512; }')
+       $XFS_IO_PROG -c "truncate $offset" -c fsync $testfile
+done
+
+echo "Drop writes to filesystem from here onwards"
+_load_flakey_table $FLAKEY_DROP_WRITES
+
+echo "Unpin log items in AIL"
+echo 0 > /sys/fs/xfs/${device}/errortag/log_item_pin
+
+echo "Unmount filesystem on flakey device"
+_unmount_flakey
+
+echo "Clean up flakey device"
+_cleanup_flakey
+
+echo -n "Filesystem has a "
+_print_logstate
+
+echo "Create metadump file, restore it and check restored fs"
+_xfs_verify_metadumps '-a -o'
+
+# Mount the fs to replay the contents from the dirty log.
+_scratch_mount
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/605.out b/tests/xfs/605.out
new file mode 100644 (file)
index 0000000..7891302
--- /dev/null
@@ -0,0 +1,14 @@
+QA output created by 605
+Format filesystem on scratch device
+Initialize and mount filesystem on flakey device
+Create test file
+Punch alternative blocks of test file
+Mount cycle the filesystem on flakey device
+Pin log items in the AIL
+Create two checkpoint transactions on ondisk log
+Drop writes to filesystem from here onwards
+Unpin log items in AIL
+Unmount filesystem on flakey device
+Clean up flakey device
+Filesystem has a dirty log
+Create metadump file, restore it and check restored fs
diff --git a/tests/xfs/606 b/tests/xfs/606
new file mode 100755 (executable)
index 0000000..d52a93d
--- /dev/null
@@ -0,0 +1,56 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Red Hat, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 606
+#
+# Test xfs_growfs with "too-small" size expansion, which lead to a delta of "0"
+# in xfs_growfs_data_private. This's a regression test of 84712492e6da ("xfs:
+# short circuit xfs_growfs_data_private() if delta is zero").
+#
+. ./common/preamble
+_begin_fstest auto quick growfs
+
+_cleanup()
+{
+       local dev
+       $UMOUNT_PROG $LOOP_MNT 2>/dev/null
+       dev=$(losetup -j testfile | cut -d: -f1)
+       losetup -d $dev 2>/dev/null
+       rm -rf $LOOP_IMG $LOOP_MNT
+       cd /
+       rm -f $tmp.*
+}
+
+# real QA test starts here
+_supported_fs xfs
+_fixed_by_kernel_commit 84712492e6da \
+       "xfs: short circuit xfs_growfs_data_private() if delta is zero"
+_require_test
+_require_loop
+_require_xfs_io_command "truncate"
+_require_command "$XFS_GROWFS_PROG" xfs_growfs
+
+LOOP_IMG=$TEST_DIR/$seq.dev
+LOOP_MNT=$TEST_DIR/$seq.mnt
+rm -rf $LOOP_IMG $LOOP_MNT
+mkdir -p $LOOP_MNT
+
+# 1G image
+$XFS_IO_PROG -f -c "truncate 1073741824" $LOOP_IMG
+$MKFS_XFS_PROG -f $LOOP_IMG >$seqres.full
+# Extend by just 8K, expected to start with the last full-size AG ends of
+# above 1G block device.
+$XFS_IO_PROG -f -c "truncate 1073750016" $LOOP_IMG
+_mount -oloop $LOOP_IMG $LOOP_MNT
+# A known bug shows "XFS_IOC_FSGROWFSDATA xfsctl failed: No space left on
+# device" at here, refer to _fixed_by_kernel_commit above
+$XFS_GROWFS_PROG $LOOP_MNT >$seqres.full
+if [ $? -ne 0 ];then
+       echo "xfs_growfs fails!"
+fi
+
+echo "Silence is golden"
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/606.out b/tests/xfs/606.out
new file mode 100644 (file)
index 0000000..09bf888
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 606
+Silence is golden
diff --git a/tests/xfs/607 b/tests/xfs/607
new file mode 100755 (executable)
index 0000000..e1120ea
--- /dev/null
@@ -0,0 +1,86 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022-2024 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 607
+#
+# This is a regression test for "xfs: Fix false ENOSPC when performing direct
+# write on a delalloc extent in cow fork".  If there is a lot of free space but
+# it is very fragmented, it's possible that a very large delalloc reservation
+# could be created in the CoW fork by a buffered write.  If a directio write
+# tries to convert the delalloc reservation to a real extent, it's possible
+# that the allocation will succeed but fail to convert even the first block of
+# the directio write range.  In this case, XFS will return ENOSPC even though
+# all it needed to do was to keep converting until the allocator returns ENOSPC
+# or the first block of the direct write got some space.
+#
+. ./common/preamble
+_begin_fstest auto quick clone
+
+_cleanup()
+{
+       cd /
+       rm -f $file1 $file2 $fragmentedfile
+}
+
+# Import common functions.
+. ./common/reflink
+. ./common/inject
+
+# real QA test starts here
+_fixed_by_kernel_commit d62113303d69 \
+       "xfs: Fix false ENOSPC when performing direct write on a delalloc extent in cow fork"
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_test_program "punch-alternating"
+_require_test_reflink
+_require_xfs_io_error_injection "bmap_alloc_minlen_extent"
+_require_test_delalloc
+
+file1=$TEST_DIR/file1.$seq
+file2=$TEST_DIR/file2.$seq
+fragmentedfile=$TEST_DIR/fragmentedfile.$seq
+
+rm -f $file1 $file2 $fragmentedfile
+
+# COW operates on pages, so we must not perform operations in units smaller
+# than a page.
+blksz=$(_get_file_block_size $TEST_DIR)
+pagesz=$(_get_page_size)
+if (( $blksz < $pagesz )); then
+       blksz=$pagesz
+fi
+
+echo "Create source file"
+$XFS_IO_PROG -f -c "pwrite 0 $((blksz * 256))" $file1 >> $seqres.full
+
+sync
+
+echo "Create Reflinked file"
+_cp_reflink $file1 $file2 >> $seqres.full
+
+echo "Set cowextsize"
+$XFS_IO_PROG -c "cowextsize $((blksz * 128))" -c stat $file1 >> $seqres.full
+
+echo "Fragment FS"
+$XFS_IO_PROG -f -c "pwrite 0 $((blksz * 512))" $fragmentedfile >> $seqres.full
+sync
+$here/src/punch-alternating $fragmentedfile
+
+echo "Allocate block sized extent from now onwards"
+_test_inject_error bmap_alloc_minlen_extent 1
+
+echo "Create big delalloc extent in CoW fork"
+$XFS_IO_PROG -c "pwrite 0 $blksz" $file1 >> $seqres.full
+
+sync
+
+$XFS_IO_PROG -c 'bmap -elpv' -c 'bmap -celpv' $file1 &>> $seqres.full
+
+echo "Direct I/O write at offset 3FSB"
+$XFS_IO_PROG -d -c "pwrite $((blksz * 3)) $((blksz * 2))" $file1 >> $seqres.full
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/607.out b/tests/xfs/607.out
new file mode 100644 (file)
index 0000000..0fd9134
--- /dev/null
@@ -0,0 +1,8 @@
+QA output created by 607
+Create source file
+Create Reflinked file
+Set cowextsize
+Fragment FS
+Allocate block sized extent from now onwards
+Create big delalloc extent in CoW fork
+Direct I/O write at offset 3FSB
diff --git a/tests/xfs/708 b/tests/xfs/708
new file mode 100755 (executable)
index 0000000..76da633
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 708
+#
+# Race fsstress and bnobt repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair bnobt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/708.out b/tests/xfs/708.out
new file mode 100644 (file)
index 0000000..33c478a
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 708
+Silence is golden
diff --git a/tests/xfs/709 b/tests/xfs/709
new file mode 100755 (executable)
index 0000000..247d108
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 709
+#
+# Race fsstress and inobt repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair inobt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/709.out b/tests/xfs/709.out
new file mode 100644 (file)
index 0000000..94e83ac
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 709
+Silence is golden
diff --git a/tests/xfs/710 b/tests/xfs/710
new file mode 100755 (executable)
index 0000000..45a2a2a
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 710
+#
+# Race fsstress and refcountbt repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" reflink
+_scratch_xfs_stress_online_repair -s "repair refcountbt %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/710.out b/tests/xfs/710.out
new file mode 100644 (file)
index 0000000..f8de284
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 710
+Silence is golden
diff --git a/tests/xfs/711 b/tests/xfs/711
new file mode 100755 (executable)
index 0000000..0b1adb7
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 711
+#
+# Race fsstress and superblock repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -a 1 -s "repair sb %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/711.out b/tests/xfs/711.out
new file mode 100644 (file)
index 0000000..6c18e66
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 711
+Silence is golden
diff --git a/tests/xfs/712 b/tests/xfs/712
new file mode 100755 (executable)
index 0000000..222b2b2
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 712
+#
+# Race fsstress and agf repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair agf %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/712.out b/tests/xfs/712.out
new file mode 100644 (file)
index 0000000..a294469
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 712
+Silence is golden
diff --git a/tests/xfs/713 b/tests/xfs/713
new file mode 100755 (executable)
index 0000000..05b2a1a
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 713
+#
+# Race fsstress and agfl repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair agfl %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/713.out b/tests/xfs/713.out
new file mode 100644 (file)
index 0000000..d7fdb8e
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 713
+Silence is golden
diff --git a/tests/xfs/714 b/tests/xfs/714
new file mode 100755 (executable)
index 0000000..cd84821
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 714
+#
+# Race fsstress and agi repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair agi %agno%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/714.out b/tests/xfs/714.out
new file mode 100644 (file)
index 0000000..f5ce748
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 714
+Silence is golden
diff --git a/tests/xfs/715 b/tests/xfs/715
new file mode 100755 (executable)
index 0000000..09fa0eb
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 715
+#
+# Race fsstress and inode record repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair inode" -t "%file%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/715.out b/tests/xfs/715.out
new file mode 100644 (file)
index 0000000..b5947d8
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 715
+Silence is golden
diff --git a/tests/xfs/716 b/tests/xfs/716
new file mode 100755 (executable)
index 0000000..ef0af45
--- /dev/null
@@ -0,0 +1,85 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 716
+#
+# Make sure online repair can handle rebuilding xattrs when the data fork is
+# in btree format and we cannot just zap the attr fork.
+
+. ./common/preamble
+_begin_fstest auto quick online_repair
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+}
+
+# Import common functions.
+. ./common/inject
+. ./common/filter
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_io_error_injection "force_repair"
+_require_xfs_io_command "falloc"
+_require_xfs_io_command "repair"
+_require_test_program "punch-alternating"
+
+_scratch_mkfs > $tmp.mkfs
+_scratch_mount
+
+_require_scratch_xfs_scrub
+
+# Force data device extents so that we can create a file with the exact bmbt
+# that we need regardless of rt configuration.
+_xfs_force_bdev data $SCRATCH_MNT
+
+file=$SCRATCH_MNT/moofile
+touch $file
+
+# Create some xattrs so that we have to rebuild them.
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 76' $file.txt >> $seqres.full
+$SETFATTR_PROG -n user.SGI_BCL_FILE -v "$(cat $file.txt)" $file
+
+$SETFATTR_PROG -n user.crtime_usec -v 12345678 $file
+
+blksz=$(_get_file_block_size $SCRATCH_MNT)
+ino=$(stat -c '%i' $file)
+
+# Figure out how many extents we need to have to create a data fork that's in
+# btree format.
+umount $SCRATCH_MNT
+di_forkoff=$(_scratch_xfs_db -c "inode $ino" -c "p core.forkoff" | \
+       awk '{print $3}')
+_scratch_xfs_db -c "inode $ino" -c "p" >> $seqres.full
+_scratch_mount
+
+# Create a data fork in btree format
+min_ext_for_btree=$((di_forkoff * 8 / 16))
+$XFS_IO_PROG -c "falloc 0 $(( (min_ext_for_btree + 1) * 2 * blksz))" $file
+$here/src/punch-alternating $file
+
+# Make sure the data fork is in btree format.
+umount $SCRATCH_MNT
+_scratch_xfs_db -c "inode $ino" -c "p core.format" | grep -q "btree" || \
+       echo "data fork not in btree format?"
+echo "about to start test" >> $seqres.full
+_scratch_xfs_db -c "inode $ino" -c "p" >> $seqres.full
+_scratch_mount
+
+# Force repair the xattr fork
+_scratch_inject_error force_repair
+$XFS_IO_PROG -x -c 'repair xattr' $file 2>&1 | tee $tmp.repair.log
+grep -q 'Operation not supported' $tmp.repair.log && \
+       _notrun "online xattr repair not supported"
+
+# If online repair did it correctly, the filesystem won't be corrupt.  Let the
+# post-test check do its thing.
+
+# success, all done
+echo "Silence is golden."
+status=0
+exit
diff --git a/tests/xfs/716.out b/tests/xfs/716.out
new file mode 100644 (file)
index 0000000..5b8c574
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 716
+Silence is golden.
diff --git a/tests/xfs/717 b/tests/xfs/717
new file mode 100755 (executable)
index 0000000..ba00a71
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 717
+#
+# Race fsstress and data fork repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair bmapbtd" -t "%datafile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/717.out b/tests/xfs/717.out
new file mode 100644 (file)
index 0000000..383225a
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 717
+Silence is golden
diff --git a/tests/xfs/718 b/tests/xfs/718
new file mode 100755 (executable)
index 0000000..cc0efa7
--- /dev/null
@@ -0,0 +1,39 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 718
+#
+# Race fsstress and attr fork repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/attr
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_attrs
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -x 'xattr' -s "repair bmapbta" -t "%attrfile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/718.out b/tests/xfs/718.out
new file mode 100644 (file)
index 0000000..1dad5ab
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 718
+Silence is golden
diff --git a/tests/xfs/719 b/tests/xfs/719
new file mode 100755 (executable)
index 0000000..9e28958
--- /dev/null
@@ -0,0 +1,39 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 719
+#
+# Race fsstress and CoW fork repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/reflink
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" reflink
+_scratch_xfs_stress_online_repair -s "repair bmapbtc" -t "%cowfile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/719.out b/tests/xfs/719.out
new file mode 100644 (file)
index 0000000..25585fa
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 719
+Silence is golden
diff --git a/tests/xfs/720 b/tests/xfs/720
new file mode 100755 (executable)
index 0000000..2b6406d
--- /dev/null
@@ -0,0 +1,72 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 720
+#
+# Ensure that the sysadmin won't hit EDQUOT while repairing file data forks
+# even if the file's quota limits have been exceeded.  This tests the quota
+# reservation handling inside the bmap btree rebuilding code.
+#
+. ./common/preamble
+_begin_fstest online_repair
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/quota
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_xfs_io_command "falloc"
+_require_quota
+_require_user
+_require_test_program "punch-alternating"
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_qmount_option usrquota
+_qmount
+
+blocksize=$(_get_block_size $SCRATCH_MNT)
+alloc_unit=$(_get_file_block_size $SCRATCH_MNT)
+
+# Make sure we can actually repair a data fork
+scratchfile=$SCRATCH_MNT/file
+touch $scratchfile
+$XFS_IO_PROG -x -c 'inject force_repair' $SCRATCH_MNT
+__stress_scrub_check_commands "$scratchfile" "" 'repair bmapbtd'
+
+# Compute the number of extent records needed to guarantee btree format,
+# assuming 16 bytes for each ondisk extent record
+bmbt_records=$(( (blocksize / 16) * 5 / 4 ))
+total_size=$(( bmbt_records * 2 * alloc_unit ))
+
+# Create a file with a data fork in bmap btree format
+$XFS_IO_PROG -c "falloc 0 $total_size" $scratchfile >> $seqres.full
+$here/src/punch-alternating $scratchfile
+
+# Set a low quota hardlimit for an unprivileged uid and chown the file to it
+echo "set up quota" >> $seqres.full
+$XFS_QUOTA_PROG -x -c "limit -u bhard=$((alloc_unit * 2)) $qa_user" $SCRATCH_MNT
+chown $qa_user $scratchfile
+$XFS_QUOTA_PROG -x -c 'report -u' $SCRATCH_MNT >> $seqres.full
+
+# Rebuild the data fork
+echo "repairs" >> $seqres.full
+$XFS_IO_PROG -x -c 'inject force_repair' -c 'repair bmapbtd' $scratchfile
+$XFS_QUOTA_PROG -x -c 'report -u' $SCRATCH_MNT >> $seqres.full
+
+# Fail at appending the file as qa_user to ensure quota enforcement works
+echo "fail quota" >> $seqres.full
+su - "$qa_user" -c "$XFS_IO_PROG -c 'pwrite 10g 1' $scratchfile" >> $seqres.full
+$XFS_QUOTA_PROG -x -c 'report -u' $SCRATCH_MNT >> $seqres.full
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/720.out b/tests/xfs/720.out
new file mode 100644 (file)
index 0000000..47b41f6
--- /dev/null
@@ -0,0 +1,3 @@
+QA output created by 720
+pwrite: Disk quota exceeded
+Silence is golden
diff --git a/tests/xfs/721 b/tests/xfs/721
new file mode 100755 (executable)
index 0000000..e6ccc8b
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 721
+#
+# Race fsstress and symlink repair for a while to see if we crash or livelock.
+# We can't open special files directly for scrubbing, so we use xfs_scrub(8).
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+XFS_SCRUB_PHASE=3 _scratch_xfs_stress_online_repair -x 'symlink' -S '-k'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/721.out b/tests/xfs/721.out
new file mode 100644 (file)
index 0000000..093a84e
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 721
+Silence is golden
diff --git a/tests/xfs/722 b/tests/xfs/722
new file mode 100755 (executable)
index 0000000..f53d03b
--- /dev/null
@@ -0,0 +1,39 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 722
+#
+# Race fsstress and special file repair for a while to see if we crash or
+# livelock.  We can't open special files directly for scrubbing, so we use
+# xfs_scrub(8).
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+XFS_SCRUB_PHASE=3 _scratch_xfs_stress_online_repair -x 'mknod' -S '-k'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/722.out b/tests/xfs/722.out
new file mode 100644 (file)
index 0000000..869bd20
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 722
+Silence is golden
diff --git a/tests/xfs/723 b/tests/xfs/723
new file mode 100755 (executable)
index 0000000..b6af6ef
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 723
+#
+# Race fsstress and user quota repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" usrquota
+_scratch_xfs_stress_online_repair -s "repair usrquota"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/723.out b/tests/xfs/723.out
new file mode 100644 (file)
index 0000000..4036d5f
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 723
+Silence is golden
diff --git a/tests/xfs/724 b/tests/xfs/724
new file mode 100755 (executable)
index 0000000..2061b51
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 724
+#
+# Race fsstress and group quota repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" grpquota
+_scratch_xfs_stress_online_repair -s "repair grpquota"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/724.out b/tests/xfs/724.out
new file mode 100644 (file)
index 0000000..2d22107
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 724
+Silence is golden
diff --git a/tests/xfs/725 b/tests/xfs/725
new file mode 100755 (executable)
index 0000000..7317d04
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 725
+#
+# Race fsstress and project quota repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" prjquota
+_scratch_xfs_stress_online_repair -s "repair prjquota"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/725.out b/tests/xfs/725.out
new file mode 100644 (file)
index 0000000..128709e
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 725
+Silence is golden
diff --git a/tests/xfs/726 b/tests/xfs/726
new file mode 100755 (executable)
index 0000000..e823f99
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 726
+#
+# Race fsstress and quotacheck repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" any
+_scratch_xfs_stress_online_repair -s "repair quotacheck"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/726.out b/tests/xfs/726.out
new file mode 100644 (file)
index 0000000..4076706
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 726
+Silence is golden
diff --git a/tests/xfs/727 b/tests/xfs/727
new file mode 100755 (executable)
index 0000000..6c5ac7d
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 727
+#
+# Race fsstress and quotacheck scrub for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_quota_acct_enabled "$SCRATCH_DEV" any
+_scratch_xfs_stress_scrub -s "scrub quotacheck"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/727.out b/tests/xfs/727.out
new file mode 100644 (file)
index 0000000..2de2b4b
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 727
+Silence is golden
diff --git a/tests/xfs/728 b/tests/xfs/728
new file mode 100755 (executable)
index 0000000..17ce971
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 728
+#
+# Race fsstress and inode link count repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -x "dir" -s "repair nlinks"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/728.out b/tests/xfs/728.out
new file mode 100644 (file)
index 0000000..ab39f45
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 728
+Silence is golden
diff --git a/tests/xfs/729 b/tests/xfs/729
new file mode 100755 (executable)
index 0000000..235cb17
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 729
+#
+# Race fsstress and nlinks scrub for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -x "dir" -s "scrub nlinks"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/729.out b/tests/xfs/729.out
new file mode 100644 (file)
index 0000000..0f175ae
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 729
+Silence is golden
diff --git a/tests/xfs/730 b/tests/xfs/730
new file mode 100755 (executable)
index 0000000..c6817e9
--- /dev/null
@@ -0,0 +1,36 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 730
+#
+# Populate a XFS filesystem and fuzz every fscounter field.
+# Use xfs_scrub to fix the corruption.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz fscounters"
+test -z "$SCRATCH_XFS_LIST_METADATA_FIELDS" &&
+       SCRATCH_XFS_LIST_METADATA_FIELDS='icount,ifree,fdblocks'
+export SCRATCH_XFS_LIST_METADATA_FIELDS
+_scratch_xfs_fuzz_metadata '' 'online' 'sb 0' >> $seqres.full
+echo "Done fuzzing fscounters"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/730.out b/tests/xfs/730.out
new file mode 100644 (file)
index 0000000..28d4bec
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 730
+Format and populate
+Fuzz fscounters
+Done fuzzing fscounters
diff --git a/tests/xfs/731 b/tests/xfs/731
new file mode 100755 (executable)
index 0000000..595ab04
--- /dev/null
@@ -0,0 +1,41 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 731
+#
+# Race fsstress and fscounter repair for a while to see if we crash or livelock.
+# Summary counter repair requires us to freeze the filesystem to stop all
+# filesystem activity, so we can't have userspace wandering in and thawing it.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+# Override the default cleanup function.
+_cleanup()
+{
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -rf $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair fscounters"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/731.out b/tests/xfs/731.out
new file mode 100644 (file)
index 0000000..93b1b26
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 731
+Silence is golden
diff --git a/tests/xfs/732 b/tests/xfs/732
new file mode 100755 (executable)
index 0000000..a39a1f8
--- /dev/null
@@ -0,0 +1,46 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 732
+#
+# Race fsstress and fscounter repair on the realtime device for a while to see
+# if we crash or livelock.  Summary counter repair requires us to freeze the
+# filesystem to stop all filesystem activity, so we can't have userspace
+# wandering in and thawing it.
+#
+. ./common/preamble
+_begin_fstest auto quick rw scrub realtime
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_realtime
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" realtime
+
+# Force all files to be allocated on the realtime device
+_xfs_force_bdev realtime $SCRATCH_MNT
+_scratch_xfs_stress_online_repair -s 'scrub fscounters' -s "repair fscounters"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/732.out b/tests/xfs/732.out
new file mode 100644 (file)
index 0000000..451f82c
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 732
+Silence is golden
diff --git a/tests/xfs/733 b/tests/xfs/733
new file mode 100755 (executable)
index 0000000..d9f9b1c
--- /dev/null
@@ -0,0 +1,48 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 733
+#
+# Populate a XFS filesystem and fuzz the data mappings of every directory type.
+# Use xfs_scrub to fix the corruption.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_dir_fuzz_types
+
+# Now fuzz the block maps of each directory type.
+for dirtype in "${SCRATCH_XFS_DIR_FUZZ_TYPES[@]}"; do
+       echo "Fuzz block map for ${dirtype}" | tee -a $seqres.full
+
+       # Restore a correct copy of the filesystem before we start a round of
+       # fuzzing.  This avoids corruption errors from xfs_db when
+       # _scratch_xfs_fuzz_metadata probes the directory block fields.
+       __scratch_xfs_fuzz_mdrestore
+
+       _scratch_mount
+       inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_${dirtype})
+       _scratch_unmount
+
+       _scratch_xfs_fuzz_metadata 'u*.bmx' 'online'  "inode ${inum}" >> $seqres.full
+       echo "Done fuzzing dir map ${dirtype}"
+done
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/733.out b/tests/xfs/733.out
new file mode 100644 (file)
index 0000000..949fbc1
--- /dev/null
@@ -0,0 +1,10 @@
+QA output created by 733
+Format and populate
+Fuzz block map for BLOCK
+Done fuzzing dir map BLOCK
+Fuzz block map for LEAF
+Done fuzzing dir map LEAF
+Fuzz block map for LEAFN
+Done fuzzing dir map LEAFN
+Fuzz block map for NODE
+Done fuzzing dir map NODE
diff --git a/tests/xfs/734 b/tests/xfs/734
new file mode 100755 (executable)
index 0000000..9ffe0df
--- /dev/null
@@ -0,0 +1,48 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 734
+#
+# Populate a XFS filesystem and fuzz the data mappings of every directory type.
+# Use xfs_repair to fix the corruption.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_repair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_dir_fuzz_types
+
+# Now fuzz the block maps of each directory type.
+for dirtype in "${SCRATCH_XFS_DIR_FUZZ_TYPES[@]}"; do
+       echo "Fuzz block map for ${dirtype}" | tee -a $seqres.full
+
+       # Restore a correct copy of the filesystem before we start a round of
+       # fuzzing.  This avoids corruption errors from xfs_db when
+       # _scratch_xfs_fuzz_metadata probes the directory block fields.
+       __scratch_xfs_fuzz_mdrestore
+
+       _scratch_mount
+       inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_${dirtype})
+       _scratch_unmount
+
+       _scratch_xfs_fuzz_metadata 'u*.bmx' 'offline'  "inode ${inum}" >> $seqres.full
+       echo "Done fuzzing dir map ${dirtype}"
+done
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/734.out b/tests/xfs/734.out
new file mode 100644 (file)
index 0000000..80b91b6
--- /dev/null
@@ -0,0 +1,10 @@
+QA output created by 734
+Format and populate
+Fuzz block map for BLOCK
+Done fuzzing dir map BLOCK
+Fuzz block map for LEAF
+Done fuzzing dir map LEAF
+Fuzz block map for LEAFN
+Done fuzzing dir map LEAFN
+Fuzz block map for NODE
+Done fuzzing dir map NODE
diff --git a/tests/xfs/735 b/tests/xfs/735
new file mode 100755 (executable)
index 0000000..96a171f
--- /dev/null
@@ -0,0 +1,48 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 735
+#
+# Populate a XFS filesystem and fuzz the data mappings of every directory type.
+# Do not fix the filesystem, to test metadata verifiers.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_norepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_dir_fuzz_types
+
+# Now fuzz the block maps of each directory type.
+for dirtype in "${SCRATCH_XFS_DIR_FUZZ_TYPES[@]}"; do
+       echo "Fuzz block map for ${dirtype}" | tee -a $seqres.full
+
+       # Restore a correct copy of the filesystem before we start a round of
+       # fuzzing.  This avoids corruption errors from xfs_db when
+       # _scratch_xfs_fuzz_metadata probes the directory block fields.
+       __scratch_xfs_fuzz_mdrestore
+
+       _scratch_mount
+       inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_${dirtype})
+       _scratch_unmount
+
+       _scratch_xfs_fuzz_metadata 'u*.bmx' 'none'  "inode ${inum}" >> $seqres.full
+       echo "Done fuzzing dir map ${dirtype}"
+done
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/735.out b/tests/xfs/735.out
new file mode 100644 (file)
index 0000000..5fa88f7
--- /dev/null
@@ -0,0 +1,10 @@
+QA output created by 735
+Format and populate
+Fuzz block map for BLOCK
+Done fuzzing dir map BLOCK
+Fuzz block map for LEAF
+Done fuzzing dir map LEAF
+Fuzz block map for LEAFN
+Done fuzzing dir map LEAFN
+Fuzz block map for NODE
+Done fuzzing dir map NODE
diff --git a/tests/xfs/736 b/tests/xfs/736
new file mode 100755 (executable)
index 0000000..4a6c468
--- /dev/null
@@ -0,0 +1,48 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 736
+#
+# Populate a XFS filesystem and fuzz the attr mappings of every xattr type.
+# Use xfs_scrub to fix the corruption.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_xattr_fuzz_types
+
+# Now fuzz the block maps of each xattr type.
+for attrtype in "${SCRATCH_XFS_XATTR_FUZZ_TYPES[@]}"; do
+       echo "Fuzz block map for ${attrtype}" | tee -a $seqres.full
+
+       # Restore a correct copy of the filesystem before we start a round of
+       # fuzzing.  This avoids corruption errors from xfs_db when
+       # _scratch_xfs_fuzz_metadata probes the xattr block fields.
+       __scratch_xfs_fuzz_mdrestore
+
+       _scratch_mount
+       inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_${attrtype})
+       _scratch_unmount
+
+       _scratch_xfs_fuzz_metadata 'a*.bmx' 'online'  "inode ${inum}" >> $seqres.full
+       echo "Done fuzzing attr map ${attrtype}"
+done
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/736.out b/tests/xfs/736.out
new file mode 100644 (file)
index 0000000..444e618
--- /dev/null
@@ -0,0 +1,10 @@
+QA output created by 736
+Format and populate
+Fuzz block map for EXTENTS_REMOTE3K
+Done fuzzing attr map EXTENTS_REMOTE3K
+Fuzz block map for EXTENTS_REMOTE4K
+Done fuzzing attr map EXTENTS_REMOTE4K
+Fuzz block map for LEAF
+Done fuzzing attr map LEAF
+Fuzz block map for NODE
+Done fuzzing attr map NODE
diff --git a/tests/xfs/737 b/tests/xfs/737
new file mode 100755 (executable)
index 0000000..6fc0bba
--- /dev/null
@@ -0,0 +1,48 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 737
+#
+# Populate a XFS filesystem and fuzz the attr mappings of every xattr type.
+# Use xfs_repair to fix the corruption.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_repair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_xattr_fuzz_types
+
+# Now fuzz the block maps of each xattr type.
+for attrtype in "${SCRATCH_XFS_XATTR_FUZZ_TYPES[@]}"; do
+       echo "Fuzz block map for ${attrtype}" | tee -a $seqres.full
+
+       # Restore a correct copy of the filesystem before we start a round of
+       # fuzzing.  This avoids corruption errors from xfs_db when
+       # _scratch_xfs_fuzz_metadata probes the xattr block fields.
+       __scratch_xfs_fuzz_mdrestore
+
+       _scratch_mount
+       inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_${attrtype})
+       _scratch_unmount
+
+       _scratch_xfs_fuzz_metadata 'a*.bmx' 'offline'  "inode ${inum}" >> $seqres.full
+       echo "Done fuzzing attr map ${attrtype}"
+done
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/737.out b/tests/xfs/737.out
new file mode 100644 (file)
index 0000000..7ee0f0c
--- /dev/null
@@ -0,0 +1,10 @@
+QA output created by 737
+Format and populate
+Fuzz block map for EXTENTS_REMOTE3K
+Done fuzzing attr map EXTENTS_REMOTE3K
+Fuzz block map for EXTENTS_REMOTE4K
+Done fuzzing attr map EXTENTS_REMOTE4K
+Fuzz block map for LEAF
+Done fuzzing attr map LEAF
+Fuzz block map for NODE
+Done fuzzing attr map NODE
diff --git a/tests/xfs/738 b/tests/xfs/738
new file mode 100755 (executable)
index 0000000..e2f8a9c
--- /dev/null
@@ -0,0 +1,48 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 738
+#
+# Populate a XFS filesystem and fuzz the attr mappings of every xattr type.
+# Do not fix the filesystem, to test metadata verifiers.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_norepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_xattr_fuzz_types
+
+# Now fuzz the block maps of each xattr type.
+for attrtype in "${SCRATCH_XFS_XATTR_FUZZ_TYPES[@]}"; do
+       echo "Fuzz block map for ${attrtype}" | tee -a $seqres.full
+
+       # Restore a correct copy of the filesystem before we start a round of
+       # fuzzing.  This avoids corruption errors from xfs_db when
+       # _scratch_xfs_fuzz_metadata probes the xattr block fields.
+       __scratch_xfs_fuzz_mdrestore
+
+       _scratch_mount
+       inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_${attrtype})
+       _scratch_unmount
+
+       _scratch_xfs_fuzz_metadata 'a*.bmx' 'none'  "inode ${inum}" >> $seqres.full
+       echo "Done fuzzing attr map ${attrtype}"
+done
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/738.out b/tests/xfs/738.out
new file mode 100644 (file)
index 0000000..e0cf9f5
--- /dev/null
@@ -0,0 +1,10 @@
+QA output created by 738
+Format and populate
+Fuzz block map for EXTENTS_REMOTE3K
+Done fuzzing attr map EXTENTS_REMOTE3K
+Fuzz block map for EXTENTS_REMOTE4K
+Done fuzzing attr map EXTENTS_REMOTE4K
+Fuzz block map for LEAF
+Done fuzzing attr map LEAF
+Fuzz block map for NODE
+Done fuzzing attr map NODE
diff --git a/tests/xfs/739 b/tests/xfs/739
new file mode 100755 (executable)
index 0000000..fb7fe2e
--- /dev/null
@@ -0,0 +1,41 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 739
+#
+# Populate a XFS filesystem and fuzz every realtime bitmap field.
+# Use xfs_scrub to fix the corruption.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtbitmap"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.bitmap')
+if [ -n "$is_metadir" ]; then
+       path=('path -m /realtime/0.bitmap')
+else
+       path=('sb' 'addr rbmino')
+fi
+_scratch_xfs_fuzz_metadata '' 'online' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtbitmap"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/739.out b/tests/xfs/739.out
new file mode 100644 (file)
index 0000000..7f0c604
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 739
+Format and populate
+Fuzz rtbitmap
+Done fuzzing rtbitmap
diff --git a/tests/xfs/740 b/tests/xfs/740
new file mode 100755 (executable)
index 0000000..a59fa37
--- /dev/null
@@ -0,0 +1,41 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 740
+#
+# Populate a XFS filesystem and fuzz every realtime summary field.
+# Use xfs_scrub to fix the corruption.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtsummary"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.summary')
+if [ -n "$is_metadir" ]; then
+       path=('path -m /realtime/0.summary')
+else
+       path=('sb' 'addr rsumino')
+fi
+_scratch_xfs_fuzz_metadata '' 'online' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtsummary"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/740.out b/tests/xfs/740.out
new file mode 100644 (file)
index 0000000..bb86652
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 740
+Format and populate
+Fuzz rtsummary
+Done fuzzing rtsummary
diff --git a/tests/xfs/741 b/tests/xfs/741
new file mode 100755 (executable)
index 0000000..957bed7
--- /dev/null
@@ -0,0 +1,41 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 741
+#
+# Populate a XFS filesystem and fuzz every realtime bitmap field.
+# Use xfs_repair to fix the corruption.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_repair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtbitmap"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.bitmap')
+if [ -n "$is_metadir" ]; then
+       path=('path -m /realtime/0.bitmap')
+else
+       path=('sb' 'addr rbmino')
+fi
+_scratch_xfs_fuzz_metadata '' 'offline' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtbitmap"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/741.out b/tests/xfs/741.out
new file mode 100644 (file)
index 0000000..db7a071
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 741
+Format and populate
+Fuzz rtbitmap
+Done fuzzing rtbitmap
diff --git a/tests/xfs/742 b/tests/xfs/742
new file mode 100755 (executable)
index 0000000..d911748
--- /dev/null
@@ -0,0 +1,41 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 742
+#
+# Populate a XFS filesystem and fuzz every realtime summary field.
+# Use xfs_repair to fix the corruption.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_repair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtsummary"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.summary')
+if [ -n "$is_metadir" ]; then
+       path=('path -m /realtime/0.summary')
+else
+       path=('sb' 'addr rsumino')
+fi
+_scratch_xfs_fuzz_metadata '' 'offline' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtsummary"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/742.out b/tests/xfs/742.out
new file mode 100644 (file)
index 0000000..dd209a8
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 742
+Format and populate
+Fuzz rtsummary
+Done fuzzing rtsummary
diff --git a/tests/xfs/743 b/tests/xfs/743
new file mode 100755 (executable)
index 0000000..69e865a
--- /dev/null
@@ -0,0 +1,42 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 743
+#
+# Populate a XFS filesystem and fuzz every realtime bitmap field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_bothrepair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtbitmap"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.bitmap')
+if [ -n "$is_metadir" ]; then
+       path=('path -m /realtime/0.bitmap')
+else
+       path=('sb' 'addr rbmino')
+fi
+_scratch_xfs_fuzz_metadata '' 'both' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtbitmap"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/743.out b/tests/xfs/743.out
new file mode 100644 (file)
index 0000000..d4ef1fd
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 743
+Format and populate
+Fuzz rtbitmap
+Done fuzzing rtbitmap
diff --git a/tests/xfs/744 b/tests/xfs/744
new file mode 100755 (executable)
index 0000000..ea490b5
--- /dev/null
@@ -0,0 +1,42 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 744
+#
+# Populate a XFS filesystem and fuzz every realtime summary field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_bothrepair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtsummary"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.summary')
+if [ -n "$is_metadir" ]; then
+       path=('path -m /realtime/0.summary')
+else
+       path=('sb' 'addr rsumino')
+fi
+_scratch_xfs_fuzz_metadata '' 'both' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtsummary"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/744.out b/tests/xfs/744.out
new file mode 100644 (file)
index 0000000..99e0d48
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 744
+Format and populate
+Fuzz rtsummary
+Done fuzzing rtsummary
diff --git a/tests/xfs/745 b/tests/xfs/745
new file mode 100755 (executable)
index 0000000..7621d45
--- /dev/null
@@ -0,0 +1,41 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 745
+#
+# Populate a XFS filesystem and fuzz every realtime bitmap field.
+# Do not fix the filesystem, to test metadata verifiers.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_norepair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtbitmap"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.bitmap')
+if [ -n "$is_metadir" ]; then
+       path=('path -m /realtime/0.bitmap')
+else
+       path=('sb' 'addr rbmino')
+fi
+_scratch_xfs_fuzz_metadata '' 'none' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtbitmap"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/745.out b/tests/xfs/745.out
new file mode 100644 (file)
index 0000000..0b8f207
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 745
+Format and populate
+Fuzz rtbitmap
+Done fuzzing rtbitmap
diff --git a/tests/xfs/746 b/tests/xfs/746
new file mode 100755 (executable)
index 0000000..4012331
--- /dev/null
@@ -0,0 +1,41 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 746
+#
+# Populate a XFS filesystem and fuzz every realtime summary field.
+# Do not fix the filesystem, to test metadata verifiers.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_norepair realtime
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz rtsummary"
+is_metadir=$(_scratch_xfs_get_metadata_field "core.version" 'path -m /realtime/0.summary')
+if [ -n "$is_metadir" ]; then
+       path=('path -m /realtime/0.summary')
+else
+       path=('sb' 'addr rsumino')
+fi
+_scratch_xfs_fuzz_metadata '' 'none' "${path[@]}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing rtsummary"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/746.out b/tests/xfs/746.out
new file mode 100644 (file)
index 0000000..375c7a0
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 746
+Format and populate
+Fuzz rtsummary
+Done fuzzing rtsummary
diff --git a/tests/xfs/747 b/tests/xfs/747
new file mode 100755 (executable)
index 0000000..e3a8af7
--- /dev/null
@@ -0,0 +1,35 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 747
+#
+# Populate a XFS filesystem and fuzz every superblock field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz superblock"
+_scratch_xfs_fuzz_metadata '' 'both' 'sb 1' >> $seqres.full
+echo "Done fuzzing superblock"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/747.out b/tests/xfs/747.out
new file mode 100644 (file)
index 0000000..1cf4e74
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 747
+Format and populate
+Fuzz superblock
+Done fuzzing superblock
diff --git a/tests/xfs/748 b/tests/xfs/748
new file mode 100755 (executable)
index 0000000..c69e7a4
--- /dev/null
@@ -0,0 +1,35 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 748
+#
+# Populate a XFS filesystem and fuzz every AGF field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz AGF"
+_scratch_xfs_fuzz_metadata '' 'both' 'agf 0' >> $seqres.full
+echo "Done fuzzing AGF"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/748.out b/tests/xfs/748.out
new file mode 100644 (file)
index 0000000..a281c13
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 748
+Format and populate
+Fuzz AGF
+Done fuzzing AGF
diff --git a/tests/xfs/749 b/tests/xfs/749
new file mode 100755 (executable)
index 0000000..a1a6530
--- /dev/null
@@ -0,0 +1,45 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 749
+#
+# Populate a XFS filesystem and fuzz every AGFL field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz AGFL"
+_scratch_xfs_fuzz_metadata '' 'both' 'agfl 0' >> $seqres.full
+echo "Done fuzzing AGFL"
+
+# Restore a correct copy of the filesystem before we start the second round of
+# fuzzing.  This avoids corruption errors from xfs_db when we probe for flfirst
+# in the AGF and later when _scratch_xfs_fuzz_metadata probes the AGFL fields.
+__scratch_xfs_fuzz_mdrestore
+flfirst=$(_scratch_xfs_db -c 'agf 0' -c 'p flfirst' | sed -e 's/flfirst = //g')
+
+echo "Fuzz AGFL flfirst"
+SCRATCH_XFS_LIST_METADATA_FIELDS="bno[${flfirst}]" _scratch_xfs_fuzz_metadata '' 'both' 'agfl 0' >> $seqres.full
+echo "Done fuzzing AGFL flfirst"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/749.out b/tests/xfs/749.out
new file mode 100644 (file)
index 0000000..478ce52
--- /dev/null
@@ -0,0 +1,6 @@
+QA output created by 749
+Format and populate
+Fuzz AGFL
+Done fuzzing AGFL
+Fuzz AGFL flfirst
+Done fuzzing AGFL flfirst
diff --git a/tests/xfs/750 b/tests/xfs/750
new file mode 100755 (executable)
index 0000000..f9b6553
--- /dev/null
@@ -0,0 +1,35 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 750
+#
+# Populate a XFS filesystem and fuzz every AGI field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Fuzz AGI"
+_scratch_xfs_fuzz_metadata '' 'both' 'agi 0' >> $seqres.full
+echo "Done fuzzing AGI"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/750.out b/tests/xfs/750.out
new file mode 100644 (file)
index 0000000..7521416
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 750
+Format and populate
+Fuzz AGI
+Done fuzzing AGI
diff --git a/tests/xfs/751 b/tests/xfs/751
new file mode 100755 (executable)
index 0000000..a9634bf
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 751
+#
+# Populate a XFS filesystem and fuzz every bnobt field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+       _fail "could not find two-level bnobt"
+
+echo "Fuzz bnobt recs"
+_scratch_xfs_fuzz_metadata '' 'both'  "$path" 'addr bnoroot' 'addr ptrs[1]' >> $seqres.full
+echo "Done fuzzing bnobt recs"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/751.out b/tests/xfs/751.out
new file mode 100644 (file)
index 0000000..77a74f3
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 751
+Format and populate
+Fuzz bnobt recs
+Done fuzzing bnobt recs
diff --git a/tests/xfs/752 b/tests/xfs/752
new file mode 100755 (executable)
index 0000000..9fb3f43
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 752
+#
+# Populate a XFS filesystem and fuzz every bnobt key/pointer.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'bno' 2)" || \
+       _fail "could not find two-level bnobt"
+
+echo "Fuzz bnobt keyptr"
+_scratch_xfs_fuzz_metadata '' 'both'  "$path" 'addr bnoroot' >> $seqres.full
+echo "Done fuzzing bnobt keyptr"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/752.out b/tests/xfs/752.out
new file mode 100644 (file)
index 0000000..2e8348d
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 752
+Format and populate
+Fuzz bnobt keyptr
+Done fuzzing bnobt keyptr
diff --git a/tests/xfs/753 b/tests/xfs/753
new file mode 100755 (executable)
index 0000000..151a964
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 753
+#
+# Populate a XFS filesystem and fuzz every cntbt field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'cnt' 2)" || \
+       _fail "could not find two-level cntbt"
+
+echo "Fuzz cntbt"
+_scratch_xfs_fuzz_metadata '' 'both'  "$path" 'addr cntroot' 'addr ptrs[1]' >> $seqres.full
+echo "Done fuzzing cntbt"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/753.out b/tests/xfs/753.out
new file mode 100644 (file)
index 0000000..0c98196
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 753
+Format and populate
+Fuzz cntbt
+Done fuzzing cntbt
diff --git a/tests/xfs/754 b/tests/xfs/754
new file mode 100755 (executable)
index 0000000..ad9078c
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 754
+#
+# Populate a XFS filesystem and fuzz every inobt field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+       _fail "could not find two-level inobt"
+
+echo "Fuzz inobt"
+_scratch_xfs_fuzz_metadata '' 'both'  "$path" 'addr root' 'addr ptrs[1]' >> $seqres.full
+echo "Done fuzzing inobt"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/754.out b/tests/xfs/754.out
new file mode 100644 (file)
index 0000000..0b8eef9
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 754
+Format and populate
+Fuzz inobt
+Done fuzzing inobt
diff --git a/tests/xfs/755 b/tests/xfs/755
new file mode 100755 (executable)
index 0000000..b83118b
--- /dev/null
@@ -0,0 +1,39 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 755
+#
+# Populate a XFS filesystem and fuzz every finobt field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+_require_xfs_finobt
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'fino' 2)" || \
+       _fail "could not find two-level finobt"
+
+echo "Fuzz finobt"
+_scratch_xfs_fuzz_metadata '' 'both'  "$path" 'addr free_root' 'addr ptrs[1]' >> $seqres.full
+echo "Done fuzzing finobt"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/755.out b/tests/xfs/755.out
new file mode 100644 (file)
index 0000000..55e5ff4
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 755
+Format and populate
+Fuzz finobt
+Done fuzzing finobt
diff --git a/tests/xfs/756 b/tests/xfs/756
new file mode 100755 (executable)
index 0000000..351c7ed
--- /dev/null
@@ -0,0 +1,39 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 756
+#
+# Populate a XFS filesystem and fuzz every rmapbt field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_scratch_rmapbt
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+       _fail "could not find two-level rmapbt"
+
+echo "Fuzz rmapbt recs"
+_scratch_xfs_fuzz_metadata '' 'both' "$path" 'addr rmaproot' 'addr ptrs[1]' >> $seqres.full
+echo "Done fuzzing rmapbt recs"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/756.out b/tests/xfs/756.out
new file mode 100644 (file)
index 0000000..76df05a
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 756
+Format and populate
+Fuzz rmapbt recs
+Done fuzzing rmapbt recs
diff --git a/tests/xfs/757 b/tests/xfs/757
new file mode 100755 (executable)
index 0000000..06b360f
--- /dev/null
@@ -0,0 +1,39 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 757
+#
+# Populate a XFS filesystem and fuzz every rmapbt key/pointer field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_scratch_rmapbt
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'rmap' 2)" || \
+       _fail "could not find two-level rmapbt"
+
+echo "Fuzz rmapbt keyptr"
+_scratch_xfs_fuzz_metadata '' 'both' "$path" 'addr rmaproot' >> $seqres.full
+echo "Done fuzzing rmapbt keyptr"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/757.out b/tests/xfs/757.out
new file mode 100644 (file)
index 0000000..293a863
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 757
+Format and populate
+Fuzz rmapbt keyptr
+Done fuzzing rmapbt keyptr
diff --git a/tests/xfs/758 b/tests/xfs/758
new file mode 100755 (executable)
index 0000000..d2b6e46
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 758
+#
+# Populate a XFS filesystem and fuzz every refcountbt key/pointer field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+. ./common/reflink
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_reflink
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+       _fail "could not find two-level refcountbt"
+
+echo "Fuzz refcountbt"
+_scratch_xfs_fuzz_metadata '' 'both'  "$path" 'addr refcntroot' >> $seqres.full
+echo "Done fuzzing refcountbt"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/758.out b/tests/xfs/758.out
new file mode 100644 (file)
index 0000000..e969d7b
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 758
+Format and populate
+Fuzz refcountbt
+Done fuzzing refcountbt
diff --git a/tests/xfs/759 b/tests/xfs/759
new file mode 100755 (executable)
index 0000000..8ed3f0b
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 759
+#
+# Populate a XFS filesystem and fuzz every btree-format directory inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find btree-format dir inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_BTREE)
+_scratch_unmount
+
+echo "Fuzz inode"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inode"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/759.out b/tests/xfs/759.out
new file mode 100644 (file)
index 0000000..3eaa678
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 759
+Format and populate
+Find btree-format dir inode
+Fuzz inode
+Done fuzzing inode
diff --git a/tests/xfs/760 b/tests/xfs/760
new file mode 100755 (executable)
index 0000000..47a5dd3
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 760
+#
+# Populate a XFS filesystem and fuzz every extents-format file inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find extents-format file inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFREG.FMT_EXTENTS)
+_scratch_unmount
+
+echo "Fuzz inode"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inode"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/760.out b/tests/xfs/760.out
new file mode 100644 (file)
index 0000000..9b66d13
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 760
+Format and populate
+Find extents-format file inode
+Fuzz inode
+Done fuzzing inode
diff --git a/tests/xfs/761 b/tests/xfs/761
new file mode 100755 (executable)
index 0000000..87d302d
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 761
+#
+# Populate a XFS filesystem and fuzz every btree-format file inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find btree-format file inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFREG.FMT_BTREE)
+_scratch_unmount
+
+echo "Fuzz inode"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inode"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/761.out b/tests/xfs/761.out
new file mode 100644 (file)
index 0000000..43cbe4d
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 761
+Format and populate
+Find btree-format file inode
+Fuzz inode
+Done fuzzing inode
diff --git a/tests/xfs/762 b/tests/xfs/762
new file mode 100755 (executable)
index 0000000..0ce1773
--- /dev/null
@@ -0,0 +1,42 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 762
+#
+# Populate a XFS filesystem and fuzz every bmbt block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find bmbt block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFREG.FMT_BTREE)
+_scratch_unmount
+
+inode_ver=$(_scratch_xfs_get_metadata_field "core.version" "inode ${inum}")
+
+echo "Fuzz bmbt"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" "addr u${inode_ver}.bmbt.ptrs[1]" >> $seqres.full
+echo "Done fuzzing bmbt"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/762.out b/tests/xfs/762.out
new file mode 100644 (file)
index 0000000..1ff528e
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 762
+Format and populate
+Find bmbt block
+Fuzz bmbt
+Done fuzzing bmbt
diff --git a/tests/xfs/763 b/tests/xfs/763
new file mode 100755 (executable)
index 0000000..9814535
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 763
+#
+# Populate a XFS filesystem and fuzz every symlink remote block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find symlink remote block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFLNK.FMT_EXTENTS)
+_scratch_unmount
+
+echo "Fuzz symlink remote block"
+_scratch_xfs_fuzz_metadata '' 'both' "inode ${inum}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing symlink remote block"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/763.out b/tests/xfs/763.out
new file mode 100644 (file)
index 0000000..00fe93d
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 763
+Format and populate
+Find symlink remote block
+Fuzz symlink remote block
+Done fuzzing symlink remote block
diff --git a/tests/xfs/764 b/tests/xfs/764
new file mode 100755 (executable)
index 0000000..3addb9c
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 764
+#
+# Populate a XFS filesystem and fuzz every inline directory inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find inline-format dir inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_INLINE)
+_scratch_unmount
+
+echo "Fuzz inline-format dir inode"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inline-format dir inode"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/764.out b/tests/xfs/764.out
new file mode 100644 (file)
index 0000000..727f797
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 764
+Format and populate
+Find inline-format dir inode
+Fuzz inline-format dir inode
+Done fuzzing inline-format dir inode
diff --git a/tests/xfs/765 b/tests/xfs/765
new file mode 100755 (executable)
index 0000000..1a260c3
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 765
+#
+# Populate a XFS filesystem and fuzz every block-format dir block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find data-format dir block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_BLOCK)
+_scratch_unmount
+
+echo "Fuzz data-format dir block"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" 'dblock 0' >> $seqres.full
+echo "Done fuzzing data-format dir block"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/765.out b/tests/xfs/765.out
new file mode 100644 (file)
index 0000000..008c22e
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 765
+Format and populate
+Find data-format dir block
+Fuzz data-format dir block
+Done fuzzing data-format dir block
diff --git a/tests/xfs/766 b/tests/xfs/766
new file mode 100755 (executable)
index 0000000..b0ff2ad
--- /dev/null
@@ -0,0 +1,41 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 766
+#
+# Populate a XFS filesystem and fuzz every data-format dir block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find data-format dir block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_LEAF)
+blk_sz=$(_get_block_size $SCRATCH_MNT)
+_scratch_unmount
+
+echo "Fuzz data-format dir block"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" "dblock 0" >> $seqres.full
+echo "Done fuzzing data-format dir block"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/766.out b/tests/xfs/766.out
new file mode 100644 (file)
index 0000000..29b8e22
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 766
+Format and populate
+Find data-format dir block
+Fuzz data-format dir block
+Done fuzzing data-format dir block
diff --git a/tests/xfs/767 b/tests/xfs/767
new file mode 100755 (executable)
index 0000000..8ca7e7d
--- /dev/null
@@ -0,0 +1,42 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 767
+#
+# Populate a XFS filesystem and fuzz every leaf1-format dir block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find leaf1-format dir block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_LEAF)
+blk_sz=$(_get_block_size $SCRATCH_MNT)
+_scratch_unmount
+
+leaf_offset=$(( (2 ** 35) / blk_sz))
+echo "Fuzz leaf1-format dir block"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" "dblock ${leaf_offset}" >> $seqres.full
+echo "Done fuzzing leaf1-format dir block"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/767.out b/tests/xfs/767.out
new file mode 100644 (file)
index 0000000..2caba2e
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 767
+Format and populate
+Find leaf1-format dir block
+Fuzz leaf1-format dir block
+Done fuzzing leaf1-format dir block
diff --git a/tests/xfs/768 b/tests/xfs/768
new file mode 100755 (executable)
index 0000000..a8faad7
--- /dev/null
@@ -0,0 +1,42 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 768
+#
+# Populate a XFS filesystem and fuzz every leafn-format dir block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find leafn-format dir block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_NODE)
+blk_sz=$(_get_block_size $SCRATCH_MNT)
+_scratch_unmount
+
+leaf_offset=$(( ( (2 ** 35) / blk_sz) + 1))
+echo "Fuzz leafn-format dir block"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" "dblock ${leaf_offset}" >> $seqres.full
+echo "Done fuzzing leafn-format dir block"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/768.out b/tests/xfs/768.out
new file mode 100644 (file)
index 0000000..b45ce63
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 768
+Format and populate
+Find leafn-format dir block
+Fuzz leafn-format dir block
+Done fuzzing leafn-format dir block
diff --git a/tests/xfs/769 b/tests/xfs/769
new file mode 100755 (executable)
index 0000000..e3b439b
--- /dev/null
@@ -0,0 +1,42 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 769
+#
+# Populate a XFS filesystem and fuzz every node-format dir block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find node-format dir block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_NODE)
+blk_sz=$(_get_block_size $SCRATCH_MNT)
+_scratch_unmount
+
+leaf_offset=$(( (2 ** 35) / blk_sz ))
+echo "Fuzz node-format dir block"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" "dblock ${leaf_offset}" >> $seqres.full
+echo "Done fuzzing node-format dir block"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/769.out b/tests/xfs/769.out
new file mode 100644 (file)
index 0000000..dc42f6f
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 769
+Format and populate
+Find node-format dir block
+Fuzz node-format dir block
+Done fuzzing node-format dir block
diff --git a/tests/xfs/770 b/tests/xfs/770
new file mode 100755 (executable)
index 0000000..da12e0a
--- /dev/null
@@ -0,0 +1,42 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 770
+#
+# Populate a XFS filesystem and fuzz every freeindex-format dir block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find freeindex-format dir block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_NODE)
+blk_sz=$(_get_block_size $SCRATCH_MNT)
+_scratch_unmount
+
+leaf_offset=$(( (2 ** 36) / blk_sz ))
+echo "Fuzz freeindex-format dir block"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" "dblock ${leaf_offset}" >> $seqres.full
+echo "Done fuzzing freeindex-format dir block"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/770.out b/tests/xfs/770.out
new file mode 100644 (file)
index 0000000..df52cf3
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 770
+Format and populate
+Find freeindex-format dir block
+Fuzz freeindex-format dir block
+Done fuzzing freeindex-format dir block
diff --git a/tests/xfs/771 b/tests/xfs/771
new file mode 100755 (executable)
index 0000000..c1e8b38
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 771
+#
+# Populate a XFS filesystem and fuzz every inline attr inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find inline-format attr inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_LOCAL)
+_scratch_unmount
+
+echo "Fuzz inline-format attr inode"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inline-format attr inode"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/771.out b/tests/xfs/771.out
new file mode 100644 (file)
index 0000000..526bb00
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 771
+Format and populate
+Find inline-format attr inode
+Fuzz inline-format attr inode
+Done fuzzing inline-format attr inode
diff --git a/tests/xfs/772 b/tests/xfs/772
new file mode 100755 (executable)
index 0000000..50577fe
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 772
+#
+# Populate a XFS filesystem and fuzz every leaf-format attr block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find leaf-format attr block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_LEAF)
+_scratch_unmount
+
+echo "Fuzz leaf-format attr block"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" 'ablock 0' >> $seqres.full
+echo "Done fuzzing leaf-format attr block"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/772.out b/tests/xfs/772.out
new file mode 100644 (file)
index 0000000..c774116
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 772
+Format and populate
+Find leaf-format attr block
+Fuzz leaf-format attr block
+Done fuzzing leaf-format attr block
diff --git a/tests/xfs/773 b/tests/xfs/773
new file mode 100755 (executable)
index 0000000..8d37e0f
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 773
+#
+# Populate a XFS filesystem and fuzz every node-format attr block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find node-format attr block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_NODE)
+_scratch_unmount
+
+echo "Fuzz node-format attr block"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" "ablock 0" >> $seqres.full
+echo "Done fuzzing node-format attr block"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/773.out b/tests/xfs/773.out
new file mode 100644 (file)
index 0000000..d301cda
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 773
+Format and populate
+Find node-format attr block
+Fuzz node-format attr block
+Done fuzzing node-format attr block
diff --git a/tests/xfs/774 b/tests/xfs/774
new file mode 100755 (executable)
index 0000000..ad5d4d7
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 774
+#
+# Populate a XFS filesystem and fuzz every external attr block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find external attr block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_EXTENTS_REMOTE3K)
+_scratch_unmount
+
+echo "Fuzz external attr block"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" "ablock 1" >> $seqres.full
+echo "Done fuzzing external attr block"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/774.out b/tests/xfs/774.out
new file mode 100644 (file)
index 0000000..58b3ea0
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 774
+Format and populate
+Find external attr block
+Fuzz external attr block
+Done fuzzing external attr block
diff --git a/tests/xfs/775 b/tests/xfs/775
new file mode 100755 (executable)
index 0000000..ec98d68
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 775
+#
+# Populate a XFS filesystem and fuzz every refcountbt field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+. ./common/reflink
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_reflink
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'refcnt' 2)" || \
+       _fail "could not find two-level refcountbt"
+
+echo "Fuzz refcountbt"
+_scratch_xfs_fuzz_metadata '' 'both'  "$path" 'addr refcntroot' 'addr ptrs[1]' >> $seqres.full
+echo "Done fuzzing refcountbt"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/775.out b/tests/xfs/775.out
new file mode 100644 (file)
index 0000000..71eaf9c
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 775
+Format and populate
+Fuzz refcountbt
+Done fuzzing refcountbt
diff --git a/tests/xfs/776 b/tests/xfs/776
new file mode 100755 (executable)
index 0000000..a7b7add
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 776
+#
+# Populate a XFS filesystem and fuzz every btree-format attr inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find btree-format attr inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_BTREE)
+_scratch_unmount
+
+echo "Fuzz inode"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inode"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/776.out b/tests/xfs/776.out
new file mode 100644 (file)
index 0000000..226fc02
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 776
+Format and populate
+Find btree-format attr inode
+Fuzz inode
+Done fuzzing inode
diff --git a/tests/xfs/777 b/tests/xfs/777
new file mode 100755 (executable)
index 0000000..496a28d
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 777
+#
+# Populate a XFS filesystem and fuzz every blockdev inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find blockdev inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFBLK)
+_scratch_unmount
+
+echo "Fuzz inode"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inode"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/777.out b/tests/xfs/777.out
new file mode 100644 (file)
index 0000000..daca70d
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 777
+Format and populate
+Find blockdev inode
+Fuzz inode
+Done fuzzing inode
diff --git a/tests/xfs/778 b/tests/xfs/778
new file mode 100755 (executable)
index 0000000..72f7d0d
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 778
+#
+# Populate a XFS filesystem and fuzz every local-format symlink inode field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find local-format symlink inode"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFLNK.FMT_LOCAL)
+_scratch_unmount
+
+echo "Fuzz inode"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" >> $seqres.full
+echo "Done fuzzing inode"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/778.out b/tests/xfs/778.out
new file mode 100644 (file)
index 0000000..a729f11
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 778
+Format and populate
+Find local-format symlink inode
+Fuzz inode
+Done fuzzing inode
diff --git a/tests/xfs/779 b/tests/xfs/779
new file mode 100755 (executable)
index 0000000..fe0de30
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 779
+#
+# Populate a XFS filesystem and fuzz every user dquot field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+_require_quota
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+echo "${MOUNT_OPTIONS}" | grep -q 'usrquota' || _notrun "user quota disabled"
+
+echo "Fuzz user 0 dquot"
+_scratch_xfs_fuzz_metadata '' 'both'  "dquot -u 0" >> $seqres.full
+echo "Done fuzzing dquot"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/779.out b/tests/xfs/779.out
new file mode 100644 (file)
index 0000000..a8c19a9
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 779
+Format and populate
+Fuzz user 0 dquot
+Done fuzzing dquot
diff --git a/tests/xfs/780 b/tests/xfs/780
new file mode 100755 (executable)
index 0000000..0a23473
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 780
+#
+# Populate a XFS filesystem and fuzz every group dquot field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+_require_quota
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+echo "${MOUNT_OPTIONS}" | grep -q 'grpquota' || _notrun "group quota disabled"
+
+echo "Fuzz group 0 dquot"
+_scratch_xfs_fuzz_metadata '' 'both'  "dquot -g 0" >> $seqres.full
+echo "Done fuzzing dquot"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/780.out b/tests/xfs/780.out
new file mode 100644 (file)
index 0000000..df5784d
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 780
+Format and populate
+Fuzz group 0 dquot
+Done fuzzing dquot
diff --git a/tests/xfs/781 b/tests/xfs/781
new file mode 100755 (executable)
index 0000000..ada0f8a
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 781
+#
+# Populate a XFS filesystem and fuzz every project dquot field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+. ./common/quota
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+_require_quota
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+echo "${MOUNT_OPTIONS}" | grep -q 'prjquota' || _notrun "project quota disabled"
+
+echo "Fuzz project 0 dquot"
+_scratch_xfs_fuzz_metadata '' 'both'  "dquot -p 0" >> $seqres.full
+echo "Done fuzzing dquot"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/781.out b/tests/xfs/781.out
new file mode 100644 (file)
index 0000000..68c42e6
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 781
+Format and populate
+Fuzz project 0 dquot
+Done fuzzing dquot
diff --git a/tests/xfs/782 b/tests/xfs/782
new file mode 100755 (executable)
index 0000000..c113ea5
--- /dev/null
@@ -0,0 +1,41 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 782
+#
+# Populate a XFS filesystem and fuzz every single-leafn-format dir block field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+echo "Find single-leafn-format dir block"
+_scratch_mount
+inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_LEAFN)
+blk_sz=$(_get_block_size $SCRATCH_MNT)
+_scratch_unmount
+
+leaf_offset=$(( (2 ** 35) / blk_sz ))
+echo "Fuzz single-leafn-format dir block"
+_scratch_xfs_fuzz_metadata '' 'both'  "inode ${inum}" "dblock ${leaf_offset}" >> $seqres.full
+echo "Done fuzzing single-leafn-format dir block"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/782.out b/tests/xfs/782.out
new file mode 100644 (file)
index 0000000..ec750a6
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 782
+Format and populate
+Find single-leafn-format dir block
+Fuzz single-leafn-format dir block
+Done fuzzing single-leafn-format dir block
diff --git a/tests/xfs/783 b/tests/xfs/783
new file mode 100755 (executable)
index 0000000..bbc621b
--- /dev/null
@@ -0,0 +1,49 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 783
+#
+# Populate a XFS filesystem and fuzz the data mappings of every directory type.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_dir_fuzz_types
+
+# Now fuzz the block maps of each directory type.
+for dirtype in "${SCRATCH_XFS_DIR_FUZZ_TYPES[@]}"; do
+       echo "Fuzz block map for ${dirtype}" | tee -a $seqres.full
+
+       # Restore a correct copy of the filesystem before we start a round of
+       # fuzzing.  This avoids corruption errors from xfs_db when
+       # _scratch_xfs_fuzz_metadata probes the directory block fields.
+       __scratch_xfs_fuzz_mdrestore
+
+       _scratch_mount
+       inum=$(stat -c '%i' $SCRATCH_MNT/S_IFDIR.FMT_${dirtype})
+       _scratch_unmount
+
+       _scratch_xfs_fuzz_metadata 'u*.bmx' 'both'  "inode ${inum}" >> $seqres.full
+       echo "Done fuzzing dir map ${dirtype}"
+done
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/783.out b/tests/xfs/783.out
new file mode 100644 (file)
index 0000000..11e6d93
--- /dev/null
@@ -0,0 +1,10 @@
+QA output created by 783
+Format and populate
+Fuzz block map for BLOCK
+Done fuzzing dir map BLOCK
+Fuzz block map for LEAF
+Done fuzzing dir map LEAF
+Fuzz block map for LEAFN
+Done fuzzing dir map LEAFN
+Fuzz block map for NODE
+Done fuzzing dir map NODE
diff --git a/tests/xfs/784 b/tests/xfs/784
new file mode 100755 (executable)
index 0000000..595f0fa
--- /dev/null
@@ -0,0 +1,49 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 784
+#
+# Populate a XFS filesystem and fuzz the attr mappings of every xattr type.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+_scratch_xfs_set_xattr_fuzz_types
+
+# Now fuzz the block maps of each xattr type.
+for attrtype in "${SCRATCH_XFS_XATTR_FUZZ_TYPES[@]}"; do
+       echo "Fuzz block map for ${attrtype}" | tee -a $seqres.full
+
+       # Restore a correct copy of the filesystem before we start a round of
+       # fuzzing.  This avoids corruption errors from xfs_db when
+       # _scratch_xfs_fuzz_metadata probes the xattr block fields.
+       __scratch_xfs_fuzz_mdrestore
+
+       _scratch_mount
+       inum=$(stat -c '%i' $SCRATCH_MNT/ATTR.FMT_${attrtype})
+       _scratch_unmount
+
+       _scratch_xfs_fuzz_metadata 'a*.bmx' 'both'  "inode ${inum}" >> $seqres.full
+       echo "Done fuzzing attr map ${attrtype}"
+done
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/784.out b/tests/xfs/784.out
new file mode 100644 (file)
index 0000000..b5c3fdd
--- /dev/null
@@ -0,0 +1,10 @@
+QA output created by 784
+Format and populate
+Fuzz block map for EXTENTS_REMOTE3K
+Done fuzzing attr map EXTENTS_REMOTE3K
+Fuzz block map for EXTENTS_REMOTE4K
+Done fuzzing attr map EXTENTS_REMOTE4K
+Fuzz block map for LEAF
+Done fuzzing attr map LEAF
+Fuzz block map for NODE
+Done fuzzing attr map NODE
diff --git a/tests/xfs/785 b/tests/xfs/785
new file mode 100755 (executable)
index 0000000..577ef77
--- /dev/null
@@ -0,0 +1,36 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 785
+#
+# Populate a XFS filesystem and fuzz every inobt key/pointer field.
+# Use xfs_repair to fix the corruption.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_repair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+       _fail "could not find two-level inobt"
+
+echo "Fuzz inobt"
+_scratch_xfs_fuzz_metadata '' 'offline'  "$path" 'addr root' >> $seqres.full
+echo "Done fuzzing inobt"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/785.out b/tests/xfs/785.out
new file mode 100644 (file)
index 0000000..f5cdc6b
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 785
+Format and populate
+Fuzz inobt
+Done fuzzing inobt
diff --git a/tests/xfs/786 b/tests/xfs/786
new file mode 100755 (executable)
index 0000000..73b161d
--- /dev/null
@@ -0,0 +1,36 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 786
+#
+# Populate a XFS filesystem and fuzz every inobt key/pointer field.
+# Use xfs_scrub to fix the corruption.
+#
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_scrub dangerous_online_repair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+       _fail "could not find two-level inobt"
+
+echo "Fuzz inobt"
+_scratch_xfs_fuzz_metadata '' 'online'  "$path" 'addr root' >> $seqres.full
+echo "Done fuzzing inobt"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/786.out b/tests/xfs/786.out
new file mode 100644 (file)
index 0000000..bca4046
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 786
+Format and populate
+Fuzz inobt
+Done fuzzing inobt
diff --git a/tests/xfs/787 b/tests/xfs/787
new file mode 100755 (executable)
index 0000000..7477c50
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 787
+#
+# Populate a XFS filesystem and fuzz every inobt key/pointer field.
+# Try online repair and, if necessary, offline repair,
+# to test the most likely usage pattern.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_bothrepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+       _fail "could not find two-level inobt"
+
+echo "Fuzz inobt"
+_scratch_xfs_fuzz_metadata '' 'both'  "$path" 'addr root' >> $seqres.full
+echo "Done fuzzing inobt"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/787.out b/tests/xfs/787.out
new file mode 100755 (executable)
index 0000000..39bd7c2
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 787
+Format and populate
+Fuzz inobt
+Done fuzzing inobt
diff --git a/tests/xfs/788 b/tests/xfs/788
new file mode 100755 (executable)
index 0000000..c2c54cd
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle, Inc.  All rights reserved.
+#
+# FS QA Test No. 788
+#
+# Populate a XFS filesystem and fuzz every inobt key/pointer field.
+# Do not fix the filesystem, to test metadata verifiers.
+
+. ./common/preamble
+_begin_fstest dangerous_fuzzers dangerous_norepair
+
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/populate
+. ./common/fuzzy
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_xfs_fuzz_fields
+_disable_dmesg_check
+
+echo "Format and populate"
+_scratch_populate_cached nofill > $seqres.full 2>&1
+
+path="$(_scratch_xfs_find_agbtree_height 'ino' 2)" || \
+       _fail "could not find two-level inobt"
+
+echo "Fuzz inobt"
+_scratch_xfs_fuzz_metadata '' 'none'  "$path" 'addr root' >> $seqres.full
+echo "Done fuzzing inobt"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/788.out b/tests/xfs/788.out
new file mode 100644 (file)
index 0000000..5f6414d
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 788
+Format and populate
+Fuzz inobt
+Done fuzzing inobt
diff --git a/tests/xfs/789 b/tests/xfs/789
new file mode 100755 (executable)
index 0000000..b966c65
--- /dev/null
@@ -0,0 +1,59 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 789
+#
+# Simple tests of the old xfs swapext ioctl
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_io_command swapext '-v exchrange'
+_require_test
+
+# We can't do any reasonable swapping if the files we're going to create are
+# realtime files, the rt extent size is greater than 1 block, and we can't use
+# atomic extent swapping to make sure that partially written extents are fully
+# swapped.
+file_blksz=$(_get_file_block_size $TEST_DIR)
+fs_blksz=$(_get_block_size $TEST_DIR)
+if (( $file_blksz != $fs_blksz )); then
+       _xfs_has_feature $TEST_DIR reflink || \
+               _notrun "test requires atomic extent swapping for rextsize=$((file_blksz / fs_blksz))"
+fi
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+
+$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 256k -b 1m' $dir/a >> $seqres.full
+$XFS_IO_PROG -f -c 'pwrite -S 0x59 0 256k -b 1m' $dir/b >> $seqres.full
+$XFS_IO_PROG -f -c 'pwrite -S 0x60 0 256k -b 1m' $dir/c >> $seqres.full
+$XFS_IO_PROG -f -c 'pwrite -S 0x61 0 128k -b 1m' $dir/d >> $seqres.full
+md5sum $dir/a $dir/b $dir/c $dir/d | _filter_test_dir
+
+# Swap two files that are the same length
+echo swap
+$XFS_IO_PROG -c "swapext $dir/b" $dir/a
+md5sum $dir/a $dir/b | _filter_test_dir
+
+# Try to swap two files that are not the same length
+echo fail swap
+$XFS_IO_PROG -c "swapext $dir/c" $dir/d
+md5sum $dir/c $dir/d | _filter_test_dir
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/789.out b/tests/xfs/789.out
new file mode 100644 (file)
index 0000000..1ed23fc
--- /dev/null
@@ -0,0 +1,12 @@
+QA output created by 789
+af5c6e2d6c297f3139a4e99df396c072  TEST_DIR/test-789/a
+fba5c83875b2ab054e06a0284346ebdf  TEST_DIR/test-789/b
+40c08c6f2aca19bb0d2cf1dbd8bc1b1c  TEST_DIR/test-789/c
+81615449a98aaaad8dc179b3bec87f38  TEST_DIR/test-789/d
+swap
+fba5c83875b2ab054e06a0284346ebdf  TEST_DIR/test-789/a
+af5c6e2d6c297f3139a4e99df396c072  TEST_DIR/test-789/b
+fail swap
+swapext: Bad address
+40c08c6f2aca19bb0d2cf1dbd8bc1b1c  TEST_DIR/test-789/c
+81615449a98aaaad8dc179b3bec87f38  TEST_DIR/test-789/d
diff --git a/tests/xfs/790 b/tests/xfs/790
new file mode 100755 (executable)
index 0000000..db6ce74
--- /dev/null
@@ -0,0 +1,62 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 790
+#
+# Make sure an atomic swapext actually runs to completion even if we shut
+# down the filesystem midway through.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/reflink
+. ./common/inject
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_test_program "punch-alternating"
+_require_xfs_io_command startupdate
+_require_xfs_io_error_injection "bmap_finish_one"
+_require_test
+
+filesnap() {
+       echo "$1"
+       md5sum $dir/a $dir/b $dir/c | _filter_test_dir
+}
+
+dir=$TEST_DIR/test-$seq
+mkdir -p $dir
+blksz=65536
+nrblks=137
+
+# Create two files to swap, and try to fragment the first file.
+rm -f $dir/a
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+$here/src/punch-alternating $dir/a
+_pwrite_byte 0x59 0 $((blksz * nrblks)) $dir/b >> $seqres.full
+_pwrite_byte 0x59 0 $((blksz * nrblks)) $dir/c >> $seqres.full
+_pwrite_byte 0x58 0 $((blksz * nrblks)) $dir/a >> $seqres.full
+sync
+
+# Inject a bmap error and trigger it via swapext.
+filesnap "before commit"
+$XFS_IO_PROG -x -c 'inject bmap_finish_one' -c "swapext $dir/b" $dir/a
+
+# Check the file afterwards.
+_test_cycle_mount
+filesnap "after commit"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/790.out b/tests/xfs/790.out
new file mode 100644 (file)
index 0000000..70ebb2f
--- /dev/null
@@ -0,0 +1,10 @@
+QA output created by 790
+before commit
+c7221b1494117327570a0958b0abca51  TEST_DIR/test-790/a
+30cc2b6b307081e10972317013efb0f3  TEST_DIR/test-790/b
+30cc2b6b307081e10972317013efb0f3  TEST_DIR/test-790/c
+swapext: Input/output error
+after commit
+30cc2b6b307081e10972317013efb0f3  TEST_DIR/test-790/a
+c7221b1494117327570a0958b0abca51  TEST_DIR/test-790/b
+30cc2b6b307081e10972317013efb0f3  TEST_DIR/test-790/c
diff --git a/tests/xfs/791 b/tests/xfs/791
new file mode 100755 (executable)
index 0000000..84e3bee
--- /dev/null
@@ -0,0 +1,59 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 791
+#
+# Test scatter-gather atomic file writes.  We create a temporary file, write
+# sparsely to it, then use XFS_EXCH_RANGE_FILE1_WRITTEN flag to swap
+# atomicallly only the ranges that we wrote.  Inject an error so that we can
+# test that log recovery finishes the swap.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/inject
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_xfs_scratch_atomicswap
+_require_xfs_io_error_injection "bmap_finish_one"
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+_require_congruent_file_oplen $SCRATCH_MNT 65536
+
+# Create original file
+_pwrite_byte 0x58 0 1m $SCRATCH_MNT/a >> $seqres.full
+
+# Create the donor file
+$XFS_IO_PROG -f -c 'truncate 1m' $SCRATCH_MNT/b
+_pwrite_byte 0x59 64k 64k $SCRATCH_MNT/b >> $seqres.full
+_pwrite_byte 0x57 768k 64k $SCRATCH_MNT/b >> $seqres.full
+sync
+
+md5sum $SCRATCH_MNT/a | _filter_scratch
+md5sum $SCRATCH_MNT/b | _filter_scratch
+
+# Test swapext.  -h means skip holes in /b, and -e means operate to EOF
+echo swap | tee -a $seqres.full
+$XFS_IO_PROG -x -c 'inject bmap_finish_one' \
+       -c "swapext -v exchrange -f -u -h -e -a $SCRATCH_MNT/b" $SCRATCH_MNT/a
+_scratch_cycle_mount
+
+md5sum $SCRATCH_MNT/a | _filter_scratch
+md5sum $SCRATCH_MNT/b | _filter_scratch
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/791.out b/tests/xfs/791.out
new file mode 100644 (file)
index 0000000..015b6d3
--- /dev/null
@@ -0,0 +1,7 @@
+QA output created by 791
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
+c9fb827e2e3e579dc2a733ddad486d1d  SCRATCH_MNT/b
+swap
+swapext: Input/output error
+e9cbfe8489a68efaa5fcf40cf3106118  SCRATCH_MNT/a
+faf8ed02a5b0638096a817abcc6c2127  SCRATCH_MNT/b
diff --git a/tests/xfs/792 b/tests/xfs/792
new file mode 100755 (executable)
index 0000000..bfbfbce
--- /dev/null
@@ -0,0 +1,61 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 792
+#
+# Test scatter-gather atomic file commits.  Use the startupdate command to
+# create a temporary file, write sparsely to it, then commitupdate -h to
+# perform the scattered update.  Inject an error so that we can test that log
+# recovery finishes the swap.
+
+. ./common/preamble
+_begin_fstest auto quick fiexchange swapext
+
+# Override the default cleanup function.
+_cleanup()
+{
+       cd /
+       rm -r -f $tmp.* $dir
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/inject
+
+# real QA test starts here
+_supported_fs xfs
+_require_xfs_io_command swapext '-v exchrange -a'
+_require_xfs_io_command startupdate '-e'
+_require_xfs_scratch_atomicswap
+_require_xfs_io_error_injection "bmap_finish_one"
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+# Create original file
+_pwrite_byte 0x58 0 1m $SCRATCH_MNT/a >> $seqres.full
+sync
+md5sum $SCRATCH_MNT/a | _filter_scratch
+
+# Test atomic scatter-gather file commits.
+echo commit | tee -a $seqres.full
+$XFS_IO_PROG -x \
+       -c 'bmap -elpv' \
+       -c 'startupdate -e' \
+       -c 'truncate 1m' \
+       -c 'pwrite -S 0x59 64k 64k' \
+       -c 'pwrite -S 0x57 768k 64k' \
+       -c 'sync' \
+       -c 'bmap -elpv' \
+       -c 'inject bmap_finish_one' \
+       -c 'commitupdate -h -k' \
+       $SCRATCH_MNT/a >> $seqres.full
+_scratch_cycle_mount
+
+$XFS_IO_PROG -c 'bmap -elpv' $SCRATCH_MNT/a >> $seqres.full
+md5sum $SCRATCH_MNT/a | _filter_scratch
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/792.out b/tests/xfs/792.out
new file mode 100644 (file)
index 0000000..0c242dd
--- /dev/null
@@ -0,0 +1,5 @@
+QA output created by 792
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/a
+commit
+committing update: Input/output error
+e9cbfe8489a68efaa5fcf40cf3106118  SCRATCH_MNT/a
diff --git a/tests/xfs/793 b/tests/xfs/793
new file mode 100755 (executable)
index 0000000..961c33e
--- /dev/null
@@ -0,0 +1,48 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 793
+#
+# Race fsstress and realtime summary repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_realtime
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_require_xfs_has_feature "$SCRATCH_MNT" realtime
+_xfs_force_bdev realtime $SCRATCH_MNT
+
+# XXX the realtime summary scrubber isn't currently implemented upstream.
+# Don't bother trying to fix it on those kernels
+$XFS_IO_PROG -c 'scrub rtsummary' -c 'scrub rtsummary' "$SCRATCH_MNT" 2>&1 | \
+       grep -q 'Scan was not complete' && \
+       _notrun "rtsummary scrub is incomplete"
+
+_scratch_xfs_stress_online_repair -s "repair rtsummary"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/793.out b/tests/xfs/793.out
new file mode 100644 (file)
index 0000000..e8a17d4
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 793
+Silence is golden
diff --git a/tests/xfs/794 b/tests/xfs/794
new file mode 100755 (executable)
index 0000000..eeb1ceb
--- /dev/null
@@ -0,0 +1,40 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 794
+#
+# Race fsstress and extended attributes repair for a while to see if we crash
+# or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+. ./common/attr
+
+# real QA test starts here
+_supported_fs xfs
+_require_attrs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -x 'xattr' -s "repair xattr" -t "%attrfile%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/794.out b/tests/xfs/794.out
new file mode 100644 (file)
index 0000000..bc999c0
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 794
+Silence is golden
diff --git a/tests/xfs/795 b/tests/xfs/795
new file mode 100755 (executable)
index 0000000..a381db3
--- /dev/null
@@ -0,0 +1,77 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 795
+#
+# Ensure that the sysadmin won't hit EDQUOT while repairing file data contents
+# even if the file's quota limits have been exceeded.  This tests the quota
+# reservation handling inside the swapext code used by repair.
+#
+. ./common/preamble
+_begin_fstest online_repair
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/quota
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_quota
+_require_user
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_qmount_option usrquota
+_qmount
+
+blocksize=$(_get_block_size $SCRATCH_MNT)
+alloc_unit=$(_xfs_get_dir_blocksize $SCRATCH_MNT)
+
+# Make sure we can actually repair a directory
+scratchdir=$SCRATCH_MNT/dir
+scratchfile=$SCRATCH_MNT/file
+mkdir $scratchdir
+touch $scratchfile
+$XFS_IO_PROG -x -c 'inject force_repair' $SCRATCH_MNT
+__stress_scrub_check_commands "$scratchdir" "" 'repair directory'
+
+# Create a 2-dirblock directory
+total_size=$((alloc_unit * 2))
+dirents=$((total_size / 255))
+
+for ((i = 0; i < dirents; i++)); do
+       name=$(printf "x%0254d" $i)
+       ln $scratchfile $scratchdir/$name
+done
+
+# Set a low quota hardlimit for an unprivileged uid and chown the files to it
+echo "set up quota" >> $seqres.full
+$XFS_QUOTA_PROG -x -c "limit -u bhard=$alloc_unit $qa_user" $SCRATCH_MNT
+chown $qa_user $scratchdir $scratchfile
+$XFS_QUOTA_PROG -x -c 'report -u' $SCRATCH_MNT >> $seqres.full
+
+# Rebuild the directory
+echo "repairs" >> $seqres.full
+$XFS_IO_PROG -x -c 'inject force_repair' -c 'repair directory' $scratchdir
+$XFS_QUOTA_PROG -x -c 'report -u' $SCRATCH_MNT >> $seqres.full
+
+# Fail at appending the directory as qa_user to ensure quota enforcement works
+echo "fail quota" >> $seqres.full
+for ((i = 0; i < dirents; i++)); do
+       name=$(printf "y%0254d" $i)
+       su - "$qa_user" -c "ln $scratchfile $scratchdir/$name" 2>&1 | \
+               _filter_scratch | sed -e 's/y[0-9]*/yXXX/g'
+       test "${PIPESTATUS[0]}" -ne 0 && break
+done
+$XFS_QUOTA_PROG -x -c 'report -u' $SCRATCH_MNT >> $seqres.full
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/795.out b/tests/xfs/795.out
new file mode 100644 (file)
index 0000000..9181995
--- /dev/null
@@ -0,0 +1,3 @@
+QA output created by 795
+ln: failed to create hard link 'SCRATCH_MNT/dir/yXXX': Disk quota exceeded
+Silence is golden
diff --git a/tests/xfs/796 b/tests/xfs/796
new file mode 100755 (executable)
index 0000000..907e9b5
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 796
+#
+# Race fsstress and directory repair for a while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -x 'dir' -s "repair directory" -t "%dir%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/796.out b/tests/xfs/796.out
new file mode 100644 (file)
index 0000000..374e377
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 796
+Silence is golden
diff --git a/tests/xfs/797 b/tests/xfs/797
new file mode 100755 (executable)
index 0000000..0811780
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle. Inc.  All Rights Reserved.
+#
+# FS QA Test No. 797
+#
+# Race fsstress and parent pointers repair for a while to see if we crash or
+# livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       cd /
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -s "repair parent" -t "%dir%"
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/797.out b/tests/xfs/797.out
new file mode 100644 (file)
index 0000000..6b64f4b
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 797
+Silence is golden
diff --git a/tests/xfs/798 b/tests/xfs/798
new file mode 100755 (executable)
index 0000000..03e94e8
--- /dev/null
@@ -0,0 +1,105 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 798
+#
+# Make sure that xfs_scrub dry run, preen, and repair modes only modify the
+# things that they're allowed to touch.
+#
+. ./common/preamble
+_begin_fstest auto quick online_repair
+
+# Import common functions.
+. ./common/fuzzy
+. ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs xfs
+_require_scratch_nocheck
+_require_scrub
+_require_xfs_db_command "fuzz"
+_require_xfs_io_command "repair"
+
+# Make sure we support repair?
+output="$($XFS_IO_PROG -x -c 'repair -R probe' $SCRATCH_MNT 2>&1)"
+test -z "$output" && _notrun 'kernel does not support repair'
+
+# Make sure xfs_scrub is new enough to support -p(reen)
+$XFS_SCRUB_PROG -p 2>&1 | grep -q 'invalid option' && \
+       _notrun 'scrub does not support -p'
+
+_scratch_mkfs | _filter_mkfs 2>$tmp.mkfs >/dev/null
+. $tmp.mkfs
+
+test $agcount -ge 3 || _notrun 'filesystem must have at least 3 AGs'
+
+AWK_PROG='
+{
+       if ($1 ~ /Optimized:/)
+               optimized++;
+       else if ($1 ~ /Repaired:/)
+               repaired++;
+       else if ($1 ~ /Corruption:/)
+               corruption++;
+}
+END {
+       printf("corruption: %u optimized: %u repaired: %u\n",
+                       corruption, optimized, repaired);
+}
+'
+
+test_scrub() {
+       local mode="$1"
+       local scrub_arg="$2"
+       local db_args=(-x)
+
+       # Fuzz secondary superblocks because this won't cause mount failures
+       if [[ $mode =~ c ]]; then
+               db_args+=(-c 'sb 1' -c 'fuzz -d dblocks add')
+       fi
+       if [[ $mode =~ o ]]; then
+               db_args+=(-c 'sb 2' -c 'fuzz -d fname random')
+       fi
+
+       echo "testing mode? $mode scrub_arg $scrub_arg"
+       echo "db_args:${db_args[@]}:scrub_arg:$scrub_arg:$mode:" >> $seqres.full
+       echo "----------------" >> $seqres.full
+
+       _scratch_mkfs >> $seqres.full
+
+       # Make sure there's nothing left to optimize, at least according to
+       # xfs_scrub.  This clears the way for us to make targeted changes to
+       # the filesystem.
+       _scratch_mount
+       _scratch_scrub $scrub_arg >> /dev/null
+       _scratch_unmount
+
+       # Modify the filesystem as needed to trip up xfs_scrub
+       _scratch_xfs_db "${db_args[@]}" >> $seqres.full
+
+       # See how many optimizations, repairs, and corruptions it'll report
+       _scratch_mount
+       _scratch_scrub $scrub_arg 2>&1 | awk "$AWK_PROG"
+       test "${PIPESTATUS[0]}" -eq 0 || echo "xfs_scrub returned ${PIPESTATUS[0]}?"
+       echo
+       _scratch_unmount
+}
+
+test_scrub 'o' -n      # dry run with possible optimizations
+test_scrub 'o' -p      # preen
+test_scrub 'o'                 # repair
+
+test_scrub 'co' -n     # dry run with corruptions and optimizations
+test_scrub 'co' -p     # preen
+test_scrub 'co'        # repair
+
+test_scrub 'c' -n      # dry run with corruptions
+test_scrub 'c' -p      # preen
+test_scrub 'c'                 # repair
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/798.out b/tests/xfs/798.out
new file mode 100644 (file)
index 0000000..a02340c
--- /dev/null
@@ -0,0 +1,32 @@
+QA output created by 798
+testing mode? o scrub_arg -n
+corruption: 0 optimized: 0 repaired: 0
+
+testing mode? o scrub_arg -p
+corruption: 0 optimized: 1 repaired: 0
+
+testing mode? o scrub_arg 
+corruption: 0 optimized: 1 repaired: 0
+
+testing mode? co scrub_arg -n
+corruption: 1 optimized: 0 repaired: 0
+xfs_scrub returned 1?
+
+testing mode? co scrub_arg -p
+corruption: 1 optimized: 0 repaired: 0
+xfs_scrub returned 1?
+
+testing mode? co scrub_arg 
+corruption: 0 optimized: 1 repaired: 1
+
+testing mode? c scrub_arg -n
+corruption: 1 optimized: 0 repaired: 0
+xfs_scrub returned 1?
+
+testing mode? c scrub_arg -p
+corruption: 1 optimized: 0 repaired: 0
+xfs_scrub returned 1?
+
+testing mode? c scrub_arg 
+corruption: 0 optimized: 0 repaired: 1
+
diff --git a/tests/xfs/799 b/tests/xfs/799
new file mode 100755 (executable)
index 0000000..4391686
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 799
+#
+# Race fsstress doing mostly renames and xfs_scrub in force-repair mode for a
+# while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest online_repair dangerous_fsstress_repair
+
+_cleanup() {
+       cd /
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_online_repair
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_online_repair -S '-k' -x 'parent'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/799.out b/tests/xfs/799.out
new file mode 100644 (file)
index 0000000..f3fd9fa
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 799
+Silence is golden
diff --git a/tests/xfs/800 b/tests/xfs/800
new file mode 100755 (executable)
index 0000000..a23e473
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Oracle, Inc.  All Rights Reserved.
+#
+# FS QA Test No. 800
+#
+# Race fsstress doing mostly renames and xfs_scrub in read-only mode for a
+# while to see if we crash or livelock.
+#
+. ./common/preamble
+_begin_fstest scrub dangerous_fsstress_scrub
+
+_cleanup() {
+       cd /
+       _scratch_xfs_stress_scrub_cleanup &> /dev/null
+       rm -r -f $tmp.*
+}
+_register_cleanup "_cleanup" BUS
+
+# Import common functions.
+. ./common/filter
+. ./common/fuzzy
+. ./common/inject
+. ./common/xfs
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch
+_require_xfs_stress_scrub
+
+_scratch_mkfs > "$seqres.full" 2>&1
+_scratch_mount
+_scratch_xfs_stress_scrub -S '-n' -x 'parent'
+
+# success, all done
+echo Silence is golden
+status=0
+exit
diff --git a/tests/xfs/800.out b/tests/xfs/800.out
new file mode 100644 (file)
index 0000000..bdfaa2c
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 800
+Silence is golden
index 5f413e67a4c80c3c8734e4b922e3d90567f5d4b3..6bfafdb16393a42b821a3ac47a311f27a36401e9 100644 (file)
@@ -14,7 +14,7 @@ default: $(DIRT)
 
 include $(BUILDRULES)
 
-install:
+install: default
        $(INSTALL) -m 755 -d $(TARGET_DIR)
        $(INSTALL) -m 755 $(TESTS) $(TARGET_DIR)
        $(INSTALL) -m 644 group.list $(TARGET_DIR)
diff --git a/tools/get_maintainer.pl b/tools/get_maintainer.pl
new file mode 100755 (executable)
index 0000000..671ffe2
--- /dev/null
@@ -0,0 +1,2573 @@
+#!/usr/bin/env perl
+# SPDX-License-Identifier: GPL-2.0
+#
+# This script is copied from linux/scripts/get_maintainer.pl, then
+# changes for fstests specifically.
+#
+# Print selected MAINTAINERS information for
+# the files modified in a patch or for a file
+#
+# usage: perl tools/get_maintainer.pl [OPTIONS] <patch>
+#        perl tools/get_maintainer.pl [OPTIONS] -f <file>
+
+use warnings;
+use strict;
+
+my $P = $0;
+my $V = '0.26';
+
+use Getopt::Long qw(:config no_auto_abbrev);
+use Cwd;
+use File::Find;
+use File::Spec::Functions;
+
+my $cur_path = fastgetcwd() . '/';
+my $lk_path = "./";
+my $email = 1;
+my $email_usename = 1;
+my $email_maintainer = 1;
+my $email_reviewer = 1;
+my $email_fixes = 1;
+my $email_list = 1;
+my $email_moderated_list = 1;
+my $email_subscriber_list = 0;
+my $email_git = 0;
+my $email_git_all_signature_types = 0;
+my $email_git_blame = 0;
+my $email_git_blame_signatures = 1;
+my $email_git_fallback = 1;
+my $email_git_min_signatures = 1;
+my $email_git_max_maintainers = 5;
+my $email_git_min_percent = 5;
+my $email_git_since = "1-year-ago";
+my $email_hg_since = "-365";
+my $interactive = 0;
+my $email_remove_duplicates = 1;
+my $email_use_mailmap = 1;
+my $output_multiline = 1;
+my $output_separator = ", ";
+my $output_roles = 0;
+my $output_rolestats = 1;
+my $output_section_maxlen = 50;
+my $scm = 0;
+my $tree = 1;
+my $web = 0;
+my $subsystem = 0;
+my $status = 0;
+my $letters = "";
+my $keywords = 1;
+my $sections = 0;
+my $email_file_emails = 0;
+my $from_filename = 0;
+my $pattern_depth = 0;
+my $self_test = undef;
+my $version = 0;
+my $help = 0;
+my $find_maintainer_files = 0;
+my $maintainer_path;
+my $vcs_used = 0;
+
+my $exit = 0;
+
+my @files = ();
+my @fixes = ();                        # If a patch description includes Fixes: lines
+my @range = ();
+my @keyword_tvi = ();
+my @file_emails = ();
+
+my %commit_author_hash;
+my %commit_signer_hash;
+
+# Signature types of people who are either
+#      a) responsible for the code in question, or
+#      b) familiar enough with it to give relevant feedback
+my @signature_tags = ();
+push(@signature_tags, "Signed-off-by:");
+push(@signature_tags, "Reviewed-by:");
+push(@signature_tags, "Acked-by:");
+
+my $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
+
+# rfc822 email address - preloaded methods go here.
+my $rfc822_lwsp = "(?:(?:\\r\\n)?[ \\t])";
+my $rfc822_char = '[\\000-\\377]';
+
+# VCS command support: class-like functions and strings
+
+my %VCS_cmds;
+
+my %VCS_cmds_git = (
+    "execute_cmd" => \&git_execute_cmd,
+    "available" => '(which("git") ne "") && (-e ".git")',
+    "find_signers_cmd" =>
+       "git log --no-color --follow --since=\$email_git_since " .
+           '--numstat --no-merges ' .
+           '--format="GitCommit: %H%n' .
+                     'GitAuthor: %an <%ae>%n' .
+                     'GitDate: %aD%n' .
+                     'GitSubject: %s%n' .
+                     '%b%n"' .
+           " -- \$file",
+    "find_commit_signers_cmd" =>
+       "git log --no-color " .
+           '--numstat ' .
+           '--format="GitCommit: %H%n' .
+                     'GitAuthor: %an <%ae>%n' .
+                     'GitDate: %aD%n' .
+                     'GitSubject: %s%n' .
+                     '%b%n"' .
+           " -1 \$commit",
+    "find_commit_author_cmd" =>
+       "git log --no-color " .
+           '--numstat ' .
+           '--format="GitCommit: %H%n' .
+                     'GitAuthor: %an <%ae>%n' .
+                     'GitDate: %aD%n' .
+                     'GitSubject: %s%n"' .
+           " -1 \$commit",
+    "blame_range_cmd" => "git blame -l -L \$diff_start,+\$diff_length \$file",
+    "blame_file_cmd" => "git blame -l \$file",
+    "commit_pattern" => "^GitCommit: ([0-9a-f]{40,40})",
+    "blame_commit_pattern" => "^([0-9a-f]+) ",
+    "author_pattern" => "^GitAuthor: (.*)",
+    "subject_pattern" => "^GitSubject: (.*)",
+    "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$",
+    "file_exists_cmd" => "git ls-files \$file",
+    "list_files_cmd" => "git ls-files \$file",
+);
+
+my %VCS_cmds_hg = (
+    "execute_cmd" => \&hg_execute_cmd,
+    "available" => '(which("hg") ne "") && (-d ".hg")',
+    "find_signers_cmd" =>
+       "hg log --date=\$email_hg_since " .
+           "--template='HgCommit: {node}\\n" .
+                       "HgAuthor: {author}\\n" .
+                       "HgSubject: {desc}\\n'" .
+           " -- \$file",
+    "find_commit_signers_cmd" =>
+       "hg log " .
+           "--template='HgSubject: {desc}\\n'" .
+           " -r \$commit",
+    "find_commit_author_cmd" =>
+       "hg log " .
+           "--template='HgCommit: {node}\\n" .
+                       "HgAuthor: {author}\\n" .
+                       "HgSubject: {desc|firstline}\\n'" .
+           " -r \$commit",
+    "blame_range_cmd" => "",           # not supported
+    "blame_file_cmd" => "hg blame -n \$file",
+    "commit_pattern" => "^HgCommit: ([0-9a-f]{40,40})",
+    "blame_commit_pattern" => "^([ 0-9a-f]+):",
+    "author_pattern" => "^HgAuthor: (.*)",
+    "subject_pattern" => "^HgSubject: (.*)",
+    "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$",
+    "file_exists_cmd" => "hg files \$file",
+    "list_files_cmd" => "hg manifest -R \$file",
+);
+
+my $conf = which_conf(".get_maintainer.conf");
+if (-f $conf) {
+    my @conf_args;
+    open(my $conffile, '<', "$conf")
+       or warn "$P: Can't find a readable .get_maintainer.conf file $!\n";
+
+    while (<$conffile>) {
+       my $line = $_;
+
+       $line =~ s/\s*\n?$//g;
+       $line =~ s/^\s*//g;
+       $line =~ s/\s+/ /g;
+
+       next if ($line =~ m/^\s*#/);
+       next if ($line =~ m/^\s*$/);
+
+       my @words = split(" ", $line);
+       foreach my $word (@words) {
+           last if ($word =~ m/^#/);
+           push (@conf_args, $word);
+       }
+    }
+    close($conffile);
+    unshift(@ARGV, @conf_args) if @conf_args;
+}
+
+my @ignore_emails = ();
+my $ignore_file = which_conf(".get_maintainer.ignore");
+if (-f $ignore_file) {
+    open(my $ignore, '<', "$ignore_file")
+       or warn "$P: Can't find a readable .get_maintainer.ignore file $!\n";
+    while (<$ignore>) {
+       my $line = $_;
+
+       $line =~ s/\s*\n?$//;
+       $line =~ s/^\s*//;
+       $line =~ s/\s+$//;
+       $line =~ s/#.*$//;
+
+       next if ($line =~ m/^\s*$/);
+       if (rfc822_valid($line)) {
+           push(@ignore_emails, $line);
+       }
+    }
+    close($ignore);
+}
+
+if ($#ARGV > 0) {
+    foreach (@ARGV) {
+        if ($_ =~ /^-{1,2}self-test(?:=|$)/) {
+            die "$P: using --self-test does not allow any other option or argument\n";
+        }
+    }
+}
+
+if (!GetOptions(
+               'email!' => \$email,
+               'git!' => \$email_git,
+               'git-all-signature-types!' => \$email_git_all_signature_types,
+               'git-blame!' => \$email_git_blame,
+               'git-blame-signatures!' => \$email_git_blame_signatures,
+               'git-fallback!' => \$email_git_fallback,
+               'git-min-signatures=i' => \$email_git_min_signatures,
+               'git-max-maintainers=i' => \$email_git_max_maintainers,
+               'git-min-percent=i' => \$email_git_min_percent,
+               'git-since=s' => \$email_git_since,
+               'hg-since=s' => \$email_hg_since,
+               'i|interactive!' => \$interactive,
+               'remove-duplicates!' => \$email_remove_duplicates,
+               'mailmap!' => \$email_use_mailmap,
+               'm!' => \$email_maintainer,
+               'r!' => \$email_reviewer,
+               'n!' => \$email_usename,
+               'l!' => \$email_list,
+               'fixes!' => \$email_fixes,
+               'moderated!' => \$email_moderated_list,
+               's!' => \$email_subscriber_list,
+               'multiline!' => \$output_multiline,
+               'roles!' => \$output_roles,
+               'rolestats!' => \$output_rolestats,
+               'separator=s' => \$output_separator,
+               'subsystem!' => \$subsystem,
+               'status!' => \$status,
+               'scm!' => \$scm,
+               'tree!' => \$tree,
+               'web!' => \$web,
+               'letters=s' => \$letters,
+               'pattern-depth=i' => \$pattern_depth,
+               'k|keywords!' => \$keywords,
+               'sections!' => \$sections,
+               'fe|file-emails!' => \$email_file_emails,
+               'f|file' => \$from_filename,
+               'find-maintainer-files' => \$find_maintainer_files,
+               'mpath|maintainer-path=s' => \$maintainer_path,
+               'self-test:s' => \$self_test,
+               'v|version' => \$version,
+               'h|help|usage' => \$help,
+               )) {
+    die "$P: invalid argument - use --help if necessary\n";
+}
+
+if ($help != 0) {
+    usage();
+    exit 0;
+}
+
+if ($version != 0) {
+    print("${P} ${V}\n");
+    exit 0;
+}
+
+if (defined $self_test) {
+    read_all_maintainer_files();
+    self_test();
+    exit 0;
+}
+
+if (-t STDIN && !@ARGV) {
+    # We're talking to a terminal, but have no command line arguments.
+    die "$P: missing patchfile or -f file - use --help if necessary\n";
+}
+
+$output_multiline = 0 if ($output_separator ne ", ");
+$output_rolestats = 1 if ($interactive);
+$output_roles = 1 if ($output_rolestats);
+
+if ($sections || $letters ne "") {
+    $sections = 1;
+    $email = 0;
+    $email_list = 0;
+    $scm = 0;
+    $status = 0;
+    $subsystem = 0;
+    $web = 0;
+    $keywords = 0;
+    $interactive = 0;
+} else {
+    my $selections = $email + $scm + $status + $subsystem + $web;
+    if ($selections == 0) {
+       die "$P:  Missing required option: email, scm, status, subsystem or web\n";
+    }
+}
+
+if ($email &&
+    ($email_maintainer + $email_reviewer +
+     $email_list + $email_subscriber_list +
+     $email_git + $email_git_blame) == 0) {
+    die "$P: Please select at least 1 email option\n";
+}
+
+if ($tree && !top_of_fstests_tree($lk_path)) {
+    die "$P: The current directory does not appear to be "
+       . "a fstests source tree.\n";
+}
+
+## Read MAINTAINERS for type/value pairs
+
+my @typevalue = ();
+my %keyword_hash;
+my @mfiles = ();
+my @self_test_info = ();
+
+sub read_maintainer_file {
+    my ($file) = @_;
+
+    open (my $maint, '<', "$file")
+       or die "$P: Can't open MAINTAINERS file '$file': $!\n";
+    my $i = 1;
+    while (<$maint>) {
+       my $line = $_;
+       chomp $line;
+
+       if ($line =~ m/^([A-Z]):\s*(.*)/) {
+           my $type = $1;
+           my $value = $2;
+
+           ##Filename pattern matching
+           if ($type eq "F" || $type eq "X") {
+               $value =~ s@\.@\\\.@g;       ##Convert . to \.
+               $value =~ s/\*/\.\*/g;       ##Convert * to .*
+               $value =~ s/\?/\./g;         ##Convert ? to .
+               ##if pattern is a directory and it lacks a trailing slash, add one
+               if ((-d $value)) {
+                   $value =~ s@([^/])$@$1/@;
+               }
+           } elsif ($type eq "K") {
+               $keyword_hash{@typevalue} = $value;
+           }
+           push(@typevalue, "$type:$value");
+       } elsif (!(/^\s*$/ || /^\s*\#/)) {
+           push(@typevalue, $line);
+       }
+       if (defined $self_test) {
+           push(@self_test_info, {file=>$file, linenr=>$i, line=>$line});
+       }
+       $i++;
+    }
+    close($maint);
+}
+
+sub find_is_maintainer_file {
+    my ($file) = $_;
+    return if ($file !~ m@/MAINTAINERS$@);
+    $file = $File::Find::name;
+    return if (! -f $file);
+    push(@mfiles, $file);
+}
+
+sub find_ignore_git {
+    return grep { $_ !~ /^\.git$/; } @_;
+}
+
+read_all_maintainer_files();
+
+sub read_all_maintainer_files {
+    my $path = "${lk_path}MAINTAINERS";
+    if (defined $maintainer_path) {
+       $path = $maintainer_path;
+       # Perl Cookbook tilde expansion if necessary
+       $path =~ s@^~([^/]*)@ $1 ? (getpwnam($1))[7] : ( $ENV{HOME} || $ENV{LOGDIR} || (getpwuid($<))[7])@ex;
+    }
+
+    if (-d $path) {
+       $path .= '/' if ($path !~ m@/$@);
+       if ($find_maintainer_files) {
+           find( { wanted => \&find_is_maintainer_file,
+                   preprocess => \&find_ignore_git,
+                   no_chdir => 1,
+               }, "$path");
+       } else {
+           opendir(DIR, "$path") or die $!;
+           my @files = readdir(DIR);
+           closedir(DIR);
+           foreach my $file (@files) {
+               push(@mfiles, "$path$file") if ($file !~ /^\./);
+           }
+       }
+    } elsif (-f "$path") {
+       push(@mfiles, "$path");
+    } else {
+       die "$P: MAINTAINER file not found '$path'\n";
+    }
+    die "$P: No MAINTAINER files found in '$path'\n" if (scalar(@mfiles) == 0);
+    foreach my $file (@mfiles) {
+       read_maintainer_file("$file");
+    }
+}
+
+sub maintainers_in_file {
+    my ($file) = @_;
+
+    return if ($file =~ m@\bMAINTAINERS$@);
+
+    if (-f $file && ($email_file_emails || $file =~ /\.yaml$/)) {
+       open(my $f, '<', $file)
+           or die "$P: Can't open $file: $!\n";
+       my $text = do { local($/) ; <$f> };
+       close($f);
+
+       my @poss_addr = $text =~ m$[A-Za-zÀ-ÿ\"\' \,\.\+-]*\s*[\,]*\s*[\(\<\{]{0,1}[A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+\.[A-Za-z0-9]+[\)\>\}]{0,1}$g;
+       push(@file_emails, clean_file_emails(@poss_addr));
+    }
+}
+
+#
+# Read mail address map
+#
+
+my $mailmap;
+
+read_mailmap();
+
+sub read_mailmap {
+    $mailmap = {
+       names => {},
+       addresses => {}
+    };
+
+    return if (!$email_use_mailmap || !(-f "${lk_path}.mailmap"));
+
+    open(my $mailmap_file, '<', "${lk_path}.mailmap")
+       or warn "$P: Can't open .mailmap: $!\n";
+
+    while (<$mailmap_file>) {
+       s/#.*$//; #strip comments
+       s/^\s+|\s+$//g; #trim
+
+       next if (/^\s*$/); #skip empty lines
+       #entries have one of the following formats:
+       # name1 <mail1>
+       # <mail1> <mail2>
+       # name1 <mail1> <mail2>
+       # name1 <mail1> name2 <mail2>
+       # (see man git-shortlog)
+
+       if (/^([^<]+)<([^>]+)>$/) {
+           my $real_name = $1;
+           my $address = $2;
+
+           $real_name =~ s/\s+$//;
+           ($real_name, $address) = parse_email("$real_name <$address>");
+           $mailmap->{names}->{$address} = $real_name;
+
+       } elsif (/^<([^>]+)>\s*<([^>]+)>$/) {
+           my $real_address = $1;
+           my $wrong_address = $2;
+
+           $mailmap->{addresses}->{$wrong_address} = $real_address;
+
+       } elsif (/^(.+)<([^>]+)>\s*<([^>]+)>$/) {
+           my $real_name = $1;
+           my $real_address = $2;
+           my $wrong_address = $3;
+
+           $real_name =~ s/\s+$//;
+           ($real_name, $real_address) =
+               parse_email("$real_name <$real_address>");
+           $mailmap->{names}->{$wrong_address} = $real_name;
+           $mailmap->{addresses}->{$wrong_address} = $real_address;
+
+       } elsif (/^(.+)<([^>]+)>\s*(.+)\s*<([^>]+)>$/) {
+           my $real_name = $1;
+           my $real_address = $2;
+           my $wrong_name = $3;
+           my $wrong_address = $4;
+
+           $real_name =~ s/\s+$//;
+           ($real_name, $real_address) =
+               parse_email("$real_name <$real_address>");
+
+           $wrong_name =~ s/\s+$//;
+           ($wrong_name, $wrong_address) =
+               parse_email("$wrong_name <$wrong_address>");
+
+           my $wrong_email = format_email($wrong_name, $wrong_address, 1);
+           $mailmap->{names}->{$wrong_email} = $real_name;
+           $mailmap->{addresses}->{$wrong_email} = $real_address;
+       }
+    }
+    close($mailmap_file);
+}
+
+## use the filenames on the command line or find the filenames in the patchfiles
+
+if (!@ARGV) {
+    push(@ARGV, "&STDIN");
+}
+
+foreach my $file (@ARGV) {
+    if ($file ne "&STDIN") {
+       $file = canonpath($file);
+       ##if $file is a directory and it lacks a trailing slash, add one
+       if ((-d $file)) {
+           $file =~ s@([^/])$@$1/@;
+       } elsif (!(-f $file)) {
+           die "$P: file '${file}' not found\n";
+       }
+    }
+    if ($from_filename && (vcs_exists() && !vcs_file_exists($file))) {
+       warn "$P: file '$file' not found in version control $!\n";
+    }
+    if ($from_filename || ($file ne "&STDIN" && vcs_file_exists($file))) {
+       $file =~ s/^\Q${cur_path}\E//;  #strip any absolute path
+       $file =~ s/^\Q${lk_path}\E//;   #or the path to the lk tree
+       push(@files, $file);
+       if ($file ne "MAINTAINERS" && -f $file && $keywords) {
+           open(my $f, '<', $file)
+               or die "$P: Can't open $file: $!\n";
+           my $text = do { local($/) ; <$f> };
+           close($f);
+           if ($keywords) {
+               foreach my $line (keys %keyword_hash) {
+                   if ($text =~ m/$keyword_hash{$line}/x) {
+                       push(@keyword_tvi, $line);
+                   }
+               }
+           }
+       }
+    } else {
+       my $file_cnt = @files;
+       my $lastfile;
+
+       open(my $patch, "< $file")
+           or die "$P: Can't open $file: $!\n";
+
+       # We can check arbitrary information before the patch
+       # like the commit message, mail headers, etc...
+       # This allows us to match arbitrary keywords against any part
+       # of a git format-patch generated file (subject tags, etc...)
+
+       my $patch_prefix = "";                  #Parsing the intro
+
+       while (<$patch>) {
+           my $patch_line = $_;
+           if (m/^ mode change [0-7]+ => [0-7]+ (\S+)\s*$/) {
+               my $filename = $1;
+               push(@files, $filename);
+           } elsif (m/^rename (?:from|to) (\S+)\s*$/) {
+               my $filename = $1;
+               push(@files, $filename);
+           } elsif (m/^diff --git a\/(\S+) b\/(\S+)\s*$/) {
+               my $filename1 = $1;
+               my $filename2 = $2;
+               push(@files, $filename1);
+               push(@files, $filename2);
+           } elsif (m/^Fixes:\s+([0-9a-fA-F]{6,40})/) {
+               push(@fixes, $1) if ($email_fixes);
+           } elsif (m/^\+\+\+\s+(\S+)/ or m/^---\s+(\S+)/) {
+               my $filename = $1;
+               $filename =~ s@^[^/]*/@@;
+               $filename =~ s@\n@@;
+               $lastfile = $filename;
+               push(@files, $filename);
+               $patch_prefix = "^[+-].*";      #Now parsing the actual patch
+           } elsif (m/^\@\@ -(\d+),(\d+)/) {
+               if ($email_git_blame) {
+                   push(@range, "$lastfile:$1:$2");
+               }
+           } elsif ($keywords) {
+               foreach my $line (keys %keyword_hash) {
+                   if ($patch_line =~ m/${patch_prefix}$keyword_hash{$line}/x) {
+                       push(@keyword_tvi, $line);
+                   }
+               }
+           }
+       }
+       close($patch);
+
+       if ($file_cnt == @files) {
+           warn "$P: file '${file}' doesn't appear to be a patch.  "
+               . "Add -f to options?\n";
+       }
+       @files = sort_and_uniq(@files);
+    }
+}
+
+@file_emails = uniq(@file_emails);
+@fixes = uniq(@fixes);
+
+my %email_hash_name;
+my %email_hash_address;
+my @email_to = ();
+my %hash_list_to;
+my @list_to = ();
+my @scm = ();
+my @web = ();
+my @subsystem = ();
+my @status = ();
+my %deduplicate_name_hash = ();
+my %deduplicate_address_hash = ();
+
+my @maintainers = get_maintainers();
+if (@maintainers) {
+    @maintainers = merge_email(@maintainers);
+    output(@maintainers);
+}
+
+if ($scm) {
+    @scm = uniq(@scm);
+    output(@scm);
+}
+
+if ($status) {
+    @status = uniq(@status);
+    output(@status);
+}
+
+if ($subsystem) {
+    @subsystem = uniq(@subsystem);
+    output(@subsystem);
+}
+
+if ($web) {
+    @web = uniq(@web);
+    output(@web);
+}
+
+exit($exit);
+
+sub self_test {
+    my @lsfiles = ();
+    my @good_links = ();
+    my @bad_links = ();
+    my @section_headers = ();
+    my $index = 0;
+
+    @lsfiles = vcs_list_files($lk_path);
+
+    for my $x (@self_test_info) {
+       $index++;
+
+       ## Section header duplication and missing section content
+       if (($self_test eq "" || $self_test =~ /\bsections\b/) &&
+           $x->{line} =~ /^\S[^:]/ &&
+           defined $self_test_info[$index] &&
+           $self_test_info[$index]->{line} =~ /^([A-Z]):\s*\S/) {
+           my $has_S = 0;
+           my $has_F = 0;
+           my $has_ML = 0;
+           my $status = "";
+           if (grep(m@^\Q$x->{line}\E@, @section_headers)) {
+               print("$x->{file}:$x->{linenr}: warning: duplicate section header\t$x->{line}\n");
+           } else {
+               push(@section_headers, $x->{line});
+           }
+           my $nextline = $index;
+           while (defined $self_test_info[$nextline] &&
+                  $self_test_info[$nextline]->{line} =~ /^([A-Z]):\s*(\S.*)/) {
+               my $type = $1;
+               my $value = $2;
+               if ($type eq "S") {
+                   $has_S = 1;
+                   $status = $value;
+               } elsif ($type eq "F" || $type eq "N") {
+                   $has_F = 1;
+               } elsif ($type eq "M" || $type eq "R" || $type eq "L") {
+                   $has_ML = 1;
+               }
+               $nextline++;
+           }
+           if (!$has_ML && $status !~ /orphan|obsolete/i) {
+               print("$x->{file}:$x->{linenr}: warning: section without email address\t$x->{line}\n");
+           }
+           if (!$has_S) {
+               print("$x->{file}:$x->{linenr}: warning: section without status \t$x->{line}\n");
+           }
+           if (!$has_F) {
+               print("$x->{file}:$x->{linenr}: warning: section without file pattern\t$x->{line}\n");
+           }
+       }
+
+       next if ($x->{line} !~ /^([A-Z]):\s*(.*)/);
+
+       my $type = $1;
+       my $value = $2;
+
+       ## Filename pattern matching
+       if (($type eq "F" || $type eq "X") &&
+           ($self_test eq "" || $self_test =~ /\bpatterns\b/)) {
+           $value =~ s@\.@\\\.@g;       ##Convert . to \.
+           $value =~ s/\*/\.\*/g;       ##Convert * to .*
+           $value =~ s/\?/\./g;         ##Convert ? to .
+           ##if pattern is a directory and it lacks a trailing slash, add one
+           if ((-d $value)) {
+               $value =~ s@([^/])$@$1/@;
+           }
+           if (!grep(m@^$value@, @lsfiles)) {
+               print("$x->{file}:$x->{linenr}: warning: no file matches\t$x->{line}\n");
+           }
+
+       ## Link reachability
+       } elsif (($type eq "W" || $type eq "Q" || $type eq "B") &&
+                $value =~ /^https?:/ &&
+                ($self_test eq "" || $self_test =~ /\blinks\b/)) {
+           next if (grep(m@^\Q$value\E$@, @good_links));
+           my $isbad = 0;
+           if (grep(m@^\Q$value\E$@, @bad_links)) {
+               $isbad = 1;
+           } else {
+               my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $value`;
+               if ($? == 0) {
+                   push(@good_links, $value);
+               } else {
+                   push(@bad_links, $value);
+                   $isbad = 1;
+               }
+           }
+           if ($isbad) {
+               print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n");
+           }
+
+       ## SCM reachability
+       } elsif ($type eq "T" &&
+                ($self_test eq "" || $self_test =~ /\bscm\b/)) {
+           next if (grep(m@^\Q$value\E$@, @good_links));
+           my $isbad = 0;
+           if (grep(m@^\Q$value\E$@, @bad_links)) {
+               $isbad = 1;
+            } elsif ($value !~ /^(?:git|quilt|hg)\s+\S/) {
+               print("$x->{file}:$x->{linenr}: warning: malformed entry\t$x->{line}\n");
+           } elsif ($value =~ /^git\s+(\S+)(\s+([^\(]+\S+))?/) {
+               my $url = $1;
+               my $branch = "";
+               $branch = $3 if $3;
+               my $output = `git ls-remote --exit-code -h "$url" $branch > /dev/null 2>&1`;
+               if ($? == 0) {
+                   push(@good_links, $value);
+               } else {
+                   push(@bad_links, $value);
+                   $isbad = 1;
+               }
+           } elsif ($value =~ /^(?:quilt|hg)\s+(https?:\S+)/) {
+               my $url = $1;
+               my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $url`;
+               if ($? == 0) {
+                   push(@good_links, $value);
+               } else {
+                   push(@bad_links, $value);
+                   $isbad = 1;
+               }
+           }
+           if ($isbad) {
+               print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n");
+           }
+       }
+    }
+}
+
+sub ignore_email_address {
+    my ($address) = @_;
+
+    foreach my $ignore (@ignore_emails) {
+       return 1 if ($ignore eq $address);
+    }
+
+    return 0;
+}
+
+sub range_is_maintained {
+    my ($start, $end) = @_;
+
+    for (my $i = $start; $i < $end; $i++) {
+       my $line = $typevalue[$i];
+       if ($line =~ m/^([A-Z]):\s*(.*)/) {
+           my $type = $1;
+           my $value = $2;
+           if ($type eq 'S') {
+               if ($value =~ /(maintain|support)/i) {
+                   return 1;
+               }
+           }
+       }
+    }
+    return 0;
+}
+
+sub range_has_maintainer {
+    my ($start, $end) = @_;
+
+    for (my $i = $start; $i < $end; $i++) {
+       my $line = $typevalue[$i];
+       if ($line =~ m/^([A-Z]):\s*(.*)/) {
+           my $type = $1;
+           my $value = $2;
+           if ($type eq 'M') {
+               return 1;
+           }
+       }
+    }
+    return 0;
+}
+
+sub get_maintainers {
+    %email_hash_name = ();
+    %email_hash_address = ();
+    %commit_author_hash = ();
+    %commit_signer_hash = ();
+    @email_to = ();
+    %hash_list_to = ();
+    @list_to = ();
+    @scm = ();
+    @web = ();
+    @subsystem = ();
+    @status = ();
+    %deduplicate_name_hash = ();
+    %deduplicate_address_hash = ();
+    if ($email_git_all_signature_types) {
+       $signature_pattern = "(.+?)[Bb][Yy]:";
+    } else {
+       $signature_pattern = "\(" . join("|", @signature_tags) . "\)";
+    }
+
+    # Find responsible parties
+
+    my %exact_pattern_match_hash = ();
+
+    foreach my $file (@files) {
+
+       my %hash;
+       my $tvi = find_first_section();
+       while ($tvi < @typevalue) {
+           my $start = find_starting_index($tvi);
+           my $end = find_ending_index($tvi);
+           my $exclude = 0;
+           my $i;
+
+           #Do not match excluded file patterns
+
+           for ($i = $start; $i < $end; $i++) {
+               my $line = $typevalue[$i];
+               if ($line =~ m/^([A-Z]):\s*(.*)/) {
+                   my $type = $1;
+                   my $value = $2;
+                   if ($type eq 'X') {
+                       if (file_match_pattern($file, $value)) {
+                           $exclude = 1;
+                           last;
+                       }
+                   }
+               }
+           }
+
+           if (!$exclude) {
+               for ($i = $start; $i < $end; $i++) {
+                   my $line = $typevalue[$i];
+                   if ($line =~ m/^([A-Z]):\s*(.*)/) {
+                       my $type = $1;
+                       my $value = $2;
+                       if ($type eq 'F') {
+                           if (file_match_pattern($file, $value)) {
+                               my $value_pd = ($value =~ tr@/@@);
+                               my $file_pd = ($file  =~ tr@/@@);
+                               $value_pd++ if (substr($value,-1,1) ne "/");
+                               $value_pd = -1 if ($value =~ /^\.\*/);
+                               if ($value_pd >= $file_pd &&
+                                   range_is_maintained($start, $end) &&
+                                   range_has_maintainer($start, $end)) {
+                                   $exact_pattern_match_hash{$file} = 1;
+                               }
+                               if ($pattern_depth == 0 ||
+                                   (($file_pd - $value_pd) < $pattern_depth)) {
+                                   $hash{$tvi} = $value_pd;
+                               }
+                           }
+                       } elsif ($type eq 'N') {
+                           if ($file =~ m/$value/x) {
+                               $hash{$tvi} = 0;
+                           }
+                       }
+                   }
+               }
+           }
+           $tvi = $end + 1;
+       }
+
+       foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
+           add_categories($line);
+           if ($sections) {
+               my $i;
+               my $start = find_starting_index($line);
+               my $end = find_ending_index($line);
+               for ($i = $start; $i < $end; $i++) {
+                   my $line = $typevalue[$i];
+                   if ($line =~ /^[FX]:/) {            ##Restore file patterns
+                       $line =~ s/([^\\])\.([^\*])/$1\?$2/g;
+                       $line =~ s/([^\\])\.$/$1\?/g;   ##Convert . back to ?
+                       $line =~ s/\\\./\./g;           ##Convert \. to .
+                       $line =~ s/\.\*/\*/g;           ##Convert .* to *
+                   }
+                   my $count = $line =~ s/^([A-Z]):/$1:\t/g;
+                   if ($letters eq "" || (!$count || $letters =~ /$1/i)) {
+                       print("$line\n");
+                   }
+               }
+               print("\n");
+           }
+       }
+
+       maintainers_in_file($file);
+    }
+
+    if ($keywords) {
+       @keyword_tvi = sort_and_uniq(@keyword_tvi);
+       foreach my $line (@keyword_tvi) {
+           add_categories($line);
+       }
+    }
+
+    foreach my $email (@email_to, @list_to) {
+       $email->[0] = deduplicate_email($email->[0]);
+    }
+
+    foreach my $file (@files) {
+       if ($email &&
+           ($email_git ||
+            ($email_git_fallback &&
+             $file !~ /MAINTAINERS$/ &&
+             !$exact_pattern_match_hash{$file}))) {
+           vcs_file_signoffs($file);
+       }
+       if ($email && $email_git_blame) {
+           vcs_file_blame($file);
+       }
+    }
+
+    if ($email) {
+       foreach my $email (@file_emails) {
+           $email = mailmap_email($email);
+           my ($name, $address) = parse_email($email);
+
+           my $tmp_email = format_email($name, $address, $email_usename);
+           push_email_address($tmp_email, '');
+           add_role($tmp_email, 'in file');
+       }
+    }
+
+    foreach my $fix (@fixes) {
+       vcs_add_commit_signers($fix, "blamed_fixes");
+    }
+
+    my @to = ();
+    if ($email || $email_list) {
+       if ($email) {
+           @to = (@to, @email_to);
+       }
+       if ($email_list) {
+           @to = (@to, @list_to);
+       }
+    }
+
+    if ($interactive) {
+       @to = interactive_get_maintainers(\@to);
+    }
+
+    return @to;
+}
+
+sub file_match_pattern {
+    my ($file, $pattern) = @_;
+    if (substr($pattern, -1) eq "/") {
+       if ($file =~ m@^$pattern@) {
+           return 1;
+       }
+    } else {
+       if ($file =~ m@^$pattern@) {
+           my $s1 = ($file =~ tr@/@@);
+           my $s2 = ($pattern =~ tr@/@@);
+           if ($s1 == $s2) {
+               return 1;
+           }
+       }
+    }
+    return 0;
+}
+
+sub usage {
+    print <<EOT;
+usage: $P [options] patchfile
+       $P [options] -f file|directory
+version: $V
+
+MAINTAINER field selection options:
+  --email => print email address(es) if any
+    --git => include recent git \*-by: signers
+    --git-all-signature-types => include signers regardless of signature type
+        or use only ${signature_pattern} signers (default: $email_git_all_signature_types)
+    --git-fallback => use git when no exact MAINTAINERS pattern (default: $email_git_fallback)
+    --git-min-signatures => number of signatures required (default: $email_git_min_signatures)
+    --git-max-maintainers => maximum maintainers to add (default: $email_git_max_maintainers)
+    --git-min-percent => minimum percentage of commits required (default: $email_git_min_percent)
+    --git-blame => use git blame to find modified commits for patch or file
+    --git-blame-signatures => when used with --git-blame, also include all commit signers
+    --git-since => git history to use (default: $email_git_since)
+    --hg-since => hg history to use (default: $email_hg_since)
+    --interactive => display a menu (mostly useful if used with the --git option)
+    --m => include maintainer(s) if any
+    --r => include reviewer(s) if any
+    --n => include name 'Full Name <addr\@domain.tld>'
+    --l => include list(s) if any
+    --moderated => include moderated lists(s) if any (default: true)
+    --s => include subscriber only list(s) if any (default: false)
+    --remove-duplicates => minimize duplicate email names/addresses
+    --roles => show roles (status:subsystem, git-signer, list, etc...)
+    --rolestats => show roles and statistics (commits/total_commits, %)
+    --file-emails => add email addresses found in -f file (default: 0 (off))
+    --fixes => for patches, add signatures of commits with 'Fixes: <commit>' (default: 1 (on))
+  --scm => print SCM tree(s) if any
+  --status => print status if any
+  --subsystem => print subsystem name if any
+  --web => print website(s) if any
+
+Output type options:
+  --separator [, ] => separator for multiple entries on 1 line
+    using --separator also sets --nomultiline if --separator is not [, ]
+  --multiline => print 1 entry per line
+
+Other options:
+  --pattern-depth => Number of pattern directory traversals (default: 0 (all))
+  --keywords => scan patch for keywords (default: $keywords)
+  --sections => print all of the subsystem sections with pattern matches
+  --letters => print all matching 'letter' types from all matching sections
+  --mailmap => use .mailmap file (default: $email_use_mailmap)
+  --no-tree => run without a kernel tree
+  --self-test => show potential issues with MAINTAINERS file content
+  --version => show version
+  --help => show this help information
+
+Default options:
+  [--email --tree --nogit --git-fallback --m --r --n --l --multiline
+   --pattern-depth=0 --remove-duplicates --rolestats]
+
+Notes:
+  Using "-f directory" may give unexpected results:
+      Used with "--git", git signators for _all_ files in and below
+          directory are examined as git recurses directories.
+          Any specified X: (exclude) pattern matches are _not_ ignored.
+      Used with "--nogit", directory is used as a pattern match,
+          no individual file within the directory or subdirectory
+          is matched.
+      Used with "--git-blame", does not iterate all files in directory
+  Using "--git-blame" is slow and may add old committers and authors
+      that are no longer active maintainers to the output.
+  Using "--roles" or "--rolestats" with git send-email --cc-cmd or any
+      other automated tools that expect only ["name"] <email address>
+      may not work because of additional output after <email address>.
+  Using "--rolestats" and "--git-blame" shows the #/total=% commits,
+      not the percentage of the entire file authored.  # of commits is
+      not a good measure of amount of code authored.  1 major commit may
+      contain a thousand lines, 5 trivial commits may modify a single line.
+  If git is not installed, but mercurial (hg) is installed and an .hg
+      repository exists, the following options apply to mercurial:
+          --git,
+          --git-min-signatures, --git-max-maintainers, --git-min-percent, and
+          --git-blame
+      Use --hg-since not --git-since to control date selection
+  File ".get_maintainer.conf", if it exists in the fstests source root
+      directory, can change whatever get_maintainer defaults are desired.
+      Entries in this file can be any command line argument.
+      This file is prepended to any additional command line arguments.
+      Multiple lines and # comments are allowed.
+  Most options have both positive and negative forms.
+      The negative forms for --<foo> are --no<foo> and --no-<foo>.
+
+EOT
+}
+
+sub top_of_fstests_tree {
+    my ($lk_path) = @_;
+
+    if ($lk_path ne "" && substr($lk_path,length($lk_path)-1,1) ne "/") {
+       $lk_path .= "/";
+    }
+    if (   (-f "${lk_path}check")
+       && (-f "${lk_path}local.config.example")
+       && (-e "${lk_path}MAINTAINERS")
+       && (-f "${lk_path}Makefile")
+       && (-f "${lk_path}README")
+       && (-f "${lk_path}new")
+       && (-d "${lk_path}LICENSES")
+       && (-d "${lk_path}tests")
+       && (-d "${lk_path}common")
+       && (-d "${lk_path}src")
+       && (-d "${lk_path}tools")
+       && (-d "${lk_path}include")
+       && (-d "${lk_path}m4")
+       && (-d "${lk_path}lib")
+       && (-d "${lk_path}doc")) {
+       return 1;
+    }
+    return 0;
+}
+
+sub parse_email {
+    my ($formatted_email) = @_;
+
+    my $name = "";
+    my $address = "";
+
+    if ($formatted_email =~ /^([^<]+)<(.+\@.*)>.*$/) {
+       $name = $1;
+       $address = $2;
+    } elsif ($formatted_email =~ /^\s*<(.+\@\S*)>.*$/) {
+       $address = $1;
+    } elsif ($formatted_email =~ /^(.+\@\S*).*$/) {
+       $address = $1;
+    }
+
+    $name =~ s/^\s+|\s+$//g;
+    $name =~ s/^\"|\"$//g;
+    $address =~ s/^\s+|\s+$//g;
+
+    if ($name =~ /[^\w \-]/i) {         ##has "must quote" chars
+       $name =~ s/(?<!\\)"/\\"/g;       ##escape quotes
+       $name = "\"$name\"";
+    }
+
+    return ($name, $address);
+}
+
+sub format_email {
+    my ($name, $address, $usename) = @_;
+
+    my $formatted_email;
+
+    $name =~ s/^\s+|\s+$//g;
+    $name =~ s/^\"|\"$//g;
+    $address =~ s/^\s+|\s+$//g;
+
+    if ($name =~ /[^\w \-]/i) {          ##has "must quote" chars
+       $name =~ s/(?<!\\)"/\\"/g;       ##escape quotes
+       $name = "\"$name\"";
+    }
+
+    if ($usename) {
+       if ("$name" eq "") {
+           $formatted_email = "$address";
+       } else {
+           $formatted_email = "$name <$address>";
+       }
+    } else {
+       $formatted_email = $address;
+    }
+
+    return $formatted_email;
+}
+
+sub find_first_section {
+    my $index = 0;
+
+    while ($index < @typevalue) {
+       my $tv = $typevalue[$index];
+       if (($tv =~ m/^([A-Z]):\s*(.*)/)) {
+           last;
+       }
+       $index++;
+    }
+
+    return $index;
+}
+
+sub find_starting_index {
+    my ($index) = @_;
+
+    while ($index > 0) {
+       my $tv = $typevalue[$index];
+       if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
+           last;
+       }
+       $index--;
+    }
+
+    return $index;
+}
+
+sub find_ending_index {
+    my ($index) = @_;
+
+    while ($index < @typevalue) {
+       my $tv = $typevalue[$index];
+       if (!($tv =~ m/^([A-Z]):\s*(.*)/)) {
+           last;
+       }
+       $index++;
+    }
+
+    return $index;
+}
+
+sub get_subsystem_name {
+    my ($index) = @_;
+
+    my $start = find_starting_index($index);
+
+    my $subsystem = $typevalue[$start];
+    if ($output_section_maxlen && length($subsystem) > $output_section_maxlen) {
+       $subsystem = substr($subsystem, 0, $output_section_maxlen - 3);
+       $subsystem =~ s/\s*$//;
+       $subsystem = $subsystem . "...";
+    }
+    return $subsystem;
+}
+
+sub get_maintainer_role {
+    my ($index) = @_;
+
+    my $i;
+    my $start = find_starting_index($index);
+    my $end = find_ending_index($index);
+
+    my $role = "unknown";
+    my $subsystem = get_subsystem_name($index);
+
+    for ($i = $start + 1; $i < $end; $i++) {
+       my $tv = $typevalue[$i];
+       if ($tv =~ m/^([A-Z]):\s*(.*)/) {
+           my $ptype = $1;
+           my $pvalue = $2;
+           if ($ptype eq "S") {
+               $role = $pvalue;
+           }
+       }
+    }
+
+    $role = lc($role);
+    if      ($role eq "supported") {
+       $role = "supporter";
+    } elsif ($role eq "maintained") {
+       $role = "maintainer";
+    } elsif ($role eq "odd fixes") {
+       $role = "odd fixer";
+    } elsif ($role eq "orphan") {
+       $role = "orphan minder";
+    } elsif ($role eq "obsolete") {
+       $role = "obsolete minder";
+    }
+
+    return $role . ":" . $subsystem;
+}
+
+sub get_list_role {
+    my ($index) = @_;
+
+    my $subsystem = get_subsystem_name($index);
+
+    if ($subsystem eq "ALL") {
+       $subsystem = "Send To Me";
+    }
+
+    return $subsystem;
+}
+
+sub add_categories {
+    my ($index) = @_;
+
+    my $i;
+    my $start = find_starting_index($index);
+    my $end = find_ending_index($index);
+
+    push(@subsystem, $typevalue[$start]);
+
+    for ($i = $start + 1; $i < $end; $i++) {
+       my $tv = $typevalue[$i];
+       if ($tv =~ m/^([A-Z]):\s*(.*)/) {
+           my $ptype = $1;
+           my $pvalue = $2;
+           if ($ptype eq "L") {
+               my $list_address = $pvalue;
+               my $list_additional = "";
+               my $list_role = get_list_role($i);
+
+               if ($list_role ne "") {
+                   $list_role = ":" . $list_role;
+               }
+               if ($list_address =~ m/([^\s]+)\s+(.*)$/) {
+                   $list_address = $1;
+                   $list_additional = $2;
+               }
+               if ($list_additional =~ m/subscribers-only/) {
+                   if ($email_subscriber_list) {
+                       if (!$hash_list_to{lc($list_address)}) {
+                           $hash_list_to{lc($list_address)} = 1;
+                           push(@list_to, [$list_address,
+                                           "subscriber list${list_role}"]);
+                       }
+                   }
+               } else {
+                   if ($email_list) {
+                       if (!$hash_list_to{lc($list_address)}) {
+                           if ($list_additional =~ m/moderated/) {
+                               if ($email_moderated_list) {
+                                   $hash_list_to{lc($list_address)} = 1;
+                                   push(@list_to, [$list_address,
+                                                   "moderated list${list_role}"]);
+                               }
+                           } else {
+                               $hash_list_to{lc($list_address)} = 1;
+                               push(@list_to, [$list_address,
+                                               "open list${list_role}"]);
+                           }
+                       }
+                   }
+               }
+           } elsif ($ptype eq "M") {
+               if ($email_maintainer) {
+                   my $role = get_maintainer_role($i);
+                   push_email_addresses($pvalue, $role);
+               }
+           } elsif ($ptype eq "R") {
+               if ($email_reviewer) {
+                   my $subsystem = get_subsystem_name($i);
+                   push_email_addresses($pvalue, "reviewer:$subsystem");
+               }
+           } elsif ($ptype eq "T") {
+               push(@scm, $pvalue);
+           } elsif ($ptype eq "W") {
+               push(@web, $pvalue);
+           } elsif ($ptype eq "S") {
+               push(@status, $pvalue);
+           }
+       }
+    }
+}
+
+sub email_inuse {
+    my ($name, $address) = @_;
+
+    return 1 if (($name eq "") && ($address eq ""));
+    return 1 if (($name ne "") && exists($email_hash_name{lc($name)}));
+    return 1 if (($address ne "") && exists($email_hash_address{lc($address)}));
+
+    return 0;
+}
+
+sub push_email_address {
+    my ($line, $role) = @_;
+
+    my ($name, $address) = parse_email($line);
+
+    if ($address eq "") {
+       return 0;
+    }
+
+    if (!$email_remove_duplicates) {
+       push(@email_to, [format_email($name, $address, $email_usename), $role]);
+    } elsif (!email_inuse($name, $address)) {
+       push(@email_to, [format_email($name, $address, $email_usename), $role]);
+       $email_hash_name{lc($name)}++ if ($name ne "");
+       $email_hash_address{lc($address)}++;
+    }
+
+    return 1;
+}
+
+sub push_email_addresses {
+    my ($address, $role) = @_;
+
+    my @address_list = ();
+
+    if (rfc822_valid($address)) {
+       push_email_address($address, $role);
+    } elsif (@address_list = rfc822_validlist($address)) {
+       my $array_count = shift(@address_list);
+       while (my $entry = shift(@address_list)) {
+           push_email_address($entry, $role);
+       }
+    } else {
+       if (!push_email_address($address, $role)) {
+           warn("Invalid MAINTAINERS address: '" . $address . "'\n");
+       }
+    }
+}
+
+sub add_role {
+    my ($line, $role) = @_;
+
+    my ($name, $address) = parse_email($line);
+    my $email = format_email($name, $address, $email_usename);
+
+    foreach my $entry (@email_to) {
+       if ($email_remove_duplicates) {
+           my ($entry_name, $entry_address) = parse_email($entry->[0]);
+           if (($name eq $entry_name || $address eq $entry_address)
+               && ($role eq "" || !($entry->[1] =~ m/$role/))
+           ) {
+               if ($entry->[1] eq "") {
+                   $entry->[1] = "$role";
+               } else {
+                   $entry->[1] = "$entry->[1],$role";
+               }
+           }
+       } else {
+           if ($email eq $entry->[0]
+               && ($role eq "" || !($entry->[1] =~ m/$role/))
+           ) {
+               if ($entry->[1] eq "") {
+                   $entry->[1] = "$role";
+               } else {
+                   $entry->[1] = "$entry->[1],$role";
+               }
+           }
+       }
+    }
+}
+
+sub which {
+    my ($bin) = @_;
+
+    foreach my $path (split(/:/, $ENV{PATH})) {
+       if (-e "$path/$bin") {
+           return "$path/$bin";
+       }
+    }
+
+    return "";
+}
+
+sub which_conf {
+    my ($conf) = @_;
+
+    foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
+       if (-e "$path/$conf") {
+           return "$path/$conf";
+       }
+    }
+
+    return "";
+}
+
+sub mailmap_email {
+    my ($line) = @_;
+
+    my ($name, $address) = parse_email($line);
+    my $email = format_email($name, $address, 1);
+    my $real_name = $name;
+    my $real_address = $address;
+
+    if (exists $mailmap->{names}->{$email} ||
+       exists $mailmap->{addresses}->{$email}) {
+       if (exists $mailmap->{names}->{$email}) {
+           $real_name = $mailmap->{names}->{$email};
+       }
+       if (exists $mailmap->{addresses}->{$email}) {
+           $real_address = $mailmap->{addresses}->{$email};
+       }
+    } else {
+       if (exists $mailmap->{names}->{$address}) {
+           $real_name = $mailmap->{names}->{$address};
+       }
+       if (exists $mailmap->{addresses}->{$address}) {
+           $real_address = $mailmap->{addresses}->{$address};
+       }
+    }
+    return format_email($real_name, $real_address, 1);
+}
+
+sub mailmap {
+    my (@addresses) = @_;
+
+    my @mapped_emails = ();
+    foreach my $line (@addresses) {
+       push(@mapped_emails, mailmap_email($line));
+    }
+    merge_by_realname(@mapped_emails) if ($email_use_mailmap);
+    return @mapped_emails;
+}
+
+sub merge_by_realname {
+    my %address_map;
+    my (@emails) = @_;
+
+    foreach my $email (@emails) {
+       my ($name, $address) = parse_email($email);
+       if (exists $address_map{$name}) {
+           $address = $address_map{$name};
+           $email = format_email($name, $address, 1);
+       } else {
+           $address_map{$name} = $address;
+       }
+    }
+}
+
+sub git_execute_cmd {
+    my ($cmd) = @_;
+    my @lines = ();
+
+    my $output = `$cmd`;
+    $output =~ s/^\s*//gm;
+    @lines = split("\n", $output);
+
+    return @lines;
+}
+
+sub hg_execute_cmd {
+    my ($cmd) = @_;
+    my @lines = ();
+
+    my $output = `$cmd`;
+    @lines = split("\n", $output);
+
+    return @lines;
+}
+
+sub extract_formatted_signatures {
+    my (@signature_lines) = @_;
+
+    my @type = @signature_lines;
+
+    s/\s*(.*):.*/$1/ for (@type);
+
+    # cut -f2- -d":"
+    s/\s*.*:\s*(.+)\s*/$1/ for (@signature_lines);
+
+## Reformat email addresses (with names) to avoid badly written signatures
+
+    foreach my $signer (@signature_lines) {
+       $signer = deduplicate_email($signer);
+    }
+
+    return (\@type, \@signature_lines);
+}
+
+sub vcs_find_signers {
+    my ($cmd, $file) = @_;
+    my $commits;
+    my @lines = ();
+    my @signatures = ();
+    my @authors = ();
+    my @stats = ();
+
+    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+    my $pattern = $VCS_cmds{"commit_pattern"};
+    my $author_pattern = $VCS_cmds{"author_pattern"};
+    my $stat_pattern = $VCS_cmds{"stat_pattern"};
+
+    $stat_pattern =~ s/(\$\w+)/$1/eeg;         #interpolate $stat_pattern
+
+    $commits = grep(/$pattern/, @lines);       # of commits
+
+    @authors = grep(/$author_pattern/, @lines);
+    @signatures = grep(/^[ \t]*${signature_pattern}.*\@.*$/, @lines);
+    @stats = grep(/$stat_pattern/, @lines);
+
+#    print("stats: <@stats>\n");
+
+    return (0, \@signatures, \@authors, \@stats) if !@signatures;
+
+    save_commits_by_author(@lines) if ($interactive);
+    save_commits_by_signer(@lines) if ($interactive);
+
+    my ($author_ref, $authors_ref) = extract_formatted_signatures(@authors);
+    my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
+
+    return ($commits, $signers_ref, $authors_ref, \@stats);
+}
+
+sub vcs_find_author {
+    my ($cmd) = @_;
+    my @lines = ();
+
+    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+    return @lines if !@lines;
+
+    my @authors = ();
+    foreach my $line (@lines) {
+       if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
+           my $author = $1;
+           my ($name, $address) = parse_email($author);
+           $author = format_email($name, $address, 1);
+           push(@authors, $author);
+       }
+    }
+
+    save_commits_by_author(@lines) if ($interactive);
+    save_commits_by_signer(@lines) if ($interactive);
+
+    return @authors;
+}
+
+sub vcs_save_commits {
+    my ($cmd) = @_;
+    my @lines = ();
+    my @commits = ();
+
+    @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+    foreach my $line (@lines) {
+       if ($line =~ m/$VCS_cmds{"blame_commit_pattern"}/) {
+           push(@commits, $1);
+       }
+    }
+
+    return @commits;
+}
+
+sub vcs_blame {
+    my ($file) = @_;
+    my $cmd;
+    my @commits = ();
+
+    return @commits if (!(-f $file));
+
+    if (@range && $VCS_cmds{"blame_range_cmd"} eq "") {
+       my @all_commits = ();
+
+       $cmd = $VCS_cmds{"blame_file_cmd"};
+       $cmd =~ s/(\$\w+)/$1/eeg;               #interpolate $cmd
+       @all_commits = vcs_save_commits($cmd);
+
+       foreach my $file_range_diff (@range) {
+           next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
+           my $diff_file = $1;
+           my $diff_start = $2;
+           my $diff_length = $3;
+           next if ("$file" ne "$diff_file");
+           for (my $i = $diff_start; $i < $diff_start + $diff_length; $i++) {
+               push(@commits, $all_commits[$i]);
+           }
+       }
+    } elsif (@range) {
+       foreach my $file_range_diff (@range) {
+           next if (!($file_range_diff =~ m/(.+):(.+):(.+)/));
+           my $diff_file = $1;
+           my $diff_start = $2;
+           my $diff_length = $3;
+           next if ("$file" ne "$diff_file");
+           $cmd = $VCS_cmds{"blame_range_cmd"};
+           $cmd =~ s/(\$\w+)/$1/eeg;           #interpolate $cmd
+           push(@commits, vcs_save_commits($cmd));
+       }
+    } else {
+       $cmd = $VCS_cmds{"blame_file_cmd"};
+       $cmd =~ s/(\$\w+)/$1/eeg;               #interpolate $cmd
+       @commits = vcs_save_commits($cmd);
+    }
+
+    foreach my $commit (@commits) {
+       $commit =~ s/^\^//g;
+    }
+
+    return @commits;
+}
+
+my $printed_novcs = 0;
+sub vcs_exists {
+    %VCS_cmds = %VCS_cmds_git;
+    return 1 if eval $VCS_cmds{"available"};
+    %VCS_cmds = %VCS_cmds_hg;
+    return 2 if eval $VCS_cmds{"available"};
+    %VCS_cmds = ();
+    if (!$printed_novcs && $email_git) {
+       warn("$P: No supported VCS found.  Add --nogit to options?\n");
+       warn("Using a git repository produces better results.\n");
+       warn("Try Linus Torvalds' latest git repository using:\n");
+       warn("git clone git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git\n");
+       $printed_novcs = 1;
+    }
+    return 0;
+}
+
+sub vcs_is_git {
+    vcs_exists();
+    return $vcs_used == 1;
+}
+
+sub vcs_is_hg {
+    return $vcs_used == 2;
+}
+
+sub vcs_add_commit_signers {
+    return if (!vcs_exists());
+
+    my ($commit, $desc) = @_;
+    my $commit_count = 0;
+    my $commit_authors_ref;
+    my $commit_signers_ref;
+    my $stats_ref;
+    my @commit_authors = ();
+    my @commit_signers = ();
+    my $cmd;
+
+    $cmd = $VCS_cmds{"find_commit_signers_cmd"};
+    $cmd =~ s/(\$\w+)/$1/eeg;  #substitute variables in $cmd
+
+    ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, "");
+    @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
+    @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
+
+    foreach my $signer (@commit_signers) {
+       $signer = deduplicate_email($signer);
+    }
+
+    vcs_assign($desc, 1, @commit_signers);
+}
+
+sub interactive_get_maintainers {
+    my ($list_ref) = @_;
+    my @list = @$list_ref;
+
+    vcs_exists();
+
+    my %selected;
+    my %authored;
+    my %signed;
+    my $count = 0;
+    my $maintained = 0;
+    foreach my $entry (@list) {
+       $maintained = 1 if ($entry->[1] =~ /^(maintainer|supporter)/i);
+       $selected{$count} = 1;
+       $authored{$count} = 0;
+       $signed{$count} = 0;
+       $count++;
+    }
+
+    #menu loop
+    my $done = 0;
+    my $print_options = 0;
+    my $redraw = 1;
+    while (!$done) {
+       $count = 0;
+       if ($redraw) {
+           printf STDERR "\n%1s %2s %-65s",
+                         "*", "#", "email/list and role:stats";
+           if ($email_git ||
+               ($email_git_fallback && !$maintained) ||
+               $email_git_blame) {
+               print STDERR "auth sign";
+           }
+           print STDERR "\n";
+           foreach my $entry (@list) {
+               my $email = $entry->[0];
+               my $role = $entry->[1];
+               my $sel = "";
+               $sel = "*" if ($selected{$count});
+               my $commit_author = $commit_author_hash{$email};
+               my $commit_signer = $commit_signer_hash{$email};
+               my $authored = 0;
+               my $signed = 0;
+               $authored++ for (@{$commit_author});
+               $signed++ for (@{$commit_signer});
+               printf STDERR "%1s %2d %-65s", $sel, $count + 1, $email;
+               printf STDERR "%4d %4d", $authored, $signed
+                   if ($authored > 0 || $signed > 0);
+               printf STDERR "\n     %s\n", $role;
+               if ($authored{$count}) {
+                   my $commit_author = $commit_author_hash{$email};
+                   foreach my $ref (@{$commit_author}) {
+                       print STDERR "     Author: @{$ref}[1]\n";
+                   }
+               }
+               if ($signed{$count}) {
+                   my $commit_signer = $commit_signer_hash{$email};
+                   foreach my $ref (@{$commit_signer}) {
+                       print STDERR "     @{$ref}[2]: @{$ref}[1]\n";
+                   }
+               }
+
+               $count++;
+           }
+       }
+       my $date_ref = \$email_git_since;
+       $date_ref = \$email_hg_since if (vcs_is_hg());
+       if ($print_options) {
+           $print_options = 0;
+           if (vcs_exists()) {
+               print STDERR <<EOT
+
+Version Control options:
+g  use git history      [$email_git]
+gf use git-fallback     [$email_git_fallback]
+b  use git blame        [$email_git_blame]
+bs use blame signatures [$email_git_blame_signatures]
+c# minimum commits      [$email_git_min_signatures]
+%# min percent          [$email_git_min_percent]
+d# history to use       [$$date_ref]
+x# max maintainers      [$email_git_max_maintainers]
+t  all signature types  [$email_git_all_signature_types]
+m  use .mailmap         [$email_use_mailmap]
+EOT
+           }
+           print STDERR <<EOT
+
+Additional options:
+0  toggle all
+tm toggle maintainers
+tg toggle git entries
+tl toggle open list entries
+ts toggle subscriber list entries
+f  emails in file       [$email_file_emails]
+k  keywords in file     [$keywords]
+r  remove duplicates    [$email_remove_duplicates]
+p# pattern match depth  [$pattern_depth]
+EOT
+       }
+       print STDERR
+"\n#(toggle), A#(author), S#(signed) *(all), ^(none), O(options), Y(approve): ";
+
+       my $input = <STDIN>;
+       chomp($input);
+
+       $redraw = 1;
+       my $rerun = 0;
+       my @wish = split(/[, ]+/, $input);
+       foreach my $nr (@wish) {
+           $nr = lc($nr);
+           my $sel = substr($nr, 0, 1);
+           my $str = substr($nr, 1);
+           my $val = 0;
+           $val = $1 if $str =~ /^(\d+)$/;
+
+           if ($sel eq "y") {
+               $interactive = 0;
+               $done = 1;
+               $output_rolestats = 0;
+               $output_roles = 0;
+               last;
+           } elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
+               $selected{$nr - 1} = !$selected{$nr - 1};
+           } elsif ($sel eq "*" || $sel eq '^') {
+               my $toggle = 0;
+               $toggle = 1 if ($sel eq '*');
+               for (my $i = 0; $i < $count; $i++) {
+                   $selected{$i} = $toggle;
+               }
+           } elsif ($sel eq "0") {
+               for (my $i = 0; $i < $count; $i++) {
+                   $selected{$i} = !$selected{$i};
+               }
+           } elsif ($sel eq "t") {
+               if (lc($str) eq "m") {
+                   for (my $i = 0; $i < $count; $i++) {
+                       $selected{$i} = !$selected{$i}
+                           if ($list[$i]->[1] =~ /^(maintainer|supporter)/i);
+                   }
+               } elsif (lc($str) eq "g") {
+                   for (my $i = 0; $i < $count; $i++) {
+                       $selected{$i} = !$selected{$i}
+                           if ($list[$i]->[1] =~ /^(author|commit|signer)/i);
+                   }
+               } elsif (lc($str) eq "l") {
+                   for (my $i = 0; $i < $count; $i++) {
+                       $selected{$i} = !$selected{$i}
+                           if ($list[$i]->[1] =~ /^(open list)/i);
+                   }
+               } elsif (lc($str) eq "s") {
+                   for (my $i = 0; $i < $count; $i++) {
+                       $selected{$i} = !$selected{$i}
+                           if ($list[$i]->[1] =~ /^(subscriber list)/i);
+                   }
+               }
+           } elsif ($sel eq "a") {
+               if ($val > 0 && $val <= $count) {
+                   $authored{$val - 1} = !$authored{$val - 1};
+               } elsif ($str eq '*' || $str eq '^') {
+                   my $toggle = 0;
+                   $toggle = 1 if ($str eq '*');
+                   for (my $i = 0; $i < $count; $i++) {
+                       $authored{$i} = $toggle;
+                   }
+               }
+           } elsif ($sel eq "s") {
+               if ($val > 0 && $val <= $count) {
+                   $signed{$val - 1} = !$signed{$val - 1};
+               } elsif ($str eq '*' || $str eq '^') {
+                   my $toggle = 0;
+                   $toggle = 1 if ($str eq '*');
+                   for (my $i = 0; $i < $count; $i++) {
+                       $signed{$i} = $toggle;
+                   }
+               }
+           } elsif ($sel eq "o") {
+               $print_options = 1;
+               $redraw = 1;
+           } elsif ($sel eq "g") {
+               if ($str eq "f") {
+                   bool_invert(\$email_git_fallback);
+               } else {
+                   bool_invert(\$email_git);
+               }
+               $rerun = 1;
+           } elsif ($sel eq "b") {
+               if ($str eq "s") {
+                   bool_invert(\$email_git_blame_signatures);
+               } else {
+                   bool_invert(\$email_git_blame);
+               }
+               $rerun = 1;
+           } elsif ($sel eq "c") {
+               if ($val > 0) {
+                   $email_git_min_signatures = $val;
+                   $rerun = 1;
+               }
+           } elsif ($sel eq "x") {
+               if ($val > 0) {
+                   $email_git_max_maintainers = $val;
+                   $rerun = 1;
+               }
+           } elsif ($sel eq "%") {
+               if ($str ne "" && $val >= 0) {
+                   $email_git_min_percent = $val;
+                   $rerun = 1;
+               }
+           } elsif ($sel eq "d") {
+               if (vcs_is_git()) {
+                   $email_git_since = $str;
+               } elsif (vcs_is_hg()) {
+                   $email_hg_since = $str;
+               }
+               $rerun = 1;
+           } elsif ($sel eq "t") {
+               bool_invert(\$email_git_all_signature_types);
+               $rerun = 1;
+           } elsif ($sel eq "f") {
+               bool_invert(\$email_file_emails);
+               $rerun = 1;
+           } elsif ($sel eq "r") {
+               bool_invert(\$email_remove_duplicates);
+               $rerun = 1;
+           } elsif ($sel eq "m") {
+               bool_invert(\$email_use_mailmap);
+               read_mailmap();
+               $rerun = 1;
+           } elsif ($sel eq "k") {
+               bool_invert(\$keywords);
+               $rerun = 1;
+           } elsif ($sel eq "p") {
+               if ($str ne "" && $val >= 0) {
+                   $pattern_depth = $val;
+                   $rerun = 1;
+               }
+           } elsif ($sel eq "h" || $sel eq "?") {
+               print STDERR <<EOT
+
+Interactive mode allows you to select the various maintainers, submitters,
+commit signers and mailing lists that could be CC'd on a patch.
+
+Any *'d entry is selected.
+
+If you have git or hg installed, you can choose to summarize the commit
+history of files in the patch.  Also, each line of the current file can
+be matched to its commit author and that commits signers with blame.
+
+Various knobs exist to control the length of time for active commit
+tracking, the maximum number of commit authors and signers to add,
+and such.
+
+Enter selections at the prompt until you are satisfied that the selected
+maintainers are appropriate.  You may enter multiple selections separated
+by either commas or spaces.
+
+EOT
+           } else {
+               print STDERR "invalid option: '$nr'\n";
+               $redraw = 0;
+           }
+       }
+       if ($rerun) {
+           print STDERR "git-blame can be very slow, please have patience..."
+               if ($email_git_blame);
+           goto &get_maintainers;
+       }
+    }
+
+    #drop not selected entries
+    $count = 0;
+    my @new_emailto = ();
+    foreach my $entry (@list) {
+       if ($selected{$count}) {
+           push(@new_emailto, $list[$count]);
+       }
+       $count++;
+    }
+    return @new_emailto;
+}
+
+sub bool_invert {
+    my ($bool_ref) = @_;
+
+    if ($$bool_ref) {
+       $$bool_ref = 0;
+    } else {
+       $$bool_ref = 1;
+    }
+}
+
+sub deduplicate_email {
+    my ($email) = @_;
+
+    my $matched = 0;
+    my ($name, $address) = parse_email($email);
+    $email = format_email($name, $address, 1);
+    $email = mailmap_email($email);
+
+    return $email if (!$email_remove_duplicates);
+
+    ($name, $address) = parse_email($email);
+
+    if ($name ne "" && $deduplicate_name_hash{lc($name)}) {
+       $name = $deduplicate_name_hash{lc($name)}->[0];
+       $address = $deduplicate_name_hash{lc($name)}->[1];
+       $matched = 1;
+    } elsif ($deduplicate_address_hash{lc($address)}) {
+       $name = $deduplicate_address_hash{lc($address)}->[0];
+       $address = $deduplicate_address_hash{lc($address)}->[1];
+       $matched = 1;
+    }
+    if (!$matched) {
+       $deduplicate_name_hash{lc($name)} = [ $name, $address ];
+       $deduplicate_address_hash{lc($address)} = [ $name, $address ];
+    }
+    $email = format_email($name, $address, 1);
+    $email = mailmap_email($email);
+    return $email;
+}
+
+sub save_commits_by_author {
+    my (@lines) = @_;
+
+    my @authors = ();
+    my @commits = ();
+    my @subjects = ();
+
+    foreach my $line (@lines) {
+       if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
+           my $author = $1;
+           $author = deduplicate_email($author);
+           push(@authors, $author);
+       }
+       push(@commits, $1) if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
+       push(@subjects, $1) if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
+    }
+
+    for (my $i = 0; $i < @authors; $i++) {
+       my $exists = 0;
+       foreach my $ref(@{$commit_author_hash{$authors[$i]}}) {
+           if (@{$ref}[0] eq $commits[$i] &&
+               @{$ref}[1] eq $subjects[$i]) {
+               $exists = 1;
+               last;
+           }
+       }
+       if (!$exists) {
+           push(@{$commit_author_hash{$authors[$i]}},
+                [ ($commits[$i], $subjects[$i]) ]);
+       }
+    }
+}
+
+sub save_commits_by_signer {
+    my (@lines) = @_;
+
+    my $commit = "";
+    my $subject = "";
+
+    foreach my $line (@lines) {
+       $commit = $1 if ($line =~ m/$VCS_cmds{"commit_pattern"}/);
+       $subject = $1 if ($line =~ m/$VCS_cmds{"subject_pattern"}/);
+       if ($line =~ /^[ \t]*${signature_pattern}.*\@.*$/) {
+           my @signatures = ($line);
+           my ($types_ref, $signers_ref) = extract_formatted_signatures(@signatures);
+           my @types = @$types_ref;
+           my @signers = @$signers_ref;
+
+           my $type = $types[0];
+           my $signer = $signers[0];
+
+           $signer = deduplicate_email($signer);
+
+           my $exists = 0;
+           foreach my $ref(@{$commit_signer_hash{$signer}}) {
+               if (@{$ref}[0] eq $commit &&
+                   @{$ref}[1] eq $subject &&
+                   @{$ref}[2] eq $type) {
+                   $exists = 1;
+                   last;
+               }
+           }
+           if (!$exists) {
+               push(@{$commit_signer_hash{$signer}},
+                    [ ($commit, $subject, $type) ]);
+           }
+       }
+    }
+}
+
+sub vcs_assign {
+    my ($role, $divisor, @lines) = @_;
+
+    my %hash;
+    my $count = 0;
+
+    return if (@lines <= 0);
+
+    if ($divisor <= 0) {
+       warn("Bad divisor in " . (caller(0))[3] . ": $divisor\n");
+       $divisor = 1;
+    }
+
+    @lines = mailmap(@lines);
+
+    return if (@lines <= 0);
+
+    @lines = sort(@lines);
+
+    # uniq -c
+    $hash{$_}++ for @lines;
+
+    # sort -rn
+    foreach my $line (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
+       my $sign_offs = $hash{$line};
+       my $percent = $sign_offs * 100 / $divisor;
+
+       $percent = 100 if ($percent > 100);
+       next if (ignore_email_address($line));
+       $count++;
+       last if ($sign_offs < $email_git_min_signatures ||
+                $count > $email_git_max_maintainers ||
+                $percent < $email_git_min_percent);
+       push_email_address($line, '');
+       if ($output_rolestats) {
+           my $fmt_percent = sprintf("%.0f", $percent);
+           add_role($line, "$role:$sign_offs/$divisor=$fmt_percent%");
+       } else {
+           add_role($line, $role);
+       }
+    }
+}
+
+sub vcs_file_signoffs {
+    my ($file) = @_;
+
+    my $authors_ref;
+    my $signers_ref;
+    my $stats_ref;
+    my @authors = ();
+    my @signers = ();
+    my @stats = ();
+    my $commits;
+
+    $vcs_used = vcs_exists();
+    return if (!$vcs_used);
+
+    my $cmd = $VCS_cmds{"find_signers_cmd"};
+    $cmd =~ s/(\$\w+)/$1/eeg;          # interpolate $cmd
+
+    ($commits, $signers_ref, $authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
+
+    @signers = @{$signers_ref} if defined $signers_ref;
+    @authors = @{$authors_ref} if defined $authors_ref;
+    @stats = @{$stats_ref} if defined $stats_ref;
+
+#    print("commits: <$commits>\nsigners:<@signers>\nauthors: <@authors>\nstats: <@stats>\n");
+
+    foreach my $signer (@signers) {
+       $signer = deduplicate_email($signer);
+    }
+
+    vcs_assign("commit_signer", $commits, @signers);
+    vcs_assign("authored", $commits, @authors);
+    if ($#authors == $#stats) {
+       my $stat_pattern = $VCS_cmds{"stat_pattern"};
+       $stat_pattern =~ s/(\$\w+)/$1/eeg;      #interpolate $stat_pattern
+
+       my $added = 0;
+       my $deleted = 0;
+       for (my $i = 0; $i <= $#stats; $i++) {
+           if ($stats[$i] =~ /$stat_pattern/) {
+               $added += $1;
+               $deleted += $2;
+           }
+       }
+       my @tmp_authors = uniq(@authors);
+       foreach my $author (@tmp_authors) {
+           $author = deduplicate_email($author);
+       }
+       @tmp_authors = uniq(@tmp_authors);
+       my @list_added = ();
+       my @list_deleted = ();
+       foreach my $author (@tmp_authors) {
+           my $auth_added = 0;
+           my $auth_deleted = 0;
+           for (my $i = 0; $i <= $#stats; $i++) {
+               if ($author eq deduplicate_email($authors[$i]) &&
+                   $stats[$i] =~ /$stat_pattern/) {
+                   $auth_added += $1;
+                   $auth_deleted += $2;
+               }
+           }
+           for (my $i = 0; $i < $auth_added; $i++) {
+               push(@list_added, $author);
+           }
+           for (my $i = 0; $i < $auth_deleted; $i++) {
+               push(@list_deleted, $author);
+           }
+       }
+       vcs_assign("added_lines", $added, @list_added);
+       vcs_assign("removed_lines", $deleted, @list_deleted);
+    }
+}
+
+sub vcs_file_blame {
+    my ($file) = @_;
+
+    my @signers = ();
+    my @all_commits = ();
+    my @commits = ();
+    my $total_commits;
+    my $total_lines;
+
+    $vcs_used = vcs_exists();
+    return if (!$vcs_used);
+
+    @all_commits = vcs_blame($file);
+    @commits = uniq(@all_commits);
+    $total_commits = @commits;
+    $total_lines = @all_commits;
+
+    if ($email_git_blame_signatures) {
+       if (vcs_is_hg()) {
+           my $commit_count;
+           my $commit_authors_ref;
+           my $commit_signers_ref;
+           my $stats_ref;
+           my @commit_authors = ();
+           my @commit_signers = ();
+           my $commit = join(" -r ", @commits);
+           my $cmd;
+
+           $cmd = $VCS_cmds{"find_commit_signers_cmd"};
+           $cmd =~ s/(\$\w+)/$1/eeg;   #substitute variables in $cmd
+
+           ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
+           @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
+           @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
+
+           push(@signers, @commit_signers);
+       } else {
+           foreach my $commit (@commits) {
+               my $commit_count;
+               my $commit_authors_ref;
+               my $commit_signers_ref;
+               my $stats_ref;
+               my @commit_authors = ();
+               my @commit_signers = ();
+               my $cmd;
+
+               $cmd = $VCS_cmds{"find_commit_signers_cmd"};
+               $cmd =~ s/(\$\w+)/$1/eeg;       #substitute variables in $cmd
+
+               ($commit_count, $commit_signers_ref, $commit_authors_ref, $stats_ref) = vcs_find_signers($cmd, $file);
+               @commit_authors = @{$commit_authors_ref} if defined $commit_authors_ref;
+               @commit_signers = @{$commit_signers_ref} if defined $commit_signers_ref;
+
+               push(@signers, @commit_signers);
+           }
+       }
+    }
+
+    if ($from_filename) {
+       if ($output_rolestats) {
+           my @blame_signers;
+           if (vcs_is_hg()) {{         # Double brace for last exit
+               my $commit_count;
+               my @commit_signers = ();
+               @commits = uniq(@commits);
+               @commits = sort(@commits);
+               my $commit = join(" -r ", @commits);
+               my $cmd;
+
+               $cmd = $VCS_cmds{"find_commit_author_cmd"};
+               $cmd =~ s/(\$\w+)/$1/eeg;       #substitute variables in $cmd
+
+               my @lines = ();
+
+               @lines = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+               last if !@lines;
+
+               my @authors = ();
+               foreach my $line (@lines) {
+                   if ($line =~ m/$VCS_cmds{"author_pattern"}/) {
+                       my $author = $1;
+                       $author = deduplicate_email($author);
+                       push(@authors, $author);
+                   }
+               }
+
+               save_commits_by_author(@lines) if ($interactive);
+               save_commits_by_signer(@lines) if ($interactive);
+
+               push(@signers, @authors);
+           }}
+           else {
+               foreach my $commit (@commits) {
+                   my $i;
+                   my $cmd = $VCS_cmds{"find_commit_author_cmd"};
+                   $cmd =~ s/(\$\w+)/$1/eeg;   #interpolate $cmd
+                   my @author = vcs_find_author($cmd);
+                   next if !@author;
+
+                   my $formatted_author = deduplicate_email($author[0]);
+
+                   my $count = grep(/$commit/, @all_commits);
+                   for ($i = 0; $i < $count ; $i++) {
+                       push(@blame_signers, $formatted_author);
+                   }
+               }
+           }
+           if (@blame_signers) {
+               vcs_assign("authored lines", $total_lines, @blame_signers);
+           }
+       }
+       foreach my $signer (@signers) {
+           $signer = deduplicate_email($signer);
+       }
+       vcs_assign("commits", $total_commits, @signers);
+    } else {
+       foreach my $signer (@signers) {
+           $signer = deduplicate_email($signer);
+       }
+       vcs_assign("modified commits", $total_commits, @signers);
+    }
+}
+
+sub vcs_file_exists {
+    my ($file) = @_;
+
+    my $exists;
+
+    my $vcs_used = vcs_exists();
+    return 0 if (!$vcs_used);
+
+    my $cmd = $VCS_cmds{"file_exists_cmd"};
+    $cmd =~ s/(\$\w+)/$1/eeg;          # interpolate $cmd
+    $cmd .= " 2>&1";
+    $exists = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+    return 0 if ($? != 0);
+
+    return $exists;
+}
+
+sub vcs_list_files {
+    my ($file) = @_;
+
+    my @lsfiles = ();
+
+    my $vcs_used = vcs_exists();
+    return 0 if (!$vcs_used);
+
+    my $cmd = $VCS_cmds{"list_files_cmd"};
+    $cmd =~ s/(\$\w+)/$1/eeg;   # interpolate $cmd
+    @lsfiles = &{$VCS_cmds{"execute_cmd"}}($cmd);
+
+    return () if ($? != 0);
+
+    return @lsfiles;
+}
+
+sub uniq {
+    my (@parms) = @_;
+
+    my %saw;
+    @parms = grep(!$saw{$_}++, @parms);
+    return @parms;
+}
+
+sub sort_and_uniq {
+    my (@parms) = @_;
+
+    my %saw;
+    @parms = sort @parms;
+    @parms = grep(!$saw{$_}++, @parms);
+    return @parms;
+}
+
+sub clean_file_emails {
+    my (@file_emails) = @_;
+    my @fmt_emails = ();
+
+    foreach my $email (@file_emails) {
+       $email =~ s/[\(\<\{]{0,1}([A-Za-z0-9_\.\+-]+\@[A-Za-z0-9\.-]+)[\)\>\}]{0,1}/\<$1\>/g;
+       my ($name, $address) = parse_email($email);
+       if ($name eq '"[,\.]"') {
+           $name = "";
+       }
+
+       my @nw = split(/[^A-Za-zÀ-ÿ\'\,\.\+-]/, $name);
+       if (@nw > 2) {
+           my $first = $nw[@nw - 3];
+           my $middle = $nw[@nw - 2];
+           my $last = $nw[@nw - 1];
+
+           if (((length($first) == 1 && $first =~ m/[A-Za-z]/) ||
+                (length($first) == 2 && substr($first, -1) eq ".")) ||
+               (length($middle) == 1 ||
+                (length($middle) == 2 && substr($middle, -1) eq "."))) {
+               $name = "$first $middle $last";
+           } else {
+               $name = "$middle $last";
+           }
+       }
+
+       if (substr($name, -1) =~ /[,\.]/) {
+           $name = substr($name, 0, length($name) - 1);
+       } elsif (substr($name, -2) =~ /[,\.]"/) {
+           $name = substr($name, 0, length($name) - 2) . '"';
+       }
+
+       if (substr($name, 0, 1) =~ /[,\.]/) {
+           $name = substr($name, 1, length($name) - 1);
+       } elsif (substr($name, 0, 2) =~ /"[,\.]/) {
+           $name = '"' . substr($name, 2, length($name) - 2);
+       }
+
+       my $fmt_email = format_email($name, $address, $email_usename);
+       push(@fmt_emails, $fmt_email);
+    }
+    return @fmt_emails;
+}
+
+sub merge_email {
+    my @lines;
+    my %saw;
+
+    for (@_) {
+       my ($address, $role) = @$_;
+       if (!$saw{$address}) {
+           if ($output_roles) {
+               push(@lines, "$address ($role)");
+           } else {
+               push(@lines, $address);
+           }
+           $saw{$address} = 1;
+       }
+    }
+
+    return @lines;
+}
+
+sub output {
+    my (@parms) = @_;
+
+    if ($output_multiline) {
+       foreach my $line (@parms) {
+           print("${line}\n");
+       }
+    } else {
+       print(join($output_separator, @parms));
+       print("\n");
+    }
+}
+
+my $rfc822re;
+
+sub make_rfc822re {
+#   Basic lexical tokens are specials, domain_literal, quoted_string, atom, and
+#   comment.  We must allow for rfc822_lwsp (or comments) after each of these.
+#   This regexp will only work on addresses which have had comments stripped
+#   and replaced with rfc822_lwsp.
+
+    my $specials = '()<>@,;:\\\\".\\[\\]';
+    my $controls = '\\000-\\037\\177';
+
+    my $dtext = "[^\\[\\]\\r\\\\]";
+    my $domain_literal = "\\[(?:$dtext|\\\\.)*\\]$rfc822_lwsp*";
+
+    my $quoted_string = "\"(?:[^\\\"\\r\\\\]|\\\\.|$rfc822_lwsp)*\"$rfc822_lwsp*";
+
+#   Use zero-width assertion to spot the limit of an atom.  A simple
+#   $rfc822_lwsp* causes the regexp engine to hang occasionally.
+    my $atom = "[^$specials $controls]+(?:$rfc822_lwsp+|\\Z|(?=[\\[\"$specials]))";
+    my $word = "(?:$atom|$quoted_string)";
+    my $localpart = "$word(?:\\.$rfc822_lwsp*$word)*";
+
+    my $sub_domain = "(?:$atom|$domain_literal)";
+    my $domain = "$sub_domain(?:\\.$rfc822_lwsp*$sub_domain)*";
+
+    my $addr_spec = "$localpart\@$rfc822_lwsp*$domain";
+
+    my $phrase = "$word*";
+    my $route = "(?:\@$domain(?:,\@$rfc822_lwsp*$domain)*:$rfc822_lwsp*)";
+    my $route_addr = "\\<$rfc822_lwsp*$route?$addr_spec\\>$rfc822_lwsp*";
+    my $mailbox = "(?:$addr_spec|$phrase$route_addr)";
+
+    my $group = "$phrase:$rfc822_lwsp*(?:$mailbox(?:,\\s*$mailbox)*)?;\\s*";
+    my $address = "(?:$mailbox|$group)";
+
+    return "$rfc822_lwsp*$address";
+}
+
+sub rfc822_strip_comments {
+    my $s = shift;
+#   Recursively remove comments, and replace with a single space.  The simpler
+#   regexps in the Email Addressing FAQ are imperfect - they will miss escaped
+#   chars in atoms, for example.
+
+    while ($s =~ s/^((?:[^"\\]|\\.)*
+                    (?:"(?:[^"\\]|\\.)*"(?:[^"\\]|\\.)*)*)
+                    \((?:[^()\\]|\\.)*\)/$1 /osx) {}
+    return $s;
+}
+
+#   valid: returns true if the parameter is an RFC822 valid address
+#
+sub rfc822_valid {
+    my $s = rfc822_strip_comments(shift);
+
+    if (!$rfc822re) {
+        $rfc822re = make_rfc822re();
+    }
+
+    return $s =~ m/^$rfc822re$/so && $s =~ m/^$rfc822_char*$/;
+}
+
+#   validlist: In scalar context, returns true if the parameter is an RFC822
+#              valid list of addresses.
+#
+#              In list context, returns an empty list on failure (an invalid
+#              address was found); otherwise a list whose first element is the
+#              number of addresses found and whose remaining elements are the
+#              addresses.  This is needed to disambiguate failure (invalid)
+#              from success with no addresses found, because an empty string is
+#              a valid list.
+
+sub rfc822_validlist {
+    my $s = rfc822_strip_comments(shift);
+
+    if (!$rfc822re) {
+        $rfc822re = make_rfc822re();
+    }
+    # * null list items are valid according to the RFC
+    # * the '1' business is to aid in distinguishing failure from no results
+
+    my @r;
+    if ($s =~ m/^(?:$rfc822re)?(?:,(?:$rfc822re)?)*$/so &&
+       $s =~ m/^$rfc822_char*$/) {
+        while ($s =~ m/(?:^|,$rfc822_lwsp*)($rfc822re)/gos) {
+            push(@r, $1);
+        }
+        return wantarray ? (scalar(@r), @r) : 1;
+    }
+    return wantarray ? () : 0;
+}
index 414cb538c21a9ec91989b7a40371a3ff1a2ff669..dcf896e0a2216810787f328f4eb8eab9cee2e981 100755 (executable)
@@ -62,8 +62,8 @@ ENDL
 
        # Aggregate the groups each test belongs to for the group file
        grep -I -R "^_begin_fstest" $test_dir/ | \
-               sed -e "s/^.*\/\($VALID_TEST_NAME\):_begin_fstest/\1/" \
-               >> $new_groups
+               sed -e "s/^.*\/\($VALID_TEST_NAME\):_begin_fstest/\1/" \
+               sort -ug >> $new_groups
 
        # Create the list of unique groups for existence checking
        grep -I -R "^_begin_fstest" $test_dir/ | \
index 99b1541429317ff6fcf2438b4173a520673cf865..09a9982b7f807e8c766c3c00139bcd2917b3fcda 100755 (executable)
@@ -34,6 +34,8 @@ did="$(basename "${dest}")"
 
 git mv "tests/${src}" "tests/${dest}"
 git mv "tests/${src}.out" "tests/${dest}.out"
+# make sure testcase is executable
+chmod a+x "tests/${dest}"
 sed -e "s/^# FS[[:space:]]*QA.*Test.*[0-9]\+$/# FS QA Test No. ${did}/g" -i "tests/${dest}"
 sed -e "s/^QA output created by ${sid}$/QA output created by ${did}/g" -i "tests/${dest}.out"
 sed -e "s/test-${sid}/test-${did}/g" -i "tests/${dest}.out"