diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rwxr-xr-x | tests/common.rb | 154 | ||||
-rwxr-xr-x | tests/test_bindfs.rb | 206 | ||||
-rwxr-xr-x | tests/test_concurrent.rb | 45 |
4 files changed, 407 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..79b8dee --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,2 @@ + +TESTS = test_bindfs.rb diff --git a/tests/common.rb b/tests/common.rb new file mode 100755 index 0000000..f15110c --- /dev/null +++ b/tests/common.rb @@ -0,0 +1,154 @@ +#!/usr/bin/env ruby +# +# Copyright 2006,2007,2008,2009,2010 Martin Pärtel <martin.partel@gmail.com> +# +# This file is part of bindfs. +# +# bindfs is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# bindfs is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with bindfs. If not, see <http://www.gnu.org/licenses/>. +# + +require 'fileutils.rb' +include FileUtils + +# Set the default umask for all tests +File.umask 0022 + +EXECUTABLE_PATH = '../src/bindfs' +TESTDIR_NAME = 'tmp_test_bindfs' + +# If set to an array of test names, only those will be run +$only_these_tests = nil + +# Prepares a test environment with a mounted directory +def testenv(bindfs_args, &block) + + testcase_title = bindfs_args + + return unless $only_these_tests == nil or $only_these_tests.member? testcase_title + + puts "--- #{testcase_title} ---" + puts "[ #{bindfs_args} ]" + + begin + Dir.mkdir TESTDIR_NAME + rescue Exception => ex + $stderr.puts "ERROR creating testdir at #{TESTDIR_NAME}" + $stderr.puts ex + exit! 1 + end + + begin + Dir.chdir TESTDIR_NAME + Dir.mkdir 'src' + Dir.mkdir 'mnt' + rescue Exception => ex + $stderr.puts "ERROR preparing testdir at #{TESTDIR_NAME}" + $stderr.puts ex + exit! 1 + end + + bindfs_pid = nil + begin + cmd = "../#{EXECUTABLE_PATH} #{bindfs_args} src mnt" + bindfs_pid = Process.fork do + exec cmd + exit! 127 + end + rescue Exception => ex + $stderr.puts "ERROR running bindfs" + $stderr.puts ex + Dir.chdir '..' + system("rm -Rf #{TESTDIR_NAME}") + exit! 1 + end + + # Wait for bindfs to daemonize itself + Process.wait bindfs_pid + + # TODO: check that mounting was successful + + testcase_ok = true + begin + yield + rescue Exception => ex + $stderr.puts "ERROR: testcase `#{testcase_title}' failed" + $stderr.puts ex + $stderr.puts ex.backtrace + testcase_ok = false + end + + begin + unless system(umount_cmd + ' mnt') + raise Exception.new(umount_cmd + " failed with status #{$?}") + end + rescue Exception => ex + $stderr.puts "ERROR: failed to umount" + $stderr.puts ex + $stderr.puts ex.backtrace + testcase_ok = false + end + + begin + Dir.chdir '..' + rescue Exception => ex + $stderr.puts "ERROR: failed to exit test env" + $stderr.puts ex + $stderr.puts ex.backtrace + exit! 1 + end + + unless system "rm -Rf #{TESTDIR_NAME}" + $stderr.puts "ERROR: failed to clear test directory" + exit! 1 + end + + if testcase_ok + puts "OK" + else + exit! 1 + end +end + +# Like testenv but skips the test if not running as root +def root_testenv(bindfs_args, &block) + if Process.uid == 0 + testenv(bindfs_args, &block) + else + puts "--- #{bindfs_args} ---" + puts "[ #{bindfs_args} ]" + puts "SKIP (requires root)" + end +end + +def umount_cmd + if `which fusermount`.strip.empty? + then 'umount' + else 'fusermount -uz' + end +end + +def assert + raise Exception.new('test failed') unless yield +end + +def assert_exception(ex) + begin + yield + rescue ex + return + end + raise Exception.new('expected exception ' + ex.to_s) +end + + diff --git a/tests/test_bindfs.rb b/tests/test_bindfs.rb new file mode 100755 index 0000000..8d5b18f --- /dev/null +++ b/tests/test_bindfs.rb @@ -0,0 +1,206 @@ +#!/usr/bin/env ruby +# +# Copyright 2006,2007,2008,2009,2010 Martin Pärtel <martin.partel@gmail.com> +# +# This file is part of bindfs. +# +# bindfs is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# bindfs is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with bindfs. If not, see <http://www.gnu.org/licenses/>. +# + +require 'common.rb' + +include Errno + +# FileUtils.chown turned out to be quite buggy in Ruby 1.8.7, +# so we'll use File.chown instead. +def chown(user, group, list) + user = Etc.getpwnam(user).uid if user.is_a? String + group = Etc.getgrnam(group).gid if group.is_a? String + + list = [list] unless list.is_a? Array + for file in list + File.chown(user, group, file) + end +end + +# Treat parameters as test names and run only those +$only_these_tests = ARGV unless ARGV.empty? + +# Some useful shorthands +$nobody_uid = nobody_uid = Etc.getpwnam('nobody').uid +$nogroup_gid = nogroup_gid = Etc.getgrnam('nogroup').gid + + +testenv("") do + assert { File.basename(pwd) == TESTDIR_NAME } +end + +testenv("-u nobody -g nogroup") do + touch('src/file') + + assert { File.stat('mnt/file').uid == nobody_uid } + assert { File.stat('mnt/file').gid == nogroup_gid } +end + +testenv("-p 0600:u+D") do + touch('src/file') + chmod(0777, 'src/file') + + assert { File.stat('mnt/file').mode & 0777 == 0600 } +end + +testenv("--chmod-deny") do + touch('src/file') + + assert_exception(EPERM) { chmod(0777, 'mnt/file') } +end + +testenv("-u nobody -m #{Process.uid} -p 0600,u+D") do + touch('src/file') + + assert { File.stat('mnt/file').uid == Process.uid } +end + +testenv("--create-with-perms=og=r:ogd+x") do + touch('src/file') + mkdir('src/dir') + + assert { File.stat('mnt/file').mode & 0077 == 0044 } + assert { File.stat('mnt/dir').mode & 0077 == 0055 } +end + +testenv("--ctime-from-mtime") do + sf = 'src/file' + mf = 'mnt/file' + + touch(sf) + sleep(1.1) + chmod(0777, mf) + + assert { File.stat(mf).ctime == File.stat(mf).mtime } + assert { File.stat(sf).ctime > File.stat(sf).mtime } + +end + +# Define expectation for changing [uid, gid, both] +# for each combination of chown/chgrp flags. +chown_chgrp_test_cases = { + :chown_normal => { + :chgrp_normal => [:uid, :gid, :both], + :chgrp_ignore => [:uid, nil, :uid], + :chgrp_deny => [:uid, EPERM, EPERM] + }, + :chown_ignore => { + :chgrp_normal => [nil, :gid, :gid], + :chgrp_ignore => [nil, nil, nil], + :chgrp_deny => [nil, EPERM, EPERM] + }, + :chown_deny => { + :chgrp_normal => [EPERM, :gid, EPERM], + :chgrp_ignore => [EPERM, nil, EPERM], + :chgrp_deny => [EPERM, EPERM, EPERM] + } +} + +def run_chown_chgrp_test_case(chown_flag, chgrp_flag, expectations) + flags = [chown_flag, chgrp_flag].map do |flag| + '--' + flag.to_s.sub('_', '-') + end.join ' ' + + srcfile = 'src/file' + mntfile = 'mnt/file' + tests = [ + lambda { chown('nobody', nil, mntfile) }, + lambda { chown(nil, 'nogroup', mntfile) }, + lambda { chown('nobody', 'nogroup', mntfile) } + ] + + for testcase, expect in tests.zip expectations + root_testenv(flags) do + touch(srcfile) + if expect.respond_to? :exception + assert_exception(expect) { testcase.call } + else + testcase.call + uid = File.stat(srcfile).uid + gid = File.stat(srcfile).gid + + case expect + when :uid + assert { uid == $nobody_uid } + assert { gid != $nogroup_gid } + when :gid + assert { uid != $nobody_uid } + assert { gid == $nogroup_gid } + when :both + assert { uid == $nobody_uid } + assert { gid == $nogroup_gid } + when nil + assert { uid != $nobody_uid } + assert { gid != $nogroup_gid } + end + end + end + end +end + +chown_chgrp_test_cases.each do |chown_flag, more| + more.each do |chgrp_flag, expectations| + run_chown_chgrp_test_case(chown_flag, chgrp_flag, expectations) + end +end + +root_testenv("--chown-deny") do + touch('src/file') + + assert_exception(EPERM) { chown('nobody', nil, 'mnt/file') } + assert_exception(EPERM) { chown('nobody', 'nogroup', 'mnt/file') } + chown(nil, 'nogroup', 'mnt/file') +end + +testenv("--chmod-allow-x --chmod-ignore") do + touch('src/file') + + chmod(01700, 'src/file') # sticky bit set + + chmod(00077, 'mnt/file') # should change x bits; should not unset sticky bit + assert { File.stat('src/file').mode & 07777 == 01611 } + + + mkdir('src/dir') + chmod(0700, 'src/dir') + chmod(0077, 'mnt/dir') # bits on dir should not change + assert { File.stat('src/dir').mode & 0777 == 0700 } +end + +testenv("--chmod-deny --chmod-allow-x") do + touch('src/file') + + chmod(0700, 'src/file') + + chmod(0700, 'mnt/file') # no-op chmod should work + + assert_exception(EPERM) { chmod(0777, 'mnt/file') } + assert_exception(EPERM) { chmod(0000, 'mnt/file') } + assert_exception(EPERM) { chmod(01700, 'mnt/file') } # sticky bit + + chmod(0611, 'mnt/file') # chmod that only changes x-bits should work + assert { File.stat('src/file').mode & 07777 == 00611 } + + + mkdir('src/dir') + chmod(0700, 'src/dir') + assert_exception(EPERM) { chmod(0700, 'mnt/dir') } # chmod on dir should not work +end + diff --git a/tests/test_concurrent.rb b/tests/test_concurrent.rb new file mode 100755 index 0000000..f07c031 --- /dev/null +++ b/tests/test_concurrent.rb @@ -0,0 +1,45 @@ +#!/usr/bin/env ruby +# +# Copyright 2006,2007,2008,2009,2010 Martin Pärtel <martin.partel@gmail.com> +# +# This file is part of bindfs. +# +# bindfs is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# bindfs is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with bindfs. If not, see <http://www.gnu.org/licenses/>. +# +require 'common.rb' + +raise "Please run this as root" unless Process.uid == 0 + +raise "Give two users as parameters" unless ARGV.length == 2 + +user1 = ARGV[0] +user2 = ARGV[1] + +raise "Give two _different_ users as parameters" unless user1 != user2 + +testenv "--mirror=#{user1},#{user2}" do + touch('src/file') + + count = 0 + 10.times do |i| + out1 = `su #{user1} -c "stat --format=%U mnt/file"` + out2 = `su #{user2} -c "stat --format=%U mnt/file"` + + out1.strip! + out2.strip! + puts "#{i+1}: #{out1} #{out2}" + raise "FAIL: #{user1} saw #{out1} on iter #{i}" unless out1 == user1 + raise "FAIL: #{user2} saw #{out2} on iter #{i}" unless out2 == user2 + end +end |