summaryrefslogtreecommitdiff
path: root/mkfunnyfarm
blob: 99c111e2e1f2f237d0643319770342f8350c3faf (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
#!/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 <http://www.gnu.org/licenses/>.
#
# =*= 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()