Show More
@@ -6,7 +6,7 | |||
|
6 | 6 | # of the GNU General Public License, incorporated herein by reference. |
|
7 | 7 | |
|
8 | 8 | from demandload import * |
|
9 | demandload(globals(), "cStringIO changelog errno manifest os tempfile") | |
|
9 | demandload(globals(), "cStringIO changelog errno manifest os tempfile util") | |
|
10 | 10 | |
|
11 | 11 | # writes to metadata files are ordered. reads: changelog, manifest, |
|
12 | 12 | # normal files. writes: normal files, manifest, changelog. |
@@ -36,19 +36,21 class appendfile(object): | |||
|
36 | 36 | def __init__(self, fp, tmpname): |
|
37 | 37 | if tmpname: |
|
38 | 38 | self.tmpname = tmpname |
|
39 |
self.tmpfp = |
|
|
39 | self.tmpfp = util.posixfile(self.tmpname, 'ab+') | |
|
40 | 40 | else: |
|
41 | 41 | fd, self.tmpname = tempfile.mkstemp() |
|
42 |
|
|
|
42 | os.close(fd) | |
|
43 | self.tmpfp = util.posixfile(self.tmpname, 'ab+') | |
|
43 | 44 | self.realfp = fp |
|
44 | 45 | self.offset = fp.tell() |
|
45 | 46 | # real file is not written by anyone else. cache its size so |
|
46 | 47 | # seek and read can be fast. |
|
47 |
self.realsize = |
|
|
48 | self.realsize = util.fstat(fp).st_size | |
|
49 | self.name = fp.name | |
|
48 | 50 | |
|
49 | 51 | def end(self): |
|
50 | 52 | self.tmpfp.flush() # make sure the stat is correct |
|
51 |
return self.realsize + |
|
|
53 | return self.realsize + util.fstat(self.tmpfp).st_size | |
|
52 | 54 | |
|
53 | 55 | def tell(self): |
|
54 | 56 | return self.offset |
@@ -160,7 +160,7 class bundlerepository(localrepo.localre | |||
|
160 | 160 | def __init__(self, ui, path, bundlename): |
|
161 | 161 | localrepo.localrepository.__init__(self, ui, path) |
|
162 | 162 | f = open(bundlename, "rb") |
|
163 |
s = |
|
|
163 | s = util.fstat(f) | |
|
164 | 164 | self.bundlefile = f |
|
165 | 165 | header = self.bundlefile.read(6) |
|
166 | 166 | if not header.startswith("HG"): |
@@ -14,7 +14,7 from node import * | |||
|
14 | 14 | from i18n import gettext as _ |
|
15 | 15 | from demandload import demandload |
|
16 | 16 | demandload(globals(), "binascii changegroup errno heapq mdiff os") |
|
17 | demandload(globals(), "sha struct zlib") | |
|
17 | demandload(globals(), "sha struct util zlib") | |
|
18 | 18 | |
|
19 | 19 | # revlog version strings |
|
20 | 20 | REVLOGV0 = 0 |
@@ -322,7 +322,7 class revlog(object): | |||
|
322 | 322 | i = "" |
|
323 | 323 | else: |
|
324 | 324 | try: |
|
325 |
st = |
|
|
325 | st = util.fstat(f) | |
|
326 | 326 | except AttributeError, inst: |
|
327 | 327 | st = None |
|
328 | 328 | else: |
@@ -57,7 +57,7 class sshrepository(remoterepository): | |||
|
57 | 57 | |
|
58 | 58 | def readerr(self): |
|
59 | 59 | while 1: |
|
60 |
size = |
|
|
60 | size = util.fstat(self.pipee).st_size | |
|
61 | 61 | if size == 0: break |
|
62 | 62 | l = self.pipee.readline() |
|
63 | 63 | if not l: break |
@@ -406,8 +406,18 def rename(src, dst): | |||
|
406 | 406 | """forcibly rename a file""" |
|
407 | 407 | try: |
|
408 | 408 | os.rename(src, dst) |
|
409 | except: | |
|
410 | os.unlink(dst) | |
|
409 | except OSError, err: | |
|
410 | # on windows, rename to existing file is not allowed, so we | |
|
411 | # must delete destination first. but if file is open, unlink | |
|
412 | # schedules it for delete but does not delete it. rename | |
|
413 | # happens immediately even for open files, so we create | |
|
414 | # temporary file, delete it, rename destination to that name, | |
|
415 | # then delete that. then rename is safe to do. | |
|
416 | fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.') | |
|
417 | os.close(fd) | |
|
418 | os.unlink(temp) | |
|
419 | os.rename(dst, temp) | |
|
420 | os.unlink(temp) | |
|
411 | 421 | os.rename(src, dst) |
|
412 | 422 | |
|
413 | 423 | def unlink(f): |
@@ -449,90 +459,13 def audit_path(path): | |||
|
449 | 459 | or os.pardir in parts): |
|
450 | 460 | raise Abort(_("path contains illegal component: %s\n") % path) |
|
451 | 461 | |
|
452 | def opener(base, audit=True): | |
|
453 | """ | |
|
454 | return a function that opens files relative to base | |
|
455 | ||
|
456 | this function is used to hide the details of COW semantics and | |
|
457 | remote file access from higher level code. | |
|
458 | """ | |
|
459 | p = base | |
|
460 | audit_p = audit | |
|
461 | ||
|
462 | def mktempcopy(name): | |
|
463 | d, fn = os.path.split(name) | |
|
464 | fd, temp = tempfile.mkstemp(prefix=fn, dir=d) | |
|
465 | fp = os.fdopen(fd, "wb") | |
|
466 | try: | |
|
467 | fp.write(file(name, "rb").read()) | |
|
468 | except: | |
|
469 | try: os.unlink(temp) | |
|
470 | except: pass | |
|
471 | raise | |
|
472 | fp.close() | |
|
473 | st = os.lstat(name) | |
|
474 | os.chmod(temp, st.st_mode) | |
|
475 | return temp | |
|
476 | ||
|
477 | class atomictempfile(file): | |
|
478 | """the file will only be copied when rename is called""" | |
|
479 | def __init__(self, name, mode): | |
|
480 | self.__name = name | |
|
481 | self.temp = mktempcopy(name) | |
|
482 | file.__init__(self, self.temp, mode) | |
|
483 | def rename(self): | |
|
484 | if not self.closed: | |
|
485 | file.close(self) | |
|
486 | rename(self.temp, self.__name) | |
|
487 | def __del__(self): | |
|
488 | if not self.closed: | |
|
489 | try: | |
|
490 | os.unlink(self.temp) | |
|
491 | except: pass | |
|
492 | file.close(self) | |
|
493 | ||
|
494 | class atomicfile(atomictempfile): | |
|
495 | """the file will only be copied on close""" | |
|
496 | def __init__(self, name, mode): | |
|
497 | atomictempfile.__init__(self, name, mode) | |
|
498 | def close(self): | |
|
499 | self.rename() | |
|
500 | def __del__(self): | |
|
501 | self.rename() | |
|
502 | ||
|
503 | def o(path, mode="r", text=False, atomic=False, atomictemp=False): | |
|
504 | if audit_p: | |
|
505 | audit_path(path) | |
|
506 | f = os.path.join(p, path) | |
|
507 | ||
|
508 | if not text: | |
|
509 | mode += "b" # for that other OS | |
|
510 | ||
|
511 | if mode[0] != "r": | |
|
512 | try: | |
|
513 | nlink = nlinks(f) | |
|
514 | except OSError: | |
|
515 | d = os.path.dirname(f) | |
|
516 | if not os.path.isdir(d): | |
|
517 | os.makedirs(d) | |
|
518 | else: | |
|
519 | if atomic: | |
|
520 | return atomicfile(f, mode) | |
|
521 | elif atomictemp: | |
|
522 | return atomictempfile(f, mode) | |
|
523 | if nlink > 1: | |
|
524 | rename(mktempcopy(f), f) | |
|
525 | return file(f, mode) | |
|
526 | ||
|
527 | return o | |
|
528 | ||
|
529 | 462 | def _makelock_file(info, pathname): |
|
530 | 463 | ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL) |
|
531 | 464 | os.write(ld, info) |
|
532 | 465 | os.close(ld) |
|
533 | 466 | |
|
534 | 467 | def _readlock_file(pathname): |
|
535 | return file(pathname).read() | |
|
468 | return posixfile(pathname).read() | |
|
536 | 469 | |
|
537 | 470 | def nlinks(pathname): |
|
538 | 471 | """Return number of hardlinks for the given file.""" |
@@ -544,6 +477,15 else: | |||
|
544 | 477 | def os_link(src, dst): |
|
545 | 478 | raise OSError(0, _("Hardlinks not supported")) |
|
546 | 479 | |
|
480 | def fstat(fp): | |
|
481 | '''stat file object that may not have fileno method.''' | |
|
482 | try: | |
|
483 | return os.fstat(fp.fileno()) | |
|
484 | except AttributeError: | |
|
485 | return os.stat(fp.name) | |
|
486 | ||
|
487 | posixfile = file | |
|
488 | ||
|
547 | 489 | # Platform specific variants |
|
548 | 490 | if os.name == 'nt': |
|
549 | 491 | demandload(globals(), "msvcrt") |
@@ -722,6 +664,84 else: | |||
|
722 | 664 | return _("stopped by signal %d") % val, val |
|
723 | 665 | raise ValueError(_("invalid exit code")) |
|
724 | 666 | |
|
667 | def opener(base, audit=True): | |
|
668 | """ | |
|
669 | return a function that opens files relative to base | |
|
670 | ||
|
671 | this function is used to hide the details of COW semantics and | |
|
672 | remote file access from higher level code. | |
|
673 | """ | |
|
674 | p = base | |
|
675 | audit_p = audit | |
|
676 | ||
|
677 | def mktempcopy(name): | |
|
678 | d, fn = os.path.split(name) | |
|
679 | fd, temp = tempfile.mkstemp(prefix=fn, dir=d) | |
|
680 | os.close(fd) | |
|
681 | fp = posixfile(temp, "wb") | |
|
682 | try: | |
|
683 | fp.write(posixfile(name, "rb").read()) | |
|
684 | except: | |
|
685 | try: os.unlink(temp) | |
|
686 | except: pass | |
|
687 | raise | |
|
688 | fp.close() | |
|
689 | st = os.lstat(name) | |
|
690 | os.chmod(temp, st.st_mode) | |
|
691 | return temp | |
|
692 | ||
|
693 | class atomictempfile(posixfile): | |
|
694 | """the file will only be copied when rename is called""" | |
|
695 | def __init__(self, name, mode): | |
|
696 | self.__name = name | |
|
697 | self.temp = mktempcopy(name) | |
|
698 | posixfile.__init__(self, self.temp, mode) | |
|
699 | def rename(self): | |
|
700 | if not self.closed: | |
|
701 | posixfile.close(self) | |
|
702 | rename(self.temp, self.__name) | |
|
703 | def __del__(self): | |
|
704 | if not self.closed: | |
|
705 | try: | |
|
706 | os.unlink(self.temp) | |
|
707 | except: pass | |
|
708 | posixfile.close(self) | |
|
709 | ||
|
710 | class atomicfile(atomictempfile): | |
|
711 | """the file will only be copied on close""" | |
|
712 | def __init__(self, name, mode): | |
|
713 | atomictempfile.__init__(self, name, mode) | |
|
714 | def close(self): | |
|
715 | self.rename() | |
|
716 | def __del__(self): | |
|
717 | self.rename() | |
|
718 | ||
|
719 | def o(path, mode="r", text=False, atomic=False, atomictemp=False): | |
|
720 | if audit_p: | |
|
721 | audit_path(path) | |
|
722 | f = os.path.join(p, path) | |
|
723 | ||
|
724 | if not text: | |
|
725 | mode += "b" # for that other OS | |
|
726 | ||
|
727 | if mode[0] != "r": | |
|
728 | try: | |
|
729 | nlink = nlinks(f) | |
|
730 | except OSError: | |
|
731 | d = os.path.dirname(f) | |
|
732 | if not os.path.isdir(d): | |
|
733 | os.makedirs(d) | |
|
734 | else: | |
|
735 | if atomic: | |
|
736 | return atomicfile(f, mode) | |
|
737 | elif atomictemp: | |
|
738 | return atomictempfile(f, mode) | |
|
739 | if nlink > 1: | |
|
740 | rename(mktempcopy(f), f) | |
|
741 | return posixfile(f, mode) | |
|
742 | ||
|
743 | return o | |
|
744 | ||
|
725 | 745 | class chunkbuffer(object): |
|
726 | 746 | """Allow arbitrary sized chunks of data to be efficiently read from an |
|
727 | 747 | iterator over chunks of arbitrary size.""" |
@@ -16,9 +16,9 import win32api | |||
|
16 | 16 | from demandload import * |
|
17 | 17 | from i18n import gettext as _ |
|
18 | 18 | demandload(globals(), 'errno os pywintypes win32con win32file win32process') |
|
19 | demandload(globals(), 'winerror') | |
|
19 | demandload(globals(), 'cStringIO winerror') | |
|
20 | 20 | |
|
21 |
class WinError |
|
|
21 | class WinError: | |
|
22 | 22 | winerror_map = { |
|
23 | 23 | winerror.ERROR_ACCESS_DENIED: errno.EACCES, |
|
24 | 24 | winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES, |
@@ -105,7 +105,7 class WinError(OSError): | |||
|
105 | 105 | winerror.ERROR_OUTOFMEMORY: errno.ENOMEM, |
|
106 | 106 | winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES, |
|
107 | 107 | winerror.ERROR_PATH_BUSY: errno.EBUSY, |
|
108 |
winerror.ERROR_PATH_NOT_FOUND: errno.ENOT |
|
|
108 | winerror.ERROR_PATH_NOT_FOUND: errno.ENOENT, | |
|
109 | 109 | winerror.ERROR_PIPE_BUSY: errno.EBUSY, |
|
110 | 110 | winerror.ERROR_PIPE_CONNECTED: errno.EPIPE, |
|
111 | 111 | winerror.ERROR_PIPE_LISTENING: errno.EPIPE, |
@@ -129,6 +129,19 class WinError(OSError): | |||
|
129 | 129 | |
|
130 | 130 | def __init__(self, err): |
|
131 | 131 | self.win_errno, self.win_function, self.win_strerror = err |
|
132 | if self.win_strerror.endswith('.'): | |
|
133 | self.win_strerror = self.win_strerror[:-1] | |
|
134 | ||
|
135 | class WinIOError(WinError, IOError): | |
|
136 | def __init__(self, err, filename=None): | |
|
137 | WinError.__init__(self, err) | |
|
138 | IOError.__init__(self, self.winerror_map.get(self.win_errno, 0), | |
|
139 | self.win_strerror) | |
|
140 | self.filename = filename | |
|
141 | ||
|
142 | class WinOSError(WinError, OSError): | |
|
143 | def __init__(self, err): | |
|
144 | WinError.__init__(self, err) | |
|
132 | 145 | OSError.__init__(self, self.winerror_map.get(self.win_errno, 0), |
|
133 | 146 | self.win_strerror) |
|
134 | 147 | |
@@ -137,7 +150,7 def os_link(src, dst): | |||
|
137 | 150 | try: |
|
138 | 151 | win32file.CreateHardLink(dst, src) |
|
139 | 152 | except pywintypes.error, details: |
|
140 | raise WinError(details) | |
|
153 | raise WinOSError(details) | |
|
141 | 154 | |
|
142 | 155 | def nlinks(pathname): |
|
143 | 156 | """Return number of hardlinks for the given file.""" |
@@ -169,3 +182,99 def system_rcpath_win32(): | |||
|
169 | 182 | proc = win32api.GetCurrentProcess() |
|
170 | 183 | filename = win32process.GetModuleFileNameEx(proc, 0) |
|
171 | 184 | return [os.path.join(os.path.dirname(filename), 'mercurial.ini')] |
|
185 | ||
|
186 | class posixfile(object): | |
|
187 | '''file object with posix-like semantics. on windows, normal | |
|
188 | files can not be deleted or renamed if they are open. must open | |
|
189 | with win32file.FILE_SHARE_DELETE. this flag does not exist on | |
|
190 | windows <= nt.''' | |
|
191 | ||
|
192 | # tried to use win32file._open_osfhandle to pass fd to os.fdopen, | |
|
193 | # but does not work at all. wrap win32 file api instead. | |
|
194 | ||
|
195 | def __init__(self, name, mode='rb'): | |
|
196 | access = 0 | |
|
197 | if 'r' in mode or '+' in mode: | |
|
198 | access |= win32file.GENERIC_READ | |
|
199 | if 'w' in mode or 'a' in mode: | |
|
200 | access |= win32file.GENERIC_WRITE | |
|
201 | if 'r' in mode: | |
|
202 | creation = win32file.OPEN_EXISTING | |
|
203 | elif 'a' in mode: | |
|
204 | creation = win32file.OPEN_ALWAYS | |
|
205 | else: | |
|
206 | creation = win32file.CREATE_ALWAYS | |
|
207 | try: | |
|
208 | self.handle = win32file.CreateFile(name, | |
|
209 | access, | |
|
210 | win32file.FILE_SHARE_READ | | |
|
211 | win32file.FILE_SHARE_WRITE | | |
|
212 | win32file.FILE_SHARE_DELETE, | |
|
213 | None, | |
|
214 | creation, | |
|
215 | win32file.FILE_ATTRIBUTE_NORMAL, | |
|
216 | 0) | |
|
217 | except pywintypes.error, err: | |
|
218 | raise WinIOError(err, name) | |
|
219 | self.closed = False | |
|
220 | self.name = name | |
|
221 | self.mode = mode | |
|
222 | ||
|
223 | def read(self, count=-1): | |
|
224 | try: | |
|
225 | cs = cStringIO.StringIO() | |
|
226 | while count: | |
|
227 | wincount = int(count) | |
|
228 | if wincount == -1: | |
|
229 | wincount = 1048576 | |
|
230 | val, data = win32file.ReadFile(self.handle, wincount) | |
|
231 | if not data: break | |
|
232 | cs.write(data) | |
|
233 | if count != -1: | |
|
234 | count -= len(data) | |
|
235 | return cs.getvalue() | |
|
236 | except pywintypes.error, err: | |
|
237 | raise WinIOError(err) | |
|
238 | ||
|
239 | def write(self, data): | |
|
240 | try: | |
|
241 | if 'a' in self.mode: | |
|
242 | win32file.SetFilePointer(self.handle, 0, win32file.FILE_END) | |
|
243 | nwrit = 0 | |
|
244 | while nwrit < len(data): | |
|
245 | val, nwrit = win32file.WriteFile(self.handle, data) | |
|
246 | data = data[nwrit:] | |
|
247 | except pywintypes.error, err: | |
|
248 | raise WinIOError(err) | |
|
249 | ||
|
250 | def seek(self, pos, whence=0): | |
|
251 | try: | |
|
252 | win32file.SetFilePointer(self.handle, int(pos), whence) | |
|
253 | except pywintypes.error, err: | |
|
254 | raise WinIOError(err) | |
|
255 | ||
|
256 | def tell(self): | |
|
257 | try: | |
|
258 | return win32file.SetFilePointer(self.handle, 0, | |
|
259 | win32file.FILE_CURRENT) | |
|
260 | except pywintypes.error, err: | |
|
261 | raise WinIOError(err) | |
|
262 | ||
|
263 | def close(self): | |
|
264 | if not self.closed: | |
|
265 | self.handle = None | |
|
266 | self.closed = True | |
|
267 | ||
|
268 | def flush(self): | |
|
269 | try: | |
|
270 | win32file.FlushFileBuffers(self.handle) | |
|
271 | except pywintypes.error, err: | |
|
272 | raise WinIOError(err) | |
|
273 | ||
|
274 | def truncate(self, pos=0): | |
|
275 | try: | |
|
276 | win32file.SetFilePointer(self.handle, int(pos), | |
|
277 | win32file.FILE_BEGIN) | |
|
278 | win32file.SetEndOfFile(self.handle) | |
|
279 | except pywintypes.error, err: | |
|
280 | raise WinIOError(err) |
General Comments 0
You need to be logged in to leave comments.
Login now