1# Copyright (c) 2009, Tomohiro Kusumi 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are met: 6# 7# 1. Redistributions of source code must retain the above copyright notice, this 8# list of conditions and the following disclaimer. 9# 2. Redistributions in binary form must reproduce the above copyright notice, 10# this list of conditions and the following disclaimer in the documentation 11# and/or other materials provided with the distribution. 12# 13# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 24import os 25import shutil 26 27from . import filebytes 28from . import fileobj 29from . import kernel 30from . import log 31from . import rrmap 32from . import util 33 34class Fileobj (rrmap.Fileobj): 35 _insert = True 36 _replace = True 37 _delete = True 38 _enabled = kernel.has_mremap() 39 _partial = False 40 41 def __init__(self, f, offset=0, length=0): 42 self.__dead = False 43 self.__sync = 0 44 self.__anon = None 45 super(Fileobj, self).__init__(f, offset, length) 46 47 def ctr(self): 48 if self.is_mappable(): 49 super(Fileobj, self).ctr() 50 else: 51 self.__init_anon() 52 f = self.__get_backing_path() 53 self.init_mapping(f) 54 self.update_fstat(f) 55 56 def dtr(self): 57 if not self.map: 58 return 59 t = self.get_fstat() 60 self.restore_rollback_log(self) 61 if not self.__anon: 62 self.flush() 63 shcopy = False 64 else: 65 shcopy = self.__flush_anon() 66 self.cleanup_mapping() 67 68 if not self.__anon: 69 self.update_mtime(self.get_path(), t) 70 if self.__dead and not self.is_dirty(): 71 f = self.__get_backing_path() 72 kernel.truncate(f) 73 self.update_mtime(f, t) 74 if self.__anon and shcopy: 75 self.__copy_anon() 76 self.__anon = None 77 78 def mmap(self, fileno): 79 return kernel.mmap_full(fileno) 80 81 def __init_anon(self): 82 assert not self.__anon 83 self.__anon = util.open_temp_file() 84 self.__anon.write(filebytes.ZERO) 85 self.__anon.seek(0) 86 self.__die() 87 log.debug("Create backing file {0} for {1}".format( 88 self.__get_backing_path(), self.get_path())) 89 90 def __copy_anon(self): 91 kernel.fsync(self.__anon) 92 src = self.__get_backing_path() 93 dst = self.get_path() 94 if src == dst: 95 return -1 96 if not os.path.isfile(src): 97 return -1 98 if kernel.read_reg_size(dst) > 0: 99 return -1 100 shutil.copy2(src, dst) 101 102 def __flush_anon(self): 103 self.sync() 104 self.clear_dirty() 105 assert self.__sync >= 1 106 return self.__sync >= 2 # need copying 107 108 def __get_backing_path(self): 109 if self.__anon: 110 return self.__anon.name 111 else: 112 return self.get_path() 113 114 def get_size(self): 115 if not self.__dead: 116 return super(Fileobj, self).get_size() 117 else: 118 return 0 119 120 def sync(self): 121 self.map.flush() 122 self.update_fstat(self.__get_backing_path()) 123 self.__sync += 1 124 125 def utime(self): 126 kernel.touch(self.__get_backing_path()) 127 self.update_fstat(self.__get_backing_path()) 128 self.__sync += 1 129 130 def __die(self, is_dying=True): 131 if not self.__dead and is_dying: 132 kernel.touch(self.__get_backing_path()) 133 self.__dead = is_dying 134 135 def insert(self, x, l, rec=True): 136 size = self.get_size() 137 n = len(l) 138 xx = x + n 139 140 if rec: 141 buf = l[:] 142 def ufn(ref): 143 ref.delete(x, n, False) 144 return x 145 def rfn(ref): 146 ref.insert(x, buf, False) 147 return x 148 self.add_undo(ufn, rfn) 149 150 self.map.resize(size + n) 151 self.map.move(xx, x, size - x) 152 self.map[x:xx] = filebytes.input_to_bytes(l) 153 self.set_dirty() 154 self.__die(False) 155 156 def replace(self, x, l, rec=True): 157 if self.is_empty(): 158 self.insert(x, l, rec) 159 return 160 size = self.get_size() 161 n = len(l) 162 xx = x + n 163 resized = False 164 if x + n > size: 165 self.map.resize(x + n) 166 resized = True 167 168 if rec: 169 ubuf = filebytes.ords(self.read(x, n)) 170 rbuf = l[:] 171 if not resized: 172 def ufn1(ref): 173 ref.replace(x, ubuf, False) 174 return x 175 def rfn1(ref): 176 ref.replace(x, rbuf, False) 177 return x 178 self.add_undo(ufn1, rfn1) 179 else: 180 def ufn2(ref): 181 ref.map.resize(size) # shrink 182 ref.replace(x, ubuf[:size - x], False) 183 return x 184 def rfn2(ref): 185 ref.map.resize(x + n) # expand 186 ref.replace(x, rbuf, False) 187 return x 188 self.add_undo(ufn2, rfn2) 189 190 self.map[x:xx] = filebytes.input_to_bytes(l) 191 self.set_dirty() 192 193 def delete(self, x, n, rec=True): 194 if self.is_empty(): 195 raise fileobj.Error("Empty buffer") 196 size = self.get_size() 197 xx = x + n 198 199 if rec: 200 buf = filebytes.ords(self.read(x, n)) 201 def ufn(ref): 202 ref.insert(x, buf, False) 203 return x 204 def rfn(ref): 205 ref.delete(x, n, False) 206 return x 207 self.add_undo(ufn, rfn) 208 209 self.map.move(x, xx, size - xx) 210 if size > n: 211 self.map.resize(size - n) 212 else: 213 self.__die() 214 self.set_dirty() 215