Show More
@@ -6,7 +6,7 b'' | |||||
6 | # of the GNU General Public License, incorporated herein by reference. |
|
6 | # of the GNU General Public License, incorporated herein by reference. | |
7 |
|
7 | |||
8 | from demandload import * |
|
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 | # writes to metadata files are ordered. reads: changelog, manifest, |
|
11 | # writes to metadata files are ordered. reads: changelog, manifest, | |
12 | # normal files. writes: normal files, manifest, changelog. |
|
12 | # normal files. writes: normal files, manifest, changelog. | |
@@ -36,19 +36,21 b' class appendfile(object):' | |||||
36 | def __init__(self, fp, tmpname): |
|
36 | def __init__(self, fp, tmpname): | |
37 | if tmpname: |
|
37 | if tmpname: | |
38 | self.tmpname = tmpname |
|
38 | self.tmpname = tmpname | |
39 |
self.tmpfp = |
|
39 | self.tmpfp = util.posixfile(self.tmpname, 'ab+') | |
40 | else: |
|
40 | else: | |
41 | fd, self.tmpname = tempfile.mkstemp() |
|
41 | fd, self.tmpname = tempfile.mkstemp() | |
42 |
|
|
42 | os.close(fd) | |
|
43 | self.tmpfp = util.posixfile(self.tmpname, 'ab+') | |||
43 | self.realfp = fp |
|
44 | self.realfp = fp | |
44 | self.offset = fp.tell() |
|
45 | self.offset = fp.tell() | |
45 | # real file is not written by anyone else. cache its size so |
|
46 | # real file is not written by anyone else. cache its size so | |
46 | # seek and read can be fast. |
|
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 | def end(self): |
|
51 | def end(self): | |
50 | self.tmpfp.flush() # make sure the stat is correct |
|
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 | def tell(self): |
|
55 | def tell(self): | |
54 | return self.offset |
|
56 | return self.offset |
@@ -160,7 +160,7 b' class bundlerepository(localrepo.localre' | |||||
160 | def __init__(self, ui, path, bundlename): |
|
160 | def __init__(self, ui, path, bundlename): | |
161 | localrepo.localrepository.__init__(self, ui, path) |
|
161 | localrepo.localrepository.__init__(self, ui, path) | |
162 | f = open(bundlename, "rb") |
|
162 | f = open(bundlename, "rb") | |
163 |
s = |
|
163 | s = util.fstat(f) | |
164 | self.bundlefile = f |
|
164 | self.bundlefile = f | |
165 | header = self.bundlefile.read(6) |
|
165 | header = self.bundlefile.read(6) | |
166 | if not header.startswith("HG"): |
|
166 | if not header.startswith("HG"): |
@@ -14,7 +14,7 b' from node import *' | |||||
14 | from i18n import gettext as _ |
|
14 | from i18n import gettext as _ | |
15 | from demandload import demandload |
|
15 | from demandload import demandload | |
16 | demandload(globals(), "binascii changegroup errno heapq mdiff os") |
|
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 | # revlog version strings |
|
19 | # revlog version strings | |
20 | REVLOGV0 = 0 |
|
20 | REVLOGV0 = 0 | |
@@ -322,7 +322,7 b' class revlog(object):' | |||||
322 | i = "" |
|
322 | i = "" | |
323 | else: |
|
323 | else: | |
324 | try: |
|
324 | try: | |
325 |
st = |
|
325 | st = util.fstat(f) | |
326 | except AttributeError, inst: |
|
326 | except AttributeError, inst: | |
327 | st = None |
|
327 | st = None | |
328 | else: |
|
328 | else: |
@@ -57,7 +57,7 b' class sshrepository(remoterepository):' | |||||
57 |
|
57 | |||
58 | def readerr(self): |
|
58 | def readerr(self): | |
59 | while 1: |
|
59 | while 1: | |
60 |
size = |
|
60 | size = util.fstat(self.pipee).st_size | |
61 | if size == 0: break |
|
61 | if size == 0: break | |
62 | l = self.pipee.readline() |
|
62 | l = self.pipee.readline() | |
63 | if not l: break |
|
63 | if not l: break |
@@ -406,8 +406,18 b' def rename(src, dst):' | |||||
406 | """forcibly rename a file""" |
|
406 | """forcibly rename a file""" | |
407 | try: |
|
407 | try: | |
408 | os.rename(src, dst) |
|
408 | os.rename(src, dst) | |
409 | except: |
|
409 | except OSError, err: | |
410 | os.unlink(dst) |
|
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 | os.rename(src, dst) |
|
421 | os.rename(src, dst) | |
412 |
|
422 | |||
413 | def unlink(f): |
|
423 | def unlink(f): | |
@@ -449,90 +459,13 b' def audit_path(path):' | |||||
449 | or os.pardir in parts): |
|
459 | or os.pardir in parts): | |
450 | raise Abort(_("path contains illegal component: %s\n") % path) |
|
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 | def _makelock_file(info, pathname): |
|
462 | def _makelock_file(info, pathname): | |
530 | ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL) |
|
463 | ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL) | |
531 | os.write(ld, info) |
|
464 | os.write(ld, info) | |
532 | os.close(ld) |
|
465 | os.close(ld) | |
533 |
|
466 | |||
534 | def _readlock_file(pathname): |
|
467 | def _readlock_file(pathname): | |
535 | return file(pathname).read() |
|
468 | return posixfile(pathname).read() | |
536 |
|
469 | |||
537 | def nlinks(pathname): |
|
470 | def nlinks(pathname): | |
538 | """Return number of hardlinks for the given file.""" |
|
471 | """Return number of hardlinks for the given file.""" | |
@@ -544,6 +477,15 b' else:' | |||||
544 | def os_link(src, dst): |
|
477 | def os_link(src, dst): | |
545 | raise OSError(0, _("Hardlinks not supported")) |
|
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 | # Platform specific variants |
|
489 | # Platform specific variants | |
548 | if os.name == 'nt': |
|
490 | if os.name == 'nt': | |
549 | demandload(globals(), "msvcrt") |
|
491 | demandload(globals(), "msvcrt") | |
@@ -722,6 +664,84 b' else:' | |||||
722 | return _("stopped by signal %d") % val, val |
|
664 | return _("stopped by signal %d") % val, val | |
723 | raise ValueError(_("invalid exit code")) |
|
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 | class chunkbuffer(object): |
|
745 | class chunkbuffer(object): | |
726 | """Allow arbitrary sized chunks of data to be efficiently read from an |
|
746 | """Allow arbitrary sized chunks of data to be efficiently read from an | |
727 | iterator over chunks of arbitrary size.""" |
|
747 | iterator over chunks of arbitrary size.""" |
@@ -16,9 +16,9 b' import win32api' | |||||
16 | from demandload import * |
|
16 | from demandload import * | |
17 | from i18n import gettext as _ |
|
17 | from i18n import gettext as _ | |
18 | demandload(globals(), 'errno os pywintypes win32con win32file win32process') |
|
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 | winerror_map = { |
|
22 | winerror_map = { | |
23 | winerror.ERROR_ACCESS_DENIED: errno.EACCES, |
|
23 | winerror.ERROR_ACCESS_DENIED: errno.EACCES, | |
24 | winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES, |
|
24 | winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES, | |
@@ -105,7 +105,7 b' class WinError(OSError):' | |||||
105 | winerror.ERROR_OUTOFMEMORY: errno.ENOMEM, |
|
105 | winerror.ERROR_OUTOFMEMORY: errno.ENOMEM, | |
106 | winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES, |
|
106 | winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES, | |
107 | winerror.ERROR_PATH_BUSY: errno.EBUSY, |
|
107 | winerror.ERROR_PATH_BUSY: errno.EBUSY, | |
108 |
winerror.ERROR_PATH_NOT_FOUND: errno.ENOT |
|
108 | winerror.ERROR_PATH_NOT_FOUND: errno.ENOENT, | |
109 | winerror.ERROR_PIPE_BUSY: errno.EBUSY, |
|
109 | winerror.ERROR_PIPE_BUSY: errno.EBUSY, | |
110 | winerror.ERROR_PIPE_CONNECTED: errno.EPIPE, |
|
110 | winerror.ERROR_PIPE_CONNECTED: errno.EPIPE, | |
111 | winerror.ERROR_PIPE_LISTENING: errno.EPIPE, |
|
111 | winerror.ERROR_PIPE_LISTENING: errno.EPIPE, | |
@@ -129,6 +129,19 b' class WinError(OSError):' | |||||
129 |
|
129 | |||
130 | def __init__(self, err): |
|
130 | def __init__(self, err): | |
131 | self.win_errno, self.win_function, self.win_strerror = err |
|
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 | OSError.__init__(self, self.winerror_map.get(self.win_errno, 0), |
|
145 | OSError.__init__(self, self.winerror_map.get(self.win_errno, 0), | |
133 | self.win_strerror) |
|
146 | self.win_strerror) | |
134 |
|
147 | |||
@@ -137,7 +150,7 b' def os_link(src, dst):' | |||||
137 | try: |
|
150 | try: | |
138 | win32file.CreateHardLink(dst, src) |
|
151 | win32file.CreateHardLink(dst, src) | |
139 | except pywintypes.error, details: |
|
152 | except pywintypes.error, details: | |
140 | raise WinError(details) |
|
153 | raise WinOSError(details) | |
141 |
|
154 | |||
142 | def nlinks(pathname): |
|
155 | def nlinks(pathname): | |
143 | """Return number of hardlinks for the given file.""" |
|
156 | """Return number of hardlinks for the given file.""" | |
@@ -169,3 +182,99 b' def system_rcpath_win32():' | |||||
169 | proc = win32api.GetCurrentProcess() |
|
182 | proc = win32api.GetCurrentProcess() | |
170 | filename = win32process.GetModuleFileNameEx(proc, 0) |
|
183 | filename = win32process.GetModuleFileNameEx(proc, 0) | |
171 | return [os.path.join(os.path.dirname(filename), 'mercurial.ini')] |
|
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