summaryrefslogtreecommitdiff
path: root/tests/t-local.sh
blob: 2ba7ee1591f027ea69e9c1dc85c59fffff83b9b4 (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
195
196
# Put test-related bits that are parted-specific here.
# This file is sourced from the testing framework.
sector_size_=${PARTED_SECTOR_SIZE:-512}

scsi_debug_lock_dir_="$abs_srcdir/scsi_debug.lock"

require_scsi_debug_module_()
{
  require_udevadm_settle_
  # check for scsi_debug module
  modprobe -n scsi_debug ||
    skip_ "you lack the scsi_debug kernel module"
}

scsi_debug_modprobe_succeeded_=

# Always run this cleanup function.
cleanup_final_() { scsi_debug_cleanup_; }

scsi_debug_cleanup_()
{
  # This function must always release the lock.
  # If modprobe succeeded, it must be sure to run rmmod.
  if test -n "$scsi_debug_modprobe_succeeded_"; then
    # We have to insist.  Otherwise, a single rmmod usually fails to remove it,
    # due either to "Resource temporarily unavailable" or to
    # "Module scsi_debug is in use".
    i=0
    udevadm settle
    while [ $i -lt 40 ] ; do
      rmmod scsi_debug \
	&& { test "$VERBOSE" = yes && warn_ $ME_ rmmod scsi_debug...; break; }
      sleep .2 || sleep 1
      i=$((i + 1))
    done
    udevadm settle
    test $i = 40 && framework_failure_ rmmod scsi_debug failed.
  fi
  rm -fr $scsi_debug_lock_dir_
}

# Helper function: wait 2s (via .1s increments) for FILE to appear.
# Usage: wait_for_dev_to_appear_ /dev/sdg
# Return 0 upon success, 1 upon failure.
wait_for_dev_to_appear_()
{
  local file=$1
  local i=0
  local incr=1
  while :; do
    udevadm settle
    ls "$file" > /dev/null 2>&1 && return 0
    sleep .1 2>/dev/null || { sleep 1; incr=10; }
    i=$(expr $i + $incr); test $i = 20 && break
  done
  return 1
}

# Tests that uses "modprobe scsi_debug ..." must not be run in parallel.
scsi_debug_acquire_lock_()
{
  local retries=20
  local lock_timeout_stale_seconds=120

  # If it was created more than $lock_timeout_stale_seconds ago, remove it.
  # FIXME: implement this

  local i=0
  local incr=1
  while :; do
    mkdir "$scsi_debug_lock_dir_" && return 0
    sleep .1 2>/dev/null || { sleep 1; incr=10; }
    i=$(expr $i + $incr); test $i = $(expr $retries \* 10) && break
  done

  warn_ "$ME_: failed to acquire lock: $scsi_debug_lock_dir_"
  return 1
}

# If there is a scsi_debug device, print the corresponding "sdN" and return 0.
# Otherwise, return 1.
new_sdX_()
{
  local m; m=$(grep -lw scsi_debug /sys/block/sd*/device/model) || return 1

  # Remove the /sys/block/ prefix, and then the /device/model suffix.
  m=${m#/sys/block/}
  m=${m%/device/model}
  echo "$m"
  return 0
}

# Create a device using the scsi_debug module with the options passed to
# this function as arguments.  Upon success, print the name of the new device.
scsi_debug_setup_()
{
  scsi_debug_acquire_lock_

  # It is not trivial to determine the name of the device we're creating.
  # Record the names of all /sys/block/sd* devices *before* probing:
  touch stamp
  modprobe scsi_debug opt_blks=64 "$@" || { rm -f stamp; return 1; }
  scsi_debug_modprobe_succeeded_=1
  test "$VERBOSE" = yes \
    && echo $ME_ modprobe scsi_debug succeeded 1>&2

  # Wait up to 2s (via .1s increments) for the list of devices to change.
  # Sleeping for a fraction of a second requires GNU sleep, so fall
  # back on sleeping 2x1s if that fails.
  # FIXME-portability: using "cmp - ..." probably requires GNU cmp.
  local incr=1
  local i=0
  local new_dev
  while :; do
    udevadm settle
    new_dev=$(new_sdX_) && break
    sleep .1 2>/dev/null || { sleep 1; incr=10; }
    i=$(expr $i + $incr); test $i = 20 && break
  done

  case $new_dev in
    sd[a-z]) ;;
    sd[a-z][a-z]) ;;
    *) warn_ $ME_ unexpected device name: $new_dev; return 1 ;;
  esac
  local t=/dev/$new_dev
  wait_for_dev_to_appear_ $t
  echo $t
  return 0
}

require_512_byte_sector_size_()
{
  test $sector_size_ = 512 \
      || skip_ FS test with sector size != 512
}

peek_()
{
  case $# in 2) ;; *) echo "usage: peek_ FILE 0_BASED_OFFSET" >&2; exit 1;; esac
  case $2 in *[^0-9]*) echo "peek_: invalid offset: $2" >&2; exit 1 ;; esac
  dd if="$1" bs=1 skip="$2" count=1
}

poke_()
{
  case $# in 3) ;; *) echo "usage: poke_ FILE 0_BASED_OFFSET BYTE" >&2; exit 1;;
    esac
  case $2 in *[^0-9]*) echo "poke_: invalid offset: $2" >&2; exit 1 ;; esac
  case $3 in ?) ;; *) echo "poke_: invalid byte: '$3'" >&2; exit 1 ;; esac
  printf %s "$3" | dd of="$1" bs=1 seek="$2" count=1 conv=notrunc
}

# byte 56 of the partition entry is the first byte of its 72-byte name field
gpt1_pte_name_offset_()
{
  local ss=$1
  case $ss in *[^0-9]*) echo "$0: invalid sector size: $ss">&2; return 1;; esac
  expr $ss \* 2 + 56
  return 0
}

# Change the name of the first partition in the primary GPT table,
# thus invalidating the PartitionEntryArrayCRC32 checksum.
gpt_corrupt_primary_table_()
{
  case $# in 2) ;; *) echo "$0: expected 2 args, got $#" >&2; return 1;; esac
  local dev=$1
  local ss=$2
  case $ss in *[^0-9]*) echo "$0: invalid sector size: $ss">&2; return 1;; esac

  # get the first byte of the name
  local orig_pte_name_byte
  orig_pte_name_byte=$(peek_ $dev $(gpt1_pte_name_offset_ $ss)) || return 1

  local new_byte
  test x"$orig_pte_name_byte" = xA && new_byte=B || new_byte=A

  # Replace with a different byte
  poke_ $dev $(gpt1_pte_name_offset_ $ss) "$new_byte" || return 1

  printf %s "$orig_pte_name_byte"
  return 0
}

gpt_restore_primary_table_()
{
  case $# in 3) ;; *) echo "$0: expected 2 args, got $#" >&2; return 1;; esac
  local dev=$1
  local ss=$2
  case $ss in *[^0-9]*) echo "$0: invalid sector size: $ss">&2; return 1;; esac
  local orig_byte=$3
  poke_ $dev $(gpt1_pte_name_offset_ $ss) "$orig_byte" || return 1
}

. "$abs_top_srcdir/tests/t-lvm.sh"