def run(self): dir_handle = winutil.create_file( self.path_to_watch, winutil.FILE_LIST_DIRECTORY, winutil.FILE_SHARE_READ, winutil.OPEN_EXISTING, winutil.FILE_FLAG_BACKUP_SEMANTICS, ) try: buffer = b'0' * 8192 while True: try: results = winutil.read_directory_changes( dir_handle, buffer, True, # Watch sub-directories as well winutil.FILE_NOTIFY_CHANGE_FILE_NAME | winutil.FILE_NOTIFY_CHANGE_DIR_NAME | winutil.FILE_NOTIFY_CHANGE_ATTRIBUTES | winutil.FILE_NOTIFY_CHANGE_SIZE | winutil.FILE_NOTIFY_CHANGE_LAST_WRITE | winutil.FILE_NOTIFY_CHANGE_SECURITY, ) for action, filename in results: if self.file_is_watched(filename): self.modified_queue.put( os.path.join(self.path_to_watch, filename)) except OverflowError: pass # the buffer overflowed, there are unknown changes except Exception: import traceback traceback.print_exc()
def __init__(self, path): from collections import defaultdict from calibre_extensions import winutil self.handle_map = {} if isbytestring(path): path = path.decode(filesystem_encoding) if not os.path.exists(path): return names = os.listdir(path) name_to_fileid = {x:windows_get_fileid(os.path.join(path, x)) for x in names} fileid_to_names = defaultdict(set) for name, fileid in iteritems(name_to_fileid): fileid_to_names[fileid].add(name) for x in names: f = os.path.normcase(os.path.abspath(os.path.join(path, x))) if not os.path.isfile(f): continue with suppress(OSError): # Ensure the file is not read-only winutil.set_file_attributes(f, winutil.FILE_ATTRIBUTE_NORMAL) try: h = winutil.create_file(f, winutil.GENERIC_READ, winutil.FILE_SHARE_DELETE, winutil.OPEN_EXISTING, winutil.FILE_FLAG_SEQUENTIAL_SCAN) except OSError as e: if e.winerror == winutil.ERROR_SHARING_VIOLATION: # The file could be a hardlink to an already opened file, # in which case we use the same handle for both files fileid = name_to_fileid[x] found = False if fileid is not None: for other in fileid_to_names[fileid]: other = os.path.normcase(os.path.abspath(os.path.join(path, other))) if other in self.handle_map: self.handle_map[f] = self.handle_map[other] found = True break if found: continue self.close_handles() if e.winerror == winutil.ERROR_SHARING_VIOLATION: err = IOError(errno.EACCES, _('File is open in another process')) err.filename = f raise err prints('CreateFile failed for: %r' % f) raise except: self.close_handles() prints('CreateFile failed for: %r' % f) raise self.handle_map[f] = h
def windows_get_size(path): ''' On windows file sizes are only accurately stored in the actual file, not in the directory entry (which could be out of date). So we open the file, and get the actual size. ''' from calibre_extensions import winutil if isbytestring(path): path = path.decode(filesystem_encoding) with closing( winutil.create_file( path, 0, winutil.FILE_SHARE_READ | winutil.FILE_SHARE_WRITE | winutil.FILE_SHARE_DELETE, winutil.OPEN_EXISTING, 0)) as h: return winutil.get_file_size(h)
def windows_open(path): if isinstance(path, bytes): path = os.fsdecode(path) h = winutil.create_file( path, winutil.GENERIC_READ | winutil.GENERIC_WRITE, # Open for reading and writing 0, # Open exclusive winutil.OPEN_ALWAYS, # If file does not exist, create it winutil.FILE_ATTRIBUTE_NORMAL, # Normal attributes ) fd = msvcrt.open_osfhandle(int(h), 0) ans = os.fdopen(fd, 'r+b') h.detach() return ans
def os_open(path, flags, mode=0o777, share_flags=winutil.FILE_SHARE_VALID_FLAGS): ''' Replacement for os.open() allowing moving or unlinking before closing ''' if not isinstance(flags, Integral): raise TypeError('flags must be an integer') if not isinstance(mode, Integral): raise TypeError('mode must be an integer') if share_flags & ~winutil.FILE_SHARE_VALID_FLAGS: raise ValueError('bad share_flags: %r' % share_flags) access_flags = _ACCESS_MAP[flags & _ACCESS_MASK] create_flags = _CREATE_MAP[flags & _CREATE_MASK] attrib_flags = winutil.FILE_ATTRIBUTE_NORMAL if flags & os.O_CREAT and mode & ~0o444 == 0: attrib_flags = winutil.FILE_ATTRIBUTE_READONLY if flags & os.O_TEMPORARY: share_flags |= winutil.FILE_SHARE_DELETE attrib_flags |= winutil.FILE_FLAG_DELETE_ON_CLOSE access_flags |= winutil.DELETE if flags & os.O_SHORT_LIVED: attrib_flags |= winutil.FILE_ATTRIBUTE_TEMPORARY if flags & os.O_SEQUENTIAL: attrib_flags |= winutil.FILE_FLAG_SEQUENTIAL_SCAN if flags & os.O_RANDOM: attrib_flags |= winutil.FILE_FLAG_RANDOM_ACCESS h = winutil.create_file(path, access_flags, share_flags, create_flags, attrib_flags) ans = msvcrt.open_osfhandle(int(h), flags | os.O_NOINHERIT) h.detach() return ans
def test_winutil(self): import tempfile from calibre import strftime from calibre_extensions import winutil self.assertEqual(winutil.special_folder_path(winutil.CSIDL_APPDATA), winutil.known_folder_path(winutil.FOLDERID_RoamingAppData)) self.assertEqual(winutil.special_folder_path(winutil.CSIDL_LOCAL_APPDATA), winutil.known_folder_path(winutil.FOLDERID_LocalAppData)) self.assertEqual(winutil.special_folder_path(winutil.CSIDL_FONTS), winutil.known_folder_path(winutil.FOLDERID_Fonts)) self.assertEqual(winutil.special_folder_path(winutil.CSIDL_PROFILE), winutil.known_folder_path(winutil.FOLDERID_Profile)) def au(x, name): self.assertTrue( isinstance(x, unicode_type), '%s() did not return a unicode string, instead returning: %r' % (name, x)) for x in 'username temp_path locale_name'.split(): au(getattr(winutil, x)(), x) d = winutil.localeconv() au(d['thousands_sep'], 'localeconv') au(d['decimal_point'], 'localeconv') for k, v in iteritems(d): au(v, k) os.environ['XXXTEST'] = 'YYY' self.assertEqual(getenv('XXXTEST'), 'YYY') del os.environ['XXXTEST'] self.assertIsNone(getenv('XXXTEST')) for k in os.environ: v = getenv(k) if v is not None: au(v, 'getenv-' + k) t = time.localtime() fmt = '%Y%a%b%e%H%M' for fmt in (fmt, fmt.encode('ascii')): x = strftime(fmt, t) au(x, 'strftime') tdir = tempfile.mkdtemp(dir=winutil.temp_path()) path = os.path.join(tdir, 'test-create-file.txt') h = winutil.create_file( path, winutil.GENERIC_READ | winutil.GENERIC_WRITE, 0, winutil.OPEN_ALWAYS, winutil.FILE_ATTRIBUTE_NORMAL) self.assertRaises(OSError, winutil.delete_file, path) del h winutil.delete_file(path) self.assertRaises(OSError, winutil.delete_file, path) self.assertRaises(OSError, winutil.create_file, os.path.join(path, 'cannot'), winutil.GENERIC_READ, 0, winutil.OPEN_ALWAYS, winutil.FILE_ATTRIBUTE_NORMAL) self.assertTrue(winutil.supports_hardlinks(os.path.abspath(os.getcwd())[0] + ':\\')) sz = 23 data = os.urandom(sz) open(path, 'wb').write(data) h = winutil.Handle(0, winutil.ModuleHandle, 'moo') r = repr(h) h2 = winutil.Handle(h.detach(), winutil.ModuleHandle, 'moo') self.assertEqual(r, repr(h2)) h2.close() h = winutil.create_file( path, winutil.GENERIC_READ | winutil.GENERIC_WRITE, 0, winutil.OPEN_ALWAYS, winutil.FILE_ATTRIBUTE_NORMAL) self.assertEqual(winutil.get_file_size(h), sz) self.assertRaises(OSError, winutil.set_file_pointer, h, 23, 23) self.assertEqual(winutil.read_file(h), data) self.assertEqual(winutil.read_file(h), b'') winutil.set_file_pointer(h, 3) self.assertEqual(winutil.read_file(h), data[3:]) self.assertEqual(winutil.nlinks(path), 1) npath = path + '.2' winutil.create_hard_link(npath, path) h.close() self.assertEqual(open(npath, 'rb').read(), data) self.assertEqual(winutil.nlinks(path), 2) winutil.delete_file(path) self.assertEqual(winutil.nlinks(npath), 1) winutil.set_file_attributes(npath, winutil.FILE_ATTRIBUTE_READONLY) self.assertRaises(OSError, winutil.delete_file, npath) winutil.set_file_attributes(npath, winutil.FILE_ATTRIBUTE_NORMAL) winutil.delete_file(npath) self.assertGreater(min(winutil.get_disk_free_space(None)), 0) open(path, 'wb').close() open(npath, 'wb').close() winutil.move_file(path, npath, winutil.MOVEFILE_WRITE_THROUGH | winutil.MOVEFILE_REPLACE_EXISTING) self.assertFalse(os.path.exists(path)) os.remove(npath) dpath = tempfile.mkdtemp(dir=os.path.dirname(path)) dh = winutil.create_file( dpath, winutil.FILE_LIST_DIRECTORY, winutil.FILE_SHARE_READ, winutil.OPEN_EXISTING, winutil.FILE_FLAG_BACKUP_SEMANTICS, ) from threading import Thread, Event started = Event() events = [] def read_changes(): buffer = b'0' * 8192 started.set() events.extend(winutil.read_directory_changes( dh, buffer, True, winutil.FILE_NOTIFY_CHANGE_FILE_NAME | winutil.FILE_NOTIFY_CHANGE_DIR_NAME | winutil.FILE_NOTIFY_CHANGE_ATTRIBUTES | winutil.FILE_NOTIFY_CHANGE_SIZE | winutil.FILE_NOTIFY_CHANGE_LAST_WRITE | winutil.FILE_NOTIFY_CHANGE_SECURITY )) t = Thread(target=read_changes, daemon=True) t.start() started.wait(1) t.join(0.1) testp = os.path.join(dpath, 'test') open(testp, 'w').close() t.join(4) self.assertTrue(events) for actions, path in events: self.assertEqual(os.path.join(dpath, path), testp) dh.close() os.remove(testp) os.rmdir(dpath) del h shutil.rmtree(tdir) m = winutil.create_mutex("test-mutex", False) self.assertRaises(OSError, winutil.create_mutex, 'test-mutex', False) m.close() self.assertEqual(winutil.parse_cmdline('"c:\\test exe.exe" "some arg" 2'), ('c:\\test exe.exe', 'some arg', '2'))