1# Copyright 2016 Google Inc. All rights reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15import errno 16import time 17 18import pywintypes 19import win32con 20import win32file 21 22from oauth2client.contrib import locked_file 23 24 25class _Win32Opener(locked_file._Opener): 26 """Open, lock, and unlock a file using windows primitives.""" 27 28 # Error #33: 29 # 'The process cannot access the file because another process' 30 FILE_IN_USE_ERROR = 33 31 32 # Error #158: 33 # 'The segment is already unlocked.' 34 FILE_ALREADY_UNLOCKED_ERROR = 158 35 36 def open_and_lock(self, timeout, delay): 37 """Open the file and lock it. 38 39 Args: 40 timeout: float, How long to try to lock for. 41 delay: float, How long to wait between retries 42 43 Raises: 44 AlreadyLockedException: if the lock is already acquired. 45 IOError: if the open fails. 46 CredentialsFileSymbolicLinkError: if the file is a symbolic 47 link. 48 """ 49 if self._locked: 50 raise locked_file.AlreadyLockedException( 51 'File {0} is already locked'.format(self._filename)) 52 start_time = time.time() 53 54 locked_file.validate_file(self._filename) 55 try: 56 self._fh = open(self._filename, self._mode) 57 except IOError as e: 58 # If we can't access with _mode, try _fallback_mode 59 # and don't lock. 60 if e.errno == errno.EACCES: 61 self._fh = open(self._filename, self._fallback_mode) 62 return 63 64 # We opened in _mode, try to lock the file. 65 while True: 66 try: 67 hfile = win32file._get_osfhandle(self._fh.fileno()) 68 win32file.LockFileEx( 69 hfile, 70 (win32con.LOCKFILE_FAIL_IMMEDIATELY | 71 win32con.LOCKFILE_EXCLUSIVE_LOCK), 0, -0x10000, 72 pywintypes.OVERLAPPED()) 73 self._locked = True 74 return 75 except pywintypes.error as e: 76 if timeout == 0: 77 raise 78 79 # If the error is not that the file is already 80 # in use, raise. 81 if e[0] != _Win32Opener.FILE_IN_USE_ERROR: 82 raise 83 84 # We could not acquire the lock. Try again. 85 if (time.time() - start_time) >= timeout: 86 locked_file.logger.warn('Could not lock %s in %s seconds', 87 self._filename, timeout) 88 if self._fh: 89 self._fh.close() 90 self._fh = open(self._filename, self._fallback_mode) 91 return 92 time.sleep(delay) 93 94 def unlock_and_close(self): 95 """Close and unlock the file using the win32 primitive.""" 96 if self._locked: 97 try: 98 hfile = win32file._get_osfhandle(self._fh.fileno()) 99 win32file.UnlockFileEx(hfile, 0, -0x10000, 100 pywintypes.OVERLAPPED()) 101 except pywintypes.error as e: 102 if e[0] != _Win32Opener.FILE_ALREADY_UNLOCKED_ERROR: 103 raise 104 self._locked = False 105 if self._fh: 106 self._fh.close() 107