root/galaxy-central/eggs/PasteScript-1.7.3-py2.6.egg/paste/script/checkperms.py @ 3

リビジョン 3, 13.0 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
2# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
3"""
4This is a module to check the filesystem for the presence and
5permissions of certain files.  It can also be used to correct the
6permissions (but not existance) of those files.
7
8Currently only supports Posix systems (with Posixy permissions).
9Permission stuff can probably be stubbed out later.
10"""
11import os
12
13def read_perm_spec(spec):
14    """
15    Reads a spec like 'rw-r--r--' into a octal number suitable for
16    chmod.  That is characters in groups of three -- first group is
17    user, second for group, third for other (all other people).  The
18    characters are r (read), w (write), and x (executable), though the
19    executable can also be s (sticky).  Files in sticky directories
20    get the directories permission setting.
21
22    Examples::
23
24      >>> print oct(read_perm_spec('rw-r--r--'))
25      0644
26      >>> print oct(read_perm_spec('rw-rwsr--'))
27      02664
28      >>> print oct(read_perm_spec('r-xr--r--'))
29      0544
30      >>> print oct(read_perm_spec('r--------'))
31      0400
32    """
33    total_mask = 0
34    # suid/sgid modes give this mask in user, group, other mode:
35    set_bits = (04000, 02000, 0)
36    pieces = (spec[0:3], spec[3:6], spec[6:9])
37    for i, (mode, set_bit) in enumerate(zip(pieces, set_bits)):
38        mask = 0
39        read, write, exe = list(mode)
40        if read == 'r':
41            mask = mask | 4
42        elif read != '-':
43            raise ValueError, (
44                "Character %r unexpected (should be '-' or 'r')"
45                % read)
46        if write == 'w':
47            mask = mask | 2
48        elif write != '-':
49            raise ValueError, (
50                "Character %r unexpected (should be '-' or 'w')"
51                % write)
52        if exe == 'x':
53            mask = mask | 1
54        elif exe not in ('s', '-'):
55            raise ValueError, (
56                "Character %r unexpected (should be '-', 'x', or 's')"
57                % exe)
58        if exe == 's' and i == 2:
59            raise ValueError, (
60                "The 'other' executable setting cannot be suid/sgid ('s')")
61        mask = mask << ((2-i)*3)
62        if exe == 's':
63            mask = mask | set_bit
64        total_mask = total_mask | mask
65    return total_mask
66
67modes = [
68    (04000, 'setuid bit',
69     'setuid bit: make contents owned by directory owner'),
70    (02000, 'setgid bit',
71     'setgid bit: make contents inherit permissions from directory'),
72    (01000, 'sticky bit',
73     'sticky bit: append-only directory'),
74    (00400, 'read by owner', 'read by owner'),
75    (00200, 'write by owner', 'write by owner'),
76    (00100, 'execute by owner', 'owner can search directory'),
77    (00040, 'allow read by group members',
78     'allow read by group members',),
79    (00020, 'allow write by group members',
80     'allow write by group members'),
81    (00010, 'execute by group members',
82     'group members can search directory'),
83    (00004, 'read by others', 'read by others'),
84    (00002, 'write by others', 'write by others'),
85    (00001, 'execution by others', 'others can search directory'),
86    ]
87
88exe_bits = [0100, 0010, 0001]
89exe_mask = 0111
90full_mask = 07777
91
92def mode_diff(filename, mode, **kw):
93    """
94    Returns the differences calculated using ``calc_mode_diff``
95    """
96    cur_mode = os.stat(filename).st_mode
97    return calc_mode_diff(cur_mode, mode, **kw)
98
99def calc_mode_diff(cur_mode, mode, keep_exe=True,
100                   not_set='not set: ',
101                   set='set: '):
102    """
103    Gives the difference between the actual mode of the file and the
104    given mode.  If ``keep_exe`` is true, then if the mode doesn't
105    include any executable information the executable information will
106    simply be ignored.  High bits are also always ignored (except
107    suid/sgid and sticky bit).
108
109    Returns a list of differences (empty list if no differences)
110    """
111    for exe_bit in exe_bits:
112        if mode & exe_bit:
113            keep_exe = False
114    diffs = []
115    isdir = os.path.isdir(filename)
116    for bit, file_desc, dir_desc in modes:
117        if keep_exe and bit in exe_bits:
118            continue
119        if isdir:
120            desc = dir_desc
121        else:
122            desc = file_desc
123        if (mode & bit) and not (cur_mode & bit):
124            diffs.append(not_set + desc)
125        if not (mode & bit) and (cur_mode & bit):
126            diffs.append(set + desc)
127    return diffs
128
129def calc_set_mode(cur_mode, mode, keep_exe=True):
130    """
131    Calculates the new mode given the current node ``cur_mode`` and
132    the mode spec ``mode`` and if ``keep_exe`` is true then also keep
133    the executable bits in ``cur_mode`` if ``mode`` has no executable
134    bits in it.  Return the new mode.
135
136    Examples::
137
138      >>> print oct(calc_set_mode(0775, 0644))
139      0755
140      >>> print oct(calc_set_mode(0775, 0744))
141      0744
142      >>> print oct(calc_set_mode(010600, 0644))
143      010644
144      >>> print oct(calc_set_mode(0775, 0644, False))
145      0644
146    """
147    for exe_bit in exe_bits:
148        if mode & exe_bit:
149            keep_exe = False
150    # This zeros-out full_mask parts of the current mode:
151    keep_parts = (cur_mode | full_mask) ^ full_mask
152    if keep_exe:
153        keep_parts = keep_parts | (cur_mode & exe_mask)
154    new_mode = keep_parts | mode
155    return new_mode
156
157def set_mode(filename, mode, **kw):
158    """
159    Sets the mode on ``filename`` using ``calc_set_mode``
160    """
161    cur_mode = os.stat(filename).st_mode
162    new_mode = calc_set_mode(cur_mode, mode, **kw)
163    os.chmod(filename, new_mode)
164
165def calc_ownership_spec(spec):
166    """
167    Calculates what a string spec means, returning (uid, username,
168    gid, groupname), where there can be None values meaning no
169    preference.
170
171    The spec is a string like ``owner:group``.  It may use numbers
172    instead of user/group names.  It may leave out ``:group``.  It may
173    use '-' to mean any-user/any-group.
174
175    """
176    import grp
177    import pwd
178    user = group = None
179    uid = gid = None
180    if ':' in spec:
181        user_spec, group_spec = spec.split(':', 1)
182    else:
183        user_spec, group_spec = spec, '-'
184    if user_spec == '-':
185        user_spec = '0'
186    if group_spec == '-':
187        group_spec = '0'
188    try:
189        uid = int(user_spec)
190    except ValueError:
191        uid = pwd.getpwnam(user_spec)
192        user = user_spec
193    else:
194        if not uid:
195            uid = user = None
196        else:
197            user = pwd.getpwuid(uid).pw_name
198    try:
199        gid = int(group_spec)
200    except ValueError:
201        gid = grp.getgrnam(group_spec)
202        group = group_spec
203    else:
204        if not gid:
205            gid = group = None
206        else:
207            group = grp.getgrgid(gid).gr_name
208    return (uid, user, gid, group)
209
210def ownership_diff(filename, spec):
211    """
212    Return a list of differences between the ownership of ``filename``
213    and the spec given.
214    """
215    import grp
216    import pwd
217    diffs = []
218    uid, user, gid, group = calc_ownership_spec(spec)
219    st = os.stat(filename)
220    if uid and uid != st.st_uid:
221        diffs.append('owned by %s (should be %s)' %
222                     (pwd.getpwuid(st.st_uid).pw_name, user))
223    if gid and gid != st.st_gid:
224        diffs.append('group %s (should be %s)' %
225                     (grp.getgrgid(st.st_gid).gr_name, group))
226    return diffs
227
228def set_ownership(filename, spec):
229    """
230    Set the ownership of ``filename`` given the spec.
231    """
232    uid, user, gid, group = calc_ownership_spec(spec)
233    st = os.stat(filename)
234    if not uid:
235        uid = st.st_uid
236    if not gid:
237        gid = st.st_gid
238    os.chmod(filename, uid, gid)
239
240class PermissionSpec(object):
241    """
242    Represents a set of specifications for permissions.
243
244    Typically reads from a file that looks like this::
245
246      rwxrwxrwx user:group filename
247
248    If the filename ends in /, then it expected to be a directory, and
249    the directory is made executable automatically, and the contents
250    of the directory are given the same permission (recursively).  By
251    default the executable bit on files is left as-is, unless the
252    permissions specifically say it should be on in some way.
253
254    You can use 'nomodify filename' for permissions to say that any
255    permission is okay, and permissions should not be changed.
256
257    Use 'noexist filename' to say that a specific file should not
258    exist.
259
260    Use 'symlink filename symlinked_to' to assert a symlink destination
261
262    The entire file is read, and most specific rules are used for each
263    file (i.e., a rule for a subdirectory overrides the rule for a
264    superdirectory).  Order does not matter.
265    """
266
267    def __init__(self):
268        self.paths = {}
269
270    def parsefile(self, filename):
271        f = open(filename)
272        lines = f.readlines()
273        f.close()
274        self.parselines(lines, filename=filename)
275
276    commands = {}
277
278    def parselines(self, lines, filename=None):
279        for lineindex, line in enumerate(lines):
280            line = line.strip()
281            if not line or line.startswith('#'):
282                continue
283            parts = line.split()
284            command = parts[0]
285            if command in self.commands:
286                cmd = self.commands[command](*parts[1:])
287            else:
288                cmd = self.commands['*'](*parts)
289            self.paths[cmd.path] = cmd
290
291    def check(self):
292        action = _Check(self)
293        self.traverse(action)
294
295    def fix(self):
296        action = _Fixer(self)
297        self.traverse(action)
298
299    def traverse(self, action):
300        paths = self.paths_sorted()
301        checked = {}
302        for path, checker in list(paths)[::-1]:
303            self.check_tree(action, path, paths, checked)
304        for path, checker in paths:
305            if path not in checked:
306                action.noexists(path, checker)
307
308    def traverse_tree(self, action, path, paths, checked):
309        if path in checked:
310            return
311        self.traverse_path(action, path, paths, checked)
312        if os.path.isdir(path):
313            for fn in os.listdir(path):
314                fn = os.path.join(path, fn)
315                self.traverse_tree(action, fn, paths, checked)
316
317    def traverse_path(self, action, path, paths, checked):
318        checked[path] = None
319        for check_path, checker in paths:
320            if path.startswith(check_path):
321                action.check(check_path, checker)
322                if not checker.inherit:
323                    break
324
325    def paths_sorted(self):
326        paths = self.paths.items()
327        paths.sort(lambda a, b: -cmp(len(a[0]), len(b[0])))
328                 
329class _Rule(object):
330    class __metaclass__(type):
331        def __new__(meta, class_name, bases, d):
332            cls = type.__new__(meta, class_name, bases, d)
333            PermissionSpec.commands[cls.__name__] = cls
334            return cls
335
336    inherit = False
337    def noexists(self):
338        return ['Path %s does not exist' % path]
339
340class _NoModify(_Rule):
341
342    name = 'nomodify'
343               
344    def __init__(self, path):
345        self.path = path
346
347    def fix(self, path):
348        pass
349
350class _NoExist(_Rule):
351
352    name = 'noexist'
353
354    def __init__(self, path):
355        self.path = path
356
357    def check(self, path):
358        return ['Path %s should not exist' % path]
359
360    def noexists(self, path):
361        return []
362
363    def fix(self, path):
364        # @@: Should delete?
365        pass
366
367class _SymLink(_Rule):
368
369    name = 'symlink'
370    inherit = True
371
372    def __init__(self, path, dest):
373        self.path = path
374        self.dest = dest
375
376    def check(self, path):
377        assert path == self.path, (
378            "_Symlink should only be passed specific path %s (not %s)"
379            % (self.path, path))
380        try:
381            link = os.path.readlink(path)
382        except OSError:
383            if e.errno != 22:
384                raise
385            return ['Path %s is not a symlink (should point to %s)'
386                    % (path, self.dest)]
387        if link != self.dest:
388            return ['Path %s should symlink to %s, not %s'
389                    % (path, self.dest, link)]
390        return []
391
392    def fix(self, path):
393        assert path == self.path, (
394            "_Symlink should only be passed specific path %s (not %s)"
395            % (self.path, path))
396        if not os.path.exists(path):
397            os.symlink(path, self.dest)
398        else:
399            # @@: This should correct the symlink or something:
400            print 'Not symlinking %s' % path
401
402class _Permission(_Rule):
403
404    name = '*'
405
406    def __init__(self, perm, owner, dir):
407        self.perm_spec = read_perm_spec(perm)
408        self.owner = owner
409        self.dir = dir
410
411    def check(self, path):
412        return mode_diff(path, self.perm_spec)
413
414    def fix(self, path):
415        set_mode(path, self.perm_spec)
416
417class _Strategy(object):
418
419    def __init__(self, spec):
420        self.spec = spec
421
422class _Check(_Strategy):
423   
424    def noexists(self, path, checker):
425        checker.noexists(path)
426
427    def check(self, path, checker):
428        checker.check(path)
429
430class _Fixer(_Strategy):
431
432    def noexists(self, path, checker):
433        pass
434
435    def check(self, path, checker):
436        checker.fix(path)
437
438if __name__ == '__main__':
439    import doctest
440    doctest.testmod()
441   
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。