summaryrefslogtreecommitdiffstats
path: root/qa/workunits/fs/damage/test-first-damage.sh
blob: 57447b957d789a357b8d79eb06172e09c96a2c0f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#!/bin/bash

set -ex

FIRST_DAMAGE="first-damage.py"
FS=cephfs
METADATA_POOL=cephfs_meta
MOUNT=~/mnt/mnt.0
PYTHON=python3

function usage {
  printf '%s: [--fs=<fs_name>] [--metadata-pool=<pool>] [--first-damage=</path/to/first-damage.py>]\n'
  exit 1
}


function create {
  ceph config set mds mds_bal_fragment_dirs 0
  mkdir dir
  DIR_INODE=$(stat -c '%i' dir)
  touch dir/a
  touch dir/"a space"
  touch -- $(printf 'dir/\xff')
  mkdir dir/.snap/1
  mkdir dir/.snap/2
  # two snaps
  rm dir/a
  mkdir dir/.snap/3
  # not present in HEAD
  touch dir/a
  mkdir dir/.snap/4
  # one snap
  rm dir/a
  touch dir/a
  mkdir dir/.snap/5
  # unlink then create
  rm dir/a
  touch dir/a
  # unlink then create, HEAD not snapped
  ls dir/.snap/*/
  mkdir big
  BIG_DIR_INODE=$(stat -c '%i' big)
  for i in `seq 1 15000`; do
    touch $(printf 'big/%08d' $i)
  done
}

function flush {
  ceph tell mds."$FS":0 flush journal
}

function damage {
  local IS=$(printf '%llx.%08llx' "$DIR_INODE" 0)
  local LS=$(ceph tell mds."$FS":0 dump snaps | jq .last_created)

  local T=$(mktemp -p /tmp)

  # nuke snap 1 version of "a"
  rados --pool="$METADATA_POOL" getomapval "$IS" a_$(printf %x $((LS-4))) "$T"
  printf '\xff\xff\xff\xf0' | dd of="$T" count=4 bs=1 conv=notrunc,nocreat
  rados --pool="$METADATA_POOL" setomapval "$IS" a_$(printf %x $((LS-4))) --input-file="$T"

  # nuke snap 4 version of "a"
  rados --pool="$METADATA_POOL" getomapval "$IS" a_$(printf %x $((LS-1))) "$T"
  printf '\xff\xff\xff\xff' | dd of="$T" count=4 bs=1 conv=notrunc,nocreat
  rados --pool="$METADATA_POOL" setomapval "$IS" a_$(printf %x $((LS-1))) --input-file="$T"

  # screw up HEAD
  rados --pool="$METADATA_POOL" getomapval "$IS" a_head "$T"
  printf '\xfe\xff\xff\xff' | dd of="$T" count=4 bs=1 conv=notrunc,nocreat
  rados --pool="$METADATA_POOL" setomapval "$IS" a_head --input-file="$T"

  # screw up HEAD on what dentry in big
  IS=$(printf '%llx.%08llx' "$BIG_DIR_INODE" 0)
  rados --pool="$METADATA_POOL" getomapval "$IS" 00009999_head "$T"
  printf '\xfe\xff\xff\xff' | dd of="$T" count=4 bs=1 conv=notrunc,nocreat
  rados --pool="$METADATA_POOL" setomapval "$IS" 00009999_head --input-file="$T"

  rm -f "$T"
}

function recover {
  flush
  ceph fs fail "$FS"
  sleep 5
  cephfs-journal-tool --rank="$FS":0 event recover_dentries summary
  cephfs-journal-tool --rank="$FS":0 journal reset
  "$PYTHON" $FIRST_DAMAGE --debug /tmp/debug1 --memo /tmp/memo1 "$METADATA_POOL"
  "$PYTHON" $FIRST_DAMAGE --debug /tmp/debug2 --memo /tmp/memo2 --repair-nosnap  "$METADATA_POOL"
  "$PYTHON" $FIRST_DAMAGE --debug /tmp/debug3 --memo /tmp/memo3 --remove "$METADATA_POOL"
  ceph fs set "$FS" joinable true
}

function check {
  stat dir || exit 1
  stat dir/a || exit 1
  for i in `seq 1 5`; do
    stat dir/.snap/$i || exit 2
  done
  stat dir/.snap/2/a || exit 3
  stat dir/.snap/5/a || exit 4
  if stat dir/.snap/1/a; then
    echo should be gone
    exit 5
  fi
  if stat dir/.snap/3/a; then
    echo should not ever exist
    exit 6
  fi
  if stat dir/.snap/4/a; then
    echo should be gone
    exit 7
  fi
}

function cleanup {
  rmdir dir/.snap/*
  find dir
  rm -rf dir
}

function mount {
  sudo --preserve-env=CEPH_CONF bin/mount.ceph :/ "$MOUNT" -o name=admin,noshare
  df -h "$MOUNT"
}

function main {
  eval set -- $(getopt --name "$0" --options '' --longoptions 'help,fs:,metadata-pool:,first-damage:,mount:,python:' -- "$@")

  while [ "$#" -gt 0 ]; do
      echo "$*"
      echo "$1"
      case "$1" in
          -h|--help)
              usage
              ;;
          --fs)
              FS="$2"
              shift 2
              ;;
          --metadata-pool)
              METADATA_POOL="$2"
              shift 2
              ;;
          --mount)
              MOUNT="$2"
              shift 2
              ;;
          --first-damage)
              FIRST_DAMAGE="$2"
              shift 2
              ;;
          --python)
              PYTHON="$2"
              shift 2
              ;;
          --)
              shift
              break
              ;;
          *)
              usage
              ;;
      esac
  done

  mount

  pushd "$MOUNT"
  create
  popd

  sudo umount -f "$MOUNT"

  # flush dentries/inodes to omap
  flush

  damage

  recover

  sleep 5 # for mds to join

  mount

  pushd "$MOUNT"
  check
  cleanup
  popd

  sudo umount -f "$MOUNT"
}

main "$@"