cfa277aa603a75da11c8c1f02b6bed8a6e7b4694
[libguestfs.git] / fuse / test-fuse.sh
1 #!/bin/bash -
2 # libguestfs
3 # Copyright (C) 2009-2011 Red Hat Inc.
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 unset CDPATH
20 set -e
21 #set -v
22
23 if [ -z "$top_builddir" ]; then
24     echo "$0: error: environment variable \$top_builddir must be set"
25     exit 1
26 fi
27
28 nr_stages=$(grep "^stage " $0 | wc -l)
29
30 # Allow top_builddir to be a relative path, but also make it absolute,
31 # and move to that directory for the initial phase of the script.
32 top_builddir=$(cd "$top_builddir" > /dev/null; pwd)
33
34 # Set TMPDIR so the appliance doesn't conflict with globally
35 # installed libguestfs.
36 export TMPDIR=$top_builddir
37
38 # Set libguestfs up for running locally.
39 export LIBGUESTFS_PATH="$top_builddir/appliance"
40
41 # Paths to the other programs and files.  NB: Must be absolute paths.
42 guestfish="$top_builddir/fish/guestfish"
43 guestmount="$top_builddir/fuse/guestmount"
44 image="$top_builddir/fuse/test.img"
45 mp="$top_builddir/fuse/test-mp"
46
47 if [ ! -x "$guestfish" -o ! -x "$guestmount" ]; then
48     echo "$0: error: guestfish or guestmount are not available"
49     exit 1
50 fi
51
52 # Ensure everything is cleaned up on exit.
53 rm -f "$image"
54 mkdir -p "$mp"
55 fusermount -u "$mp" >/dev/null 2>&1 ||:
56 function cleanup ()
57 {
58     status=$?
59     set +e
60     [ $status = 0 ] || echo "*** FAILED ***"
61     echo "Unmounting filesystem and cleaning up."
62
63     # Move out of the mountpoint (otherwise our cwd will prevent the
64     # mountpoint from being unmounted).
65     cd "$top_builddir"
66
67     # Who's using this?  Should be no one, but see below.
68     if [ -x /sbin/fuser ]; then /sbin/fuser "$mp"; fi
69
70     # If you run this and you have GNOME running at the same time,
71     # then randomly /usr/libexec/gvfs-gdu-volume-monitor will decide
72     # to do whatever it does in the mountpoint directory, preventing
73     # you from unmounting it!  Hence the need for this loop.
74     count=10
75     while ! fusermount -u "$mp" && [ $count -gt 0 ]; do
76         sleep 1
77         ((count--))
78     done
79
80     rm -f "$image"
81     rm -rf "$mp"
82     exit $status
83 }
84 trap cleanup INT TERM QUIT EXIT
85
86 s=1
87 function stage ()
88 {
89     echo "test-fuse: $s/$nr_stages:" "$@" "..."
90     ((s++))
91 }
92
93 stage Create filesystem with some initial content
94 $guestfish <<EOF
95   sparse "$image" 10M
96   run
97   part-disk /dev/sda mbr
98   mkfs ext2 /dev/sda1
99   mount_options acl,user_xattr /dev/sda1 /
100   write /hello.txt hello
101   write /world.txt "hello world"
102   touch /empty
103   touch /user_xattr
104   setxattr user.test hello123 8 /user_xattr
105   touch /acl
106   # XXX hack until libguestfs gets ACL support
107   debug sh "setfacl -m u:500:r /sysroot/acl" | cat > /dev/null
108 EOF
109
110 stage Mounting the filesystem
111 $guestmount \
112     -a "$image" -m /dev/sda1:/:acl,user_xattr \
113     -o uid="$(id -u)" -o gid="$(id -g)" "$mp"
114 # To debug guestmount, add this to the end of the preceding command:
115 # -v -x & sleep 60
116
117 stage Changing into mounted directory
118 cd "$mp"
119
120 stage Checking initial files exist
121 [ -n "$(echo *)" ]
122 [ "$(ls empty hello.txt world.txt)" = "empty
123 hello.txt
124 world.txt" ]
125
126 stage Checking initial files contain expected content
127 [ "$(cat hello.txt)" = "hello" ]
128 [ "$(cat world.txt)" = "hello world" ]
129 cat empty ;# should print nothing
130 [ -z "$(cat empty)" ]
131
132 stage Checking file modes of initial content
133 [ "$(stat -c %a empty)" = "644" ]
134 [ "$(stat -c %a hello.txt)" = "644" ]
135 [ "$(stat -c %a world.txt)" = "644" ]
136
137 stage Checking sizes of initial content
138 [ "$(stat -c %s empty)" -eq 0 ]
139 [ "$(stat -c %s hello.txt)" -eq 5 ]
140 [ "$(stat -c %s world.txt)" -eq 11 ]
141
142 stage Checking unlink
143 touch new
144 rm -f new ;# force because file is "owned" by root
145
146 stage Checking symbolic link
147 ln -s hello.txt symlink
148 [ -L symlink ]
149
150 stage Checking readlink
151 [ "$(readlink symlink)" = "hello.txt" ]
152
153 stage Checking hard link
154 [ "$(stat -c %h hello.txt)" -eq 1 ]
155 ln hello.txt link
156 [ "$(stat -c %h link)" -eq 2 ]
157 [ "$(stat -c %h hello.txt)" -eq 2 ]
158 rm -f link
159 [ ! -e link ]
160
161 # This fails because of caching.  The problem is that the linked file
162 # ("hello.txt") is cached with a link count of 2.  unlink("link")
163 # invalidates the cache for "link", but _not_ for "hello.txt" which
164 # still has the now-incorrect cached value.  However there's not much
165 # we can do about this since searching for all linked inodes of a file
166 # is an O(n) operation.
167 #[ "$(stat -c %h hello.txt)" -eq 1 ]
168
169 stage Checking mkdir
170 mkdir newdir
171 [ -d newdir ]
172
173 stage Checking rmdir
174 rmdir newdir
175 [ ! -e newdir ]
176
177 stage Checking rename
178 touch old
179 mv old new
180 [ -f new ]
181 [ ! -e old ]
182 rm -f new
183
184 stage Checking chmod
185 touch new
186 chmod a+x new
187 [ -x new ]
188 chmod a-x new
189 [ ! -x new ]
190 chmod a-w new
191 [ ! -w new ]
192 chmod a+w new
193 [ -w new ]
194 chmod a-r new
195 [ ! -r new ]
196 chmod a+r new
197 [ -r new ]
198 rm -f new
199
200 stage Checking truncate
201 truncate -s 10000 truncated
202 [ "$(stat -c %s truncated)" -eq 10000 ]
203 truncate -c -s 1000 truncated
204 [ "$(stat -c %s truncated)" -eq 1000 ]
205 truncate -c -s 10 truncated
206 [ "$(stat -c %s truncated)" -eq 10 ]
207 truncate -c -s 0 truncated
208 [ "$(stat -c %s truncated)" -eq 0 ]
209 rm -f truncated
210
211 # Disabled because of RHBZ#660687 on Debian.
212 # stage Checking utimens and timestamps
213 # for ts in 12345 1234567 987654321; do
214 #     # NB: It's not possible to set the ctime with touch.
215 #     touch -a -d @$ts timestamp
216 #     [ "$(stat -c %X timestamp)" -eq $ts ]
217 #     touch -m -d @$ts timestamp
218 #     [ "$(stat -c %Y timestamp)" -eq $ts ]
219 #     touch    -d @$ts timestamp
220 #     [ "$(stat -c %X timestamp)" -eq $ts ]
221 #     [ "$(stat -c %Y timestamp)" -eq $ts ]
222 # done
223
224 stage Checking writes
225 cp hello.txt copy.txt
226 echo >> copy.txt
227 echo world >> copy.txt
228 echo bigger >> copy.txt
229 echo biggest >> copy.txt
230 [ "$(cat copy.txt)" = "hello
231 world
232 bigger
233 biggest" ]
234
235 stage 'Checking extended attribute (xattr) read operation'
236 if getfattr --help > /dev/null 2>&1 ; then
237   [ "$(getfattr -d user_xattr | grep -v ^#)" = 'user.test="hello123"' ]
238 fi
239
240 stage Checking POSIX ACL read operation
241 if getfacl --help > /dev/null 2>&1 ; then
242   [ "$(getfacl -n acl | grep -v ^#)" = "user::rw-
243 user:500:r--
244 group::r--
245 mask::r--
246 other::r--" ]
247 fi
248
249 # These ones are not yet tested by the current script:
250 #stage XXX statfs/statvfs
251
252 # These ones cannot easily be tested by the current script, eg because
253 # this script doesn't run as root:
254 #stage XXX fsync
255 #stage XXX chown
256 #stage XXX mknod