#!/usr/bin/env python # Copyright 2013-2014 Lars Wirzenius # # This program 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 3 of the License, or # (at your option) any later version. # # This program 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 this program. If not, see . # # =*= License: GPL-3+ =*= '''Create all filesystem object types. This program creates a directory tree (the "farm") with all filesystem object types that are interesting from the point of view of testing backups. Use the --verbose option to see what is created. ''' import cliapp import os import platform import stat class MakeFunnyFarm(cliapp.Application): def add_settings(self): self.settings.boolean( ['verbose'], 'show what is created') def process_args(self, args): if len(args) != 1: raise cliapp.AppException( "Must have exactly one argument: the name of a directory " "that is to be created and populated") self.dirname = args[0] os.mkdir(self.dirname) # A bit of magic here: all the methods that create filesystem # objects have names starting with farm_ and here we find the # the names and call them. This avoids having to list all the # methods manually, or inlining all their code. A bit of magic # saves toil and boilerplate. method_names = [x for x in dir(self) if x.startswith('farm_')] for method_name in sorted(method_names): if self.settings['verbose']: print method_name[len('farm_'):] getattr(self, method_name)() def J(self, filename): '''Return filename within the farm directory.''' return os.path.join(self.dirname, filename) def farm_regular_file_empty(self): with open(self.J('file-empty'), 'w'): pass def farm_regular_file_1_byte(self): with open(self.J('file-one-byte'), 'wb') as f: f.write('x') def farm_regular_file_4096_byte(self): with open(self.J('file-four-kibibytes'), 'wb') as f: f.write('x' * 4096) def farm_hardlink_within_farm(self): with open(self.J('hardlink-1'), 'wb') as f: f.write('x' * 4096) os.link(self.J('hardlink-1'), self.J('hardlink-2')) def farm_directory_empty(self): os.mkdir(self.J('dir-empty')) def farm_symlink_within_farm_target_exists(self): with open(self.J('symlink-target'), 'w'): pass os.symlink('symlink-target', self.J('symlink-local-exists')) def farm_symlink_within_farm_target_doesnt_exist(self): os.symlink( self.J('does-not-exist'), self.J('symlink-local-doesnt_exist')) def farm_symlink_outside_farm_target_exists(self): os.symlink('/', self.J('symlink-remote-exists')) def farm_symlink_outside_farm_target_doesnt_exist(self): os.symlink( self.J('/does-not-exist'), self.J('symlink-remote-doesnt_exist')) def farm_symlink_to_containing_dir(self): os.symlink('.', self.J('symlink-to-dot')) def farm_symlink_to_subdir_of_containing_dir(self): os.mkdir(self.J('symlink-target-dir')) os.symlink('.', self.J('symlink-to-subdir')) def farm_socket(self): if platform.system() != "FreeBSD": os.mknod(self.J('socket'), stat.S_IFSOCK | 0700) def farm_fifo(self): os.mknod(self.J('fifo'), stat.S_IFIFO | 0700) def farm_regular_file_setuid(self): with open(self.J('file-setuid'), 'w'): pass os.chmod(self.J('file-setuid'), stat.S_ISUID | 0777) def farm_regular_file_setgid(self): with open(self.J('file-setgid'), 'w'): pass os.chmod(self.J('file-setgid'), stat.S_ISGID | 0777) def farm_regular_file_sticky_bit(self): with open(self.J('file-sticky-bit'), 'w'): pass os.chmod(self.J('file-sticky-bit'), stat.S_ISVTX | 0777) def farm_regular_file_xattr_user(self): with open(self.J('file-xattr-user'), 'w'): pass cliapp.runcmd( ['setfattr', '-n', 'user.foo', '-v', 'some value', self.J('file-xattr-user')]) def farm_regular_file_xattr_user_empty_value(self): # This is a special case: a key exists, but has an empty value. # Some tools treat such keys as non-existing, but others do not, # so Obnam needs to handle them. with open(self.J('file-xattr-user-empty-value'), 'w'): pass cliapp.runcmd( ['setfattr', '-n', 'user.foo', '-v', '""', self.J('file-xattr-user')]) MakeFunnyFarm(description=__doc__).run()