+OLDIFS="$IFS"
+cleanup_49() {
+ trap 0
+ IFS="$OLDIFS"
+}
+
+test_49a() {
+ [ "$OSTCOUNT" -lt "2" ] && skip_env "needs >= 2 OSTs"
+
+ trap cleanup_49 EXIT RETURN
+
+ local file=$DIR/$tfile
+
+ $LFS setstripe -N -E eof -c1 -o1 -N -E eof -c1 -o0 $file ||
+ error "setstripe on $file"
+
+ dd if=/dev/zero of=$file bs=1M count=1 || error "dd failed for $file"
+ $LFS mirror resync $file
+
+ filefrag -ves $file || error "filefrag $file failed"
+ filefrag_op=$(filefrag -ve -k $file |
+ sed -n '/ext:/,/found/{/ext:/d; /found/d; p}')
+
+#Filesystem type is: bd00bd0
+#File size of /mnt/lustre/f49a.sanity-flr is 1048576 (1024 blocks of 1024 bytes)
+# ext: device_logical: physical_offset: length: dev: flags:
+# 0: 0.. 1023: 1572864.. 1573887: 1024: 0001: net,eof
+# 1: 0.. 1023: 1572864.. 1573887: 1024: 0000: last,net,eof
+#/mnt/lustre/f49a.sanity-flr: 2 extents found
+
+ last_lun=$(echo $filefrag_op | cut -d: -f5)
+ IFS=$'\n'
+ tot_len=0
+ num_luns=1
+ for line in $filefrag_op; do
+ frag_lun=$(echo $line | cut -d: -f5)
+ ext_len=$(echo $line | cut -d: -f4)
+ if [[ "$frag_lun" != "$last_lun" ]]; then
+ if (( tot_len != 1024 )); then
+ cleanup_49
+ error "$file: OST$last_lun $tot_len != 1024"
+ else
+ (( num_luns += 1 ))
+ tot_len=0
+ fi
+ fi
+ (( tot_len += ext_len ))
+ last_lun=$frag_lun
+ done
+ if (( num_luns != 2 || tot_len != 1024 )); then
+ cleanup_49
+ error "$file: $num_luns != 2, $tot_len != 1024 on OST$last_lun"
+ fi
+
+ echo "FIEMAP on $file succeeded"
+}
+run_test 49a "FIEMAP upon FLR file"
+
+test_50A() { # EX-2179
+ mkdir -p $DIR/$tdir
+
+ local file=$DIR/$tdir/$tfile
+
+ $LFS setstripe -c1 -i0 $file || error "setstripe $file failed"
+
+ $LFS mirror extend -N -c1 -i1 $file ||
+ error "extending mirror for $file failed"
+
+ local olv=$($LFS getstripe $file | awk '/lcm_layout_gen/{print $2}')
+
+ fail mds1
+
+ $LFS mirror split -d --mirror-id=1 $file || error "split $file failed"
+
+ local flv=$($LFS getstripe $file | awk '/lcm_layout_gen/{print $2}')
+
+ echo "$file layout generation from $olv to $flv"
+ (( $flv != ($olv + 1) )) &&
+ error "split does not increase layout gen from $olv to $flv"
+
+ dd if=/dev/zero of=$file bs=1M count=1 || error "write $file failed"
+
+ $LFS getstripe -v $file || error "getstripe $file failed"
+}
+run_test 50A "mirror split update layout generation"
+
+test_50a() {
+ $LCTL get_param osc.*.import | grep -q 'connect_flags:.*seek' ||
+ skip "OST does not support SEEK_HOLE"
+
+ local file=$DIR/$tdir/$tfile
+ local offset
+ local sum1
+ local sum2
+ local blocks
+
+ mkdir -p $DIR/$tdir
+
+ echo " ** create striped file $file"
+ $LFS setstripe -E 1M -c1 -S 1M -E eof -c2 -S1M $file ||
+ error "cannot create file with PFL layout"
+ echo " ** write 1st data chunk at 1M boundary"
+ dd if=/dev/urandom of=$file bs=1k count=20 seek=1021 ||
+ error "cannot write data at 1M boundary"
+ echo " ** write 2nd data chunk at 2M boundary"
+ dd if=/dev/urandom of=$file bs=1k count=20 seek=2041 ||
+ error "cannot write data at 2M boundary"
+ echo " ** create hole at the file end"
+ $TRUNCATE $file 3700000 || error "truncate fails"
+
+ echo " ** verify sparseness"
+ offset=$(lseek_test -d 1000 $file)
+ echo " first data offset: $offset"
+ [[ $offset == 1000 ]] &&
+ error "src: data is not expected at offset $offset"
+ offset=$(lseek_test -l 3500000 $file)
+ echo " hole at the end: $offset"
+ [[ $offset == 3500000 ]] ||
+ error "src: hole is expected at offset $offset"
+
+ echo " ** extend the file with new mirror"
+ # migrate_copy_data() is used
+ $LFS mirror extend -N -E 2M -S 1M -E 1G -S 2M -E eof $file ||
+ error "cannot create mirror"
+ $LFS getstripe $file | grep lcme_flags | grep stale > /dev/null &&
+ error "$file still has stale component"
+
+ # check migrate_data_copy() was correct
+ sum_1=$($LFS mirror read -N 1 $file | md5sum)
+ sum_2=$($LFS mirror read -N 2 $file | md5sum)
+ [[ $sum_1 == $sum_2 ]] ||
+ error "data mismatch: \'$sum_1\' vs. \'$sum_2\'"
+
+ # stale first mirror
+ $LFS setstripe --comp-set -I0x10001 --comp-flags=stale $file
+ $LFS setstripe --comp-set -I0x10002 --comp-flags=stale $file
+
+ echo " ** verify mirror #2 sparseness"
+ offset=$(lseek_test -d 1000 $file)
+ echo " first data offset: $offset"
+ [[ $offset == 1000 ]] &&
+ error "dst: data is not expected at offset $offset"
+ offset=$(lseek_test -l 3500000 $file)
+ echo " hole at the end: $offset"
+ [[ $offset == 3500000 ]] ||
+ error "dst: hole is expected at offset $offset"
+
+ echo " ** copy mirror #2 to mirror #1"
+ $LFS mirror copy -i 2 -o 1 $file || error "mirror copy fails"
+ $LFS getstripe $file | grep lcme_flags | grep stale > /dev/null &&
+ error "$file still has stale component"
+
+ # check llapi_mirror_copy_many correctness
+ sum_1=$($LFS mirror read -N 1 $file | md5sum)
+ sum_2=$($LFS mirror read -N 2 $file | md5sum)
+ [[ $sum_1 == $sum_2 ]] ||
+ error "data mismatch: \'$sum_1\' vs. \'$sum_2\'"
+
+ # stale 1st component of mirror #2 before lseek call
+ $LFS setstripe --comp-set -I0x20001 --comp-flags=stale $file
+
+ echo " ** verify mirror #1 sparseness again"
+ offset=$(lseek_test -d 1000 $file)
+ echo " first data offset: $offset"
+ [[ $offset == 1000 ]] &&
+ error "dst: data is not expected at offset $offset"
+ offset=$(lseek_test -l 3500000 $file)
+ echo " hole at the end: $offset"
+ [[ $offset == 3500000 ]] ||
+ error "dst: hole is expected at offset $offset"
+
+ cancel_lru_locks osc
+
+ blocks=$(stat -c%b $file)
+ echo " ** final consumed blocks: $blocks"
+ # for 3.5Mb file consumes ~6000 blocks, use 1000 to check
+ # that file is still sparse
+ (( blocks < 1000 )) ||
+ error "Mirrored file consumes $blocks blocks"
+
+ rm $file
+}
+run_test 50a "mirror extend/copy preserves sparseness"
+
+test_50b() {
+ $LCTL get_param osc.*.import | grep -q 'connect_flags:.*seek' ||
+ skip "OST does not support SEEK_HOLE"
+
+ local file=$DIR/$tdir/$tfile
+ local offset
+ local sum1
+ local sum2
+ local blocks
+
+ mkdir -p $DIR/$tdir
+
+ echo " ** create mirrored file $file"
+ $LFS mirror create -N -E1M -c1 -S1M -E eof \
+ -N -E2M -S1M -E eof -S2M $file ||
+ error "cannot create mirrored file"
+ echo " ** write data chunk at 1M boundary"
+ dd if=/dev/urandom of=$file bs=1k count=20 seek=1021 ||
+ error "cannot write data at 1M boundary"
+ echo " ** create hole at the file end"
+ $TRUNCATE $file 3700000 || error "truncate fails"
+
+ echo " ** verify sparseness"
+ offset=$(lseek_test -d 1000 $file)
+ echo " first data offset: $offset"
+ [[ $offset == 1000 ]] &&
+ error "src: data is not expected at offset $offset"
+ offset=$(lseek_test -l 3500000 $file)
+ echo " hole at the end: $offset"
+ [[ $offset == 3500000 ]] ||
+ error "src: hole is expected at 3500000"
+
+ echo " ** resync mirror #2 to mirror #1"
+ $LFS mirror resync $file
+
+ # check llapi_mirror_copy_many correctness
+ sum_1=$($LFS mirror read -N 1 $file | md5sum)
+ sum_2=$($LFS mirror read -N 2 $file | md5sum)
+ [[ $sum_1 == $sum_2 ]] ||
+ error "data mismatch: \'$sum_1\' vs. \'$sum_2\'"
+
+ cancel_lru_locks osc
+
+ blocks=$(stat -c%b $file)
+ echo " ** consumed blocks: $blocks"
+ # without full punch() support the first component can be not sparse
+ # but the last one should be, so file should use far fewer blocks
+ (( blocks < 5000 )) ||
+ error "Mirrored file consumes $blocks blocks"
+
+ # stale first component in mirror #1
+ $LFS setstripe --comp-set -I0x10001 --comp-flags=stale,nosync $file
+ echo " ** truncate file down"
+ $TRUNCATE $file 0
+ echo " ** write data chunk at 2M boundary"
+ dd if=/dev/urandom of=$file bs=1k count=20 seek=2041 conv=notrunc ||
+ error "cannot write data at 2M boundary"
+ echo " ** resync mirror #2 to mirror #1 with nosync 1st component"
+ $LFS mirror resync $file || error "mirror rsync fails"
+ # first component is still stale
+ $LFS getstripe $file | grep 'lcme_flags:.*stale' > /dev/null ||
+ error "$file still has no stale component"
+ echo " ** resync mirror #2 to mirror #1 again"
+ $LFS setstripe --comp-set -I0x10001 --comp-flags=stale,^nosync $file
+ $LFS mirror resync $file || error "mirror rsync fails"
+ $LFS getstripe $file | grep 'lcme_flags:.*stale' > /dev/null &&
+ error "$file still has stale component"
+
+ # check llapi_mirror_copy_many correctness
+ sum_1=$($LFS mirror read -N 1 $file | md5sum)
+ sum_2=$($LFS mirror read -N 2 $file | md5sum)
+ [[ $sum_1 == $sum_2 ]] ||
+ error "data mismatch: \'$sum_1\' vs. \'$sum_2\'"
+
+ cancel_lru_locks osc
+
+ blocks=$(stat -c%b $file)
+ echo " ** final consumed blocks: $blocks"
+ # while the first component can lose sparseness, the last one should
+ # not, so whole file should still use far fewer blocks in total
+ (( blocks < 3000 )) ||
+ error "Mirrored file consumes $blocks blocks"
+ rm $file
+}
+run_test 50b "mirror rsync handles sparseness"
+
+test_50c() {
+ local tf=$DIR/$tdir/$tfile
+
+ test_mkdir $DIR/$tdir
+
+ $LFS setstripe -N2 -c-1 $tf || error "create FLR $tf failed"
+ verify_flr_state $tf "ro"
+
+ if [[ "$FSTYPE" == "ldiskfs" ]]; then
+ # ZFS does not support fallocate for now
+ out=$(fallocate -p -o 1MiB -l 1MiB $tf 2>&1) ||
+ skip_eopnotsupp "$out|punch hole in $tf failed"
+ verify_flr_state $tf "wp"
+ fi
+
+ dd if=/dev/zero of=$tf bs=4096 count=4 || error "write $tf failed"
+ $LFS mirror resync $tf || error "mirror resync $tf failed"
+ verify_flr_state $tf "ro"
+
+ $MULTIOP $tf OSMWUc || error "$MULTIOP $tf failed"
+ verify_flr_state $tf "wp"
+}
+run_test 50c "punch_hole/mmap_write stale other mirrors"
+
+test_50d() {
+ $LCTL get_param osc.*.import | grep -q 'connect_flags:.*seek' ||
+ skip "OST does not support SEEK_HOLE"
+ (( $LINUX_VERSION_CODE > $(version_code 3.0.0) )) ||
+ skip "client kernel does not support SEEK_HOLE"
+
+ local file=$DIR/$tdir/$tfile
+ local offset
+ local prt
+ local rc
+
+ mkdir -p $DIR/$tdir
+
+ echo " ** create mirrored file $file"
+ $LFS mirror create -N -E1M -c1 -S1M -E eof \
+ -N -E2M -S1M -E eof -S2M $file ||
+ error "cannot create mirrored file"
+ echo " ** write data chunk at 1M boundary"
+ dd if=/dev/urandom of=$file bs=1k count=20 seek=1021 ||
+ error "cannot write data at 1M boundary"
+ echo " ** create hole at the file start"
+ prt=$(fallocate -p -o 0 -l 1M $file 2>&1)
+ rc=$?
+
+ if [[ $rc -eq 0 ]]; then
+ verify_flr_state $file "wp"
+ elif [[ ! $prt =~ unsupported ]]; then
+ error "punch hole in $file failed: $prt"
+ else
+ skip "Fallocate punch is not supported: $prt"
+ fi
+
+ echo " ** verify sparseness"
+ offset=$(lseek_test -d 1000 $file)
+ echo " first data offset: $offset"
+ (( $offset >= 1024 * 1024 )) ||
+ error "src: data is not expected at offset $offset"
+
+ echo " ** resync mirror #2"
+ $LFS mirror resync $file
+
+ # check llapi_mirror_copy_many correctness
+ sum_1=$($LFS mirror read -N 1 $file | md5sum)
+ sum_2=$($LFS mirror read -N 2 $file | md5sum)
+ [[ $sum_1 == $sum_2 ]] ||
+ error "data mismatch: \'$sum_1\' vs. \'$sum_2\'"
+
+ cancel_lru_locks osc
+
+ # stale first component in mirror #1
+ $LFS setstripe --comp-set -I0x10001 --comp-flags=stale,nosync $file
+ echo " ** verify sparseness of mirror #2"
+ offset=$(lseek_test -d 1000 $file)
+ echo " first data offset: $offset"
+ (( $offset >= 1024 * 1024 )) ||
+ error "src: data is not expected at offset $offset"
+}
+run_test 50d "mirror rsync keep holes"
+
+test_60a() {
+ $LCTL get_param osc.*.import | grep -q 'connect_flags:.*seek' ||
+ skip "OST does not support SEEK_HOLE"
+
+ local file=$DIR/$tdir/$tfile
+ local old_size=2147483648 # 2GiB
+ local new_size
+
+ mkdir -p $DIR/$tdir
+ dd if=/dev/urandom of=$file bs=4096 count=1 seek=$((134217728 / 4096))
+ $TRUNCATE $file $old_size
+
+ $LFS mirror extend -N -c 1 $file
+ dd if=/dev/urandom of=$file bs=4096 count=1 seek=$((134217728 / 4096)) conv=notrunc
+ $LFS mirror resync $file
+
+ new_size=$(stat --format='%s' $file)
+ if ((new_size != old_size)); then
+ error "new_size ($new_size) is not equal to old_size ($old_size)"
+ fi
+
+ rm $file
+}
+run_test 60a "mirror extend sets correct size on sparse file"
+
+get_flr_layout_gen() {
+ getfattr -n lustre.lov --only-values $tf 2>/dev/null |
+ od -tx4 | awk '/000000/ { print "0x"$4; exit; }'
+}
+
+check_layout_gen() {
+ local tf=$1
+
+ local v1=$(get_flr_layout_gen $tf)
+ local v2=$($LFS getstripe -v $tf | awk '/lcm_layout_gen/ { print $2 }')
+
+ [[ $v1 -eq $v2 ]] ||
+ error "$tf in-memory layout gen $v1 != $v2 after $2"
+}
+
+test_60b() {
+ local tf=$DIR/$tdir/$tfile
+
+ test_mkdir $DIR/$tdir
+
+ $LFS setstripe -Eeof $tf || error "setstripe $tf failed"
+
+ for ((i = 0; i < 20; i++)); do
+ $LFS mirror extend -N $tf ||
+ error "extending mirror for $tf failed"
+ check_layout_gen $tf "extend"
+
+ $LFS mirror split -d --mirror-id=$((i+1)) $tf ||
+ error "split $tf failed"
+ check_layout_gen $tf "split"
+ done
+}
+run_test 60b "mirror merge/split cancel client's in-memory layout gen"
+
+get_times_61() {
+ stat --format='%X %Y %Z' $file || error "$file: cannot get times"
+}
+
+check_times_61() {
+ local file=$1
+ local -a old=( $2 $3 $4 )
+ local -a new
+
+ new=( $(get_times_61 $file) )
+ ((${old[0]} == ${new[0]})) ||
+ error "$file: atime: old '${old[0]}' != new '${new[0]}'"
+
+ ((${old[1]} == ${new[1]})) ||
+ error "$file: mtime: old '${old[1]}' != new '${new[1]}'"
+}
+
+test_61a() { # LU-14508
+ local file=$DIR/$tdir/$tfile
+ local -a tim
+ local nap=5
+
+ mkdir -p $DIR/$tdir
+ echo "create $file"
+ $LFS setstripe -E1M -Eeof $file || error "setstripe $file failed"
+ echo "create $file-2"
+ $LFS setstripe -E2M -Eeof $file-2 || error "setstripe $file-2 failed"
+
+ echo XXX > $file || error "write $file failed"
+ chown $RUNAS_ID $DIR/$tdir $file || error "chown $file failed"
+
+ echo "sleep $nap seconds, then cat $tfile"
+ sleep $nap
+ cat $file || error "cat $file failed"
+
+ echo "sleep $nap seconds, then re-write $tfile"
+ sleep $nap
+ echo XXXX > $file || error "write $file failed"
+ cp -p $file $file-2 || error "copy $file-2 failed"
+
+ echo "sleep $nap seconds"
+ sleep $nap
+
+ tim=( $(get_times_61 $file) )
+
+ echo "mirror merge $tfile-2 to $tfile and test timestamps"
+ $LFS mirror extend -N -f $file-2 $file ||
+ error "cannot mirror merge $file-2 to $file"
+ check_times_61 $file "${tim[@]}"
+
+ echo "mirror extend $tfile and test timestamps"
+ $LFS mirror extend -N -c1 -i1 $file ||
+ error "cannot extend mirror $file"
+ check_times_61 $file "${tim[@]}"
+
+ echo "migrate $tfile and test timestamps"
+ $LFS migrate -n $file || error "cannot migrate $file"
+ check_times_61 $file "${tim[@]}"
+
+ echo "normal user migrate $tfile and test timestamps"
+ $RUNAS $LFS migrate -n $file || error "cannot migrate $file"
+ check_times_61 $file "${tim[@]}"
+}
+run_test 61a "mirror extend and migrate preserve timestamps"
+
+test_61b() { # LU-14508
+ local file=$DIR/$tdir/$tfile
+ local -a tim
+ local nap=5
+
+ mkdir -p $DIR/$tdir
+ echo "create $file"
+ echo XXX > $file || error "create $file failed"
+ chown $RUNAS_ID $DIR/$tdir $file || error "chown $file failed"
+
+ echo "sleep $nap seconds, then cat $tfile"
+ sleep $nap
+ cat $file || error "cat $file failed"
+
+ echo "sleep $nap seconds, then re-write $tfile"
+ sleep $nap
+ echo XXXX > $file || error "write $file failed"
+
+ echo "sleep $nap seconds, then test timestamps"
+ sleep $nap
+
+ tim=( $(get_times_61 $file) )
+
+ echo "mirror extend $tfile and test timestamps"
+ $LFS mirror extend -N -c1 -i1 $file ||
+ error "cannot extend mirror $file"
+ check_times_61 $file "${tim[@]}"
+
+ echo "mirror split $tfile and test timestamps"
+ $LFS mirror split -d --mirror-id=1 $file ||
+ error "cannot split mirror 1 off $file"
+ check_times_61 $file "${tim[@]}"
+
+ echo "normal user mirror extend $tfile and test timestamps"
+ $RUNAS $LFS mirror extend -N -c1 -i1 $file ||
+ error "cannot extend mirror $file"
+ check_times_61 $file "${tim[@]}"
+}
+run_test 61b "mirror extend and split preserve timestamps"
+
+test_61c() { # LU-14508
+ local file=$DIR/$tdir/$tfile
+ local -a tim
+ local nap=5
+
+ mkdir -p $DIR/$tdir
+ echo "create $file"
+ echo XXX > $file || error "create $file failed"
+ chown $RUNAS_ID $DIR/$tdir $file || error "chown $file failed"
+
+ echo "sleep $nap seconds, then cat $tfile"
+ sleep $nap
+ cat $file || error "cat $file failed"
+
+ echo "sleep $nap seconds, then mirror extend $tfile and write it"
+ sleep $nap
+ $LFS mirror extend -N -c1 -i1 $file ||
+ error "cannot extend mirror $file"
+ echo XXXX > $file || error "write $file failed"
+
+ echo "sleep $nap seconds, then resync $tfile and test timestamps"
+ tim=( $(get_times_61 $file) )
+ sleep $nap
+ $LFS mirror resync $file || error "cannot resync mirror $file"
+ check_times_61 $file "${tim[@]}"
+
+ echo XXXXXX > $file || error "write $tfile failed"
+
+ echo "normal user resync $tfile and test timestamps"
+ tim=( $(get_times_61 $file) )
+ $RUNAS $LFS mirror resync $file || error "cannot resync mirror $file"
+ check_times_61 $file "${tim[@]}"
+}
+run_test 61c "mirror resync preserves timestamps"
+
+test_70() {
+ local tf=$DIR/$tdir/$tfile
+
+ test_mkdir $DIR/$tdir
+
+ while true; do
+ rm -f $tf
+ $LFS mirror create -N -E 1M -c -1 -E eof -N $tf
+ echo xxxx > $tf
+ done &
+ c_pid=$!
+ echo "mirror create pid $c_pid"
+
+ while true; do
+ $LFS mirror split -d --mirror-id=1 $tf &> /dev/null
+ done &
+ s_pid=$!
+ echo "mirror split pid $s_pid"
+
+ echo "mirror create and split race for 60 seconds, should not crash"
+ sleep 60
+ kill -9 $c_pid &> /dev/null
+ kill -9 $s_pid &> /dev/null
+
+ rm -f $tf
+ true
+}
+run_test 70 "mirror create and split race"
+
+test_100() {
+ local tf=$DIR/$tdir/$tfile
+
+ (( $OST1_VERSION >= $(version_code 2.14.51) )) ||
+ skip "Need OST version at least 2.14.51"
+
+
+ test_mkdir $DIR/$tdir
+
+ $LFS setstripe -N -E1M -c-1 -Eeof -c-1 $tf ||
+ error "setstripe $tf failed"
+
+ FSXNUM=${FSXNUM:-1000}
+ $FSX -p 5 -N $FSXNUM -S 0 -M $tf || error "fsx FLR file $tf failed"
+}
+run_test 100 "flr mode fsx test"
+