1 #!/usr/bin/env python 2 3 """ 4 Filesystem utilities. 5 6 Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 import errno 23 from imiptools.config import DEFAULT_PERMISSIONS, DEFAULT_DIR_PERMISSIONS 24 from os.path import abspath, commonprefix, exists, join 25 from os import chmod, makedirs, mkdir, rmdir 26 from time import sleep, time 27 28 def check_dir(base, dir): 29 return commonprefix([base, abspath(dir)]) == base 30 31 def fix_permissions(filename, is_dir=False): 32 try: 33 chmod(filename, is_dir and DEFAULT_DIR_PERMISSIONS or DEFAULT_PERMISSIONS) 34 except OSError: 35 pass 36 37 def make_path(base, parts): 38 for part in parts: 39 pathname = join(base, part) 40 if not exists(pathname): 41 mkdir(pathname) 42 fix_permissions(pathname, True) 43 base = pathname 44 45 class FileBase: 46 47 "Basic filesystem operations." 48 49 lock_name = "__lock__" 50 51 def __init__(self, store_dir): 52 self.store_dir = store_dir 53 if not exists(self.store_dir): 54 makedirs(self.store_dir) 55 fix_permissions(self.store_dir, True) 56 57 def get_file_object(self, base, *parts): 58 pathname = join(base, *parts) 59 return check_dir(base, pathname) and pathname or None 60 61 def get_object_in_store(self, *parts): 62 63 """ 64 Return the name of any valid object stored within a hierarchy specified 65 by the given 'parts'. 66 """ 67 68 parent = expected = self.store_dir 69 70 for part in parts: 71 filename = self.get_file_object(expected, part) 72 if not filename: 73 return False 74 parent = expected 75 expected = filename 76 77 if not exists(parent): 78 make_path(self.store_dir, parts[:-1]) 79 80 return filename 81 82 # Locking methods. 83 # This uses the directory creation method exploited by MoinMoin.util.lock. 84 # However, a simple single lock type mechanism is employed here. 85 86 def get_lock_dir(self, *parts): 87 parts = parts and list(parts) or [] 88 parts.append(self.lock_name) 89 return self.get_object_in_store(*parts) 90 91 def acquire_lock(self, timeout=None, *parts): 92 93 """ 94 Acquire an exclusive lock on the directory or a path within it described 95 by 'parts'. 96 """ 97 98 start = now = time() 99 100 while not timeout or now - start < timeout: 101 try: 102 mkdir(self.get_lock_dir(*parts)) 103 break 104 except OSError, exc: 105 if exc.errno != errno.EEXIST: 106 raise 107 sleep(1) 108 now = time() 109 110 def release_lock(self, *parts): 111 112 """ 113 Release an acquired lock on the directory or a path within it described 114 by 'parts'. 115 """ 116 117 try: 118 rmdir(self.get_lock_dir(*parts)) 119 except OSError, exc: 120 if exc.errno != errno.ENOENT: 121 raise 122 123 # vim: tabstop=4 expandtab shiftwidth=4