From 7a0e8f66785f165d76477105c2298e03eb5d0695 Mon Sep 17 00:00:00 2001 From: Liryna Date: Wed, 27 Jul 2016 20:55:20 +0200 Subject: Update Dokan wrapper to 1.0.0 --- fs/expose/dokan/__init__.py | 548 +++++++++++++++++++++++++------------------- fs/expose/dokan/libdokan.py | 304 +++++++++++++----------- 2 files changed, 486 insertions(+), 366 deletions(-) diff --git a/fs/expose/dokan/__init__.py b/fs/expose/dokan/__init__.py index 32f7e57..bfc5294 100644 --- a/fs/expose/dokan/__init__.py +++ b/fs/expose/dokan/__init__.py @@ -7,27 +7,33 @@ Expose an FS object to the native filesystem via Dokan. This module provides the necessary interfaces to mount an FS object into the local filesystem using Dokan on win32:: - http://dokan-dev.net/en/ + http://dokan-dev.github.io/ -For simple usage, the function 'mount' takes an FS object and a drive letter, -and exposes the given FS as that drive:: +For simple usage, the function 'mount' takes an FS object +and new device mount point or an existing empty folder +and exposes the given FS as that path:: >>> from fs.memoryfs import MemoryFS >>> from fs.expose import dokan >>> fs = MemoryFS() - >>> mp = dokan.mount(fs,"Q") - >>> mp.drive - 'Q' + >>> # Mount device mount point + >>> mp = dokan.mount(fs, "Q:\\") >>> mp.path 'Q:\\' >>> mp.unmount() + >>> fs = MemoryFS() + >>> # Mount in an existing empty folder. + >>> mp = dokan.mount(fs, "C:\\test") + >>> mp.path + 'C:\\test' + >>> mp.unmount() The above spawns a new background process to manage the Dokan event loop, which can be controlled through the returned subprocess.Popen object. To avoid spawning a new process, set the 'foreground' option:: >>> # This will block until the filesystem is unmounted - >>> dokan.mount(fs,"Q",foreground=True) + >>> dokan.mount(fs, "Q", foreground=True) Any additional options for the Dokan process can be passed as keyword arguments to the 'mount' function. @@ -37,7 +43,7 @@ instantiate the MountProcess class directly. It accepts all options available to subprocess.Popen:: >>> from subprocess import PIPE - >>> mp = dokan.MountProcess(fs,"Q",stderr=PIPE) + >>> mp = dokan.MountProcess(fs, "Q:\\", stderr=PIPE) >>> dokan_errors = mp.communicate()[1] @@ -52,19 +58,22 @@ systems with Dokan installed. """ # Copyright (c) 2009-2010, Cloud Matrix Pty. Ltd. +# Copyright (c) 2016-2016, Adrien J. . # All rights reserved; available under the terms of the MIT License. from __future__ import with_statement +import six import sys - import os -import signal import errno import time import stat as statinfo import subprocess -import cPickle +try: + import cPickle +except ImportError: + import pickle as cPickle import datetime import ctypes from collections import deque @@ -76,7 +85,9 @@ from fs.local_functools import wraps from fs.wrapfs import WrapFS try: - import libdokan + if six.PY2: import libdokan + elif six.PY3: from . import libdokan + else: raise except (NotImplementedError, EnvironmentError, ImportError, NameError,): is_available = False sys.modules.pop("fs.expose.dokan.libdokan", None) @@ -89,22 +100,46 @@ else: import logging logger = logging.getLogger("fs.expose.dokan") +if six.PY3: + xrange = range + # Options controlling the behavior of the Dokan filesystem +# Ouput debug message DOKAN_OPTION_DEBUG = 1 +# Ouput debug message to stderr DOKAN_OPTION_STDERR = 2 +# Use alternate stream DOKAN_OPTION_ALT_STREAM = 4 -DOKAN_OPTION_KEEP_ALIVE = 8 +# Mount drive as write-protected. +DOKAN_OPTION_WRITE_PROTECT = 8 +# Use network drive, you need to install Dokan network provider. DOKAN_OPTION_NETWORK = 16 +# Use removable drive DOKAN_OPTION_REMOVABLE = 32 +# Use mount manager +DOKAN_OPTION_MOUNT_MANAGER = 64 +# Mount the drive on current session only +DOKAN_OPTION_CURRENT_SESSION = 128 +# FileLock in User Mode +DOKAN_OPTION_FILELOCK_USER_MODE = 256 # Error codes returned by DokanMain DOKAN_SUCCESS = 0 +# General Error DOKAN_ERROR = -1 +# Bad Drive letter DOKAN_DRIVE_LETTER_ERROR = -2 +# Can't install driver DOKAN_DRIVER_INSTALL_ERROR = -3 +# Driver something wrong DOKAN_START_ERROR = -4 +# Can't assign a drive letter or mount point DOKAN_MOUNT_ERROR = -5 +# Mount point is invalid +DOKAN_MOUNT_POINT_ERROR = -6 +# Requested an incompatible version +DOKAN_VERSION_ERROR = -7 # Misc windows constants FILE_LIST_DIRECTORY = 0x01 @@ -123,40 +158,50 @@ FILE_ATTRIBUTE_READONLY = 1 FILE_ATTRIBUTE_SYSTEM = 4 FILE_ATTRIBUTE_TEMPORARY = 4 -CREATE_NEW = 1 -CREATE_ALWAYS = 2 -OPEN_EXISTING = 3 -OPEN_ALWAYS = 4 -TRUNCATE_EXISTING = 5 +FILE_CREATE = 2 +FILE_OPEN = 1 +FILE_OPEN_IF = 3 +FILE_OVERWRITE = 4 +FILE_SUPERSEDE = 0 +FILE_OVERWRITE_IF = 5 FILE_GENERIC_READ = 1179785 FILE_GENERIC_WRITE = 1179926 +FILE_DELETE_ON_CLOSE = 0x00001000 + REQ_GENERIC_READ = 0x80 | 0x08 | 0x01 REQ_GENERIC_WRITE = 0x004 | 0x0100 | 0x002 | 0x0010 -ERROR_ACCESS_DENIED = 5 -ERROR_LOCK_VIOLATION = 33 -ERROR_NOT_SUPPORTED = 50 -ERROR_FILE_EXISTS = 80 -ERROR_DIR_NOT_EMPTY = 145 -ERROR_NOT_LOCKED = 158 -ERROR_LOCK_FAILED = 167 +STATUS_SUCCESS = 0x0 +STATUS_ACCESS_DENIED = 0xC0000022 +STATUS_LOCK_NOT_GRANTED = 0xC0000055 +STATUS_NOT_SUPPORTED = 0xC00000BB +STATUS_OBJECT_NAME_COLLISION = 0xC0000035 +STATUS_DIRECTORY_NOT_EMPTY = 0xC0000101 +STATUS_NOT_LOCKED = 0xC000002A +STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034 +STATUS_NOT_IMPLEMENTED = 0xC0000002 +STATUS_OBJECT_PATH_NOT_FOUND = 0xC000003A +STATUS_BUFFER_OVERFLOW = 0x80000005 + ERROR_ALREADY_EXISTS = 183 -ERROR_LOCKED = 212 -ERROR_INVALID_LOCK_RANGE = 306 +FILE_CASE_SENSITIVE_SEARCH = 0x00000001 +FILE_CASE_PRESERVED_NAMES = 0x00000002 +FILE_SUPPORTS_REMOTE_STORAGE = 0x00000100 +FILE_UNICODE_ON_DISK = 0x00000004 +FILE_PERSISTENT_ACLS = 0x00000008 # Some useful per-process global information NATIVE_ENCODING = sys.getfilesystemencoding() -DATETIME_ZERO = datetime.datetime(1,1,1,0,0,0) +DATETIME_ZERO = datetime.datetime(1, 1, 1, 0, 0, 0) DATETIME_STARTUP = datetime.datetime.utcnow() FILETIME_UNIX_EPOCH = 116444736000000000 - def handle_fs_errors(func): """Method decorator to report FS errors in the appropriate way. @@ -165,18 +210,18 @@ def handle_fs_errors(func): makes the function return zero instead of None as an indication of successful execution. """ - name = func.__name__ func = convert_fs_errors(func) + @wraps(func) - def wrapper(*args,**kwds): + def wrapper(*args, **kwds): try: - res = func(*args,**kwds) - except OSError, e: + res = func(*args, **kwds) + except OSError as e: if e.errno: res = -1 * _errno2syserrcode(e.errno) else: res = -1 - except Exception, e: + except Exception as e: raise else: if res is None: @@ -202,6 +247,7 @@ _TIMEOUT_PROTECT_QUEUE = deque() _TIMEOUT_PROTECT_WAIT_TIME = 4 * 60 _TIMEOUT_PROTECT_RESET_TIME = 5 * 60 * 1000 + def _start_timeout_protect_thread(): """Start the background thread used to protect dokan from timeouts. @@ -217,24 +263,25 @@ def _start_timeout_protect_thread(): _TIMEOUT_PROTECT_THREAD.daemon = True _TIMEOUT_PROTECT_THREAD.start() + def _run_timeout_protect_thread(): while True: with _TIMEOUT_PROTECT_COND: try: - (when,info,finished) = _TIMEOUT_PROTECT_QUEUE.popleft() + (when, info, finished) = _TIMEOUT_PROTECT_QUEUE.popleft() except IndexError: _TIMEOUT_PROTECT_COND.wait() continue if finished: continue now = time.time() - wait_time = max(0,_TIMEOUT_PROTECT_WAIT_TIME - now + when) + wait_time = max(0, _TIMEOUT_PROTECT_WAIT_TIME - now + when) time.sleep(wait_time) with _TIMEOUT_PROTECT_LOCK: if finished: continue - libdokan.DokanResetTimeout(_TIMEOUT_PROTECT_RESET_TIME,info) - _TIMEOUT_PROTECT_QUEUE.append((now+wait_time,info,finished)) + libdokan.DokanResetTimeout(_TIMEOUT_PROTECT_RESET_TIME, info) + _TIMEOUT_PROTECT_QUEUE.append((now + wait_time, info, finished)) def timeout_protect(func): @@ -244,16 +291,16 @@ def timeout_protect(func): the function, and marks it as finished when the function exits. """ @wraps(func) - def wrapper(self,*args): + def wrapper(self, *args): if _TIMEOUT_PROTECT_THREAD is None: _start_timeout_protect_thread() info = args[-1] finished = [] try: with _TIMEOUT_PROTECT_COND: - _TIMEOUT_PROTECT_QUEUE.append((time.time(),info,finished)) + _TIMEOUT_PROTECT_QUEUE.append((time.time(), info, finished)) _TIMEOUT_PROTECT_COND.notify() - return func(self,*args) + return func(self, *args) finally: with _TIMEOUT_PROTECT_LOCK: finished.append(True) @@ -262,12 +309,13 @@ def timeout_protect(func): MIN_FH = 100 + class FSOperations(object): """Object delegating all DOKAN_OPERATIONS pointers to an FS object.""" - def __init__(self, fs, fsname="Dokan FS", volname="Dokan Volume"): + def __init__(self, fs, fsname="NTFS", volname="Dokan Volume"): if libdokan is None: - msg = "dokan library (http://dokan-dev.net/en/) is not available" + msg = 'dokan library (http://dokan-dev.github.io/) is not available' raise OSError(msg) self.fs = fs self.fsname = fsname @@ -291,8 +339,8 @@ class FSOperations(object): def get_ops_struct(self): """Get a DOKAN_OPERATIONS struct mapping to our methods.""" struct = libdokan.DOKAN_OPERATIONS() - for (nm,typ) in libdokan.DOKAN_OPERATIONS._fields_: - setattr(struct,nm,typ(getattr(self,nm))) + for (nm, typ) in libdokan.DOKAN_OPERATIONS._fields_: + setattr(struct, nm, typ(getattr(self, nm))) return struct def _get_file(self, fh): @@ -309,7 +357,7 @@ class FSOperations(object): fh = self._next_handle self._next_handle += 1 lock = threading.Lock() - self._files_by_handle[fh] = (f,path,lock) + self._files_by_handle[fh] = (f, path, lock) if path not in self._files_size_written: self._files_size_written[path] = {} self._files_size_written[path][fh] = 0 @@ -359,7 +407,7 @@ class FSOperations(object): This method implements basic lock checking. It checks all the locks held against the given file, and if any overlap the given byte range - then it returns -ERROR_LOCKED. If the range is not locked, it will + then it returns STATUS_LOCK_NOT_GRANTED. If the range is not locked, it will return zero. """ if locks is None: @@ -367,7 +415,7 @@ class FSOperations(object): try: locks = self._active_locks[path] except KeyError: - return 0 + return STATUS_SUCCESS for (lh, lstart, lend) in locks: if info is not None and info.contents.Context == lh: continue @@ -375,95 +423,88 @@ class FSOperations(object): continue if lend <= offset: continue - return -ERROR_LOCKED - return 0 + return STATUS_LOCK_NOT_GRANTED + return STATUS_SUCCESS @timeout_protect @handle_fs_errors - def CreateFile(self, path, access, sharing, disposition, flags, info): - path = normpath(path) + def ZwCreateFile(self, path, securitycontext, access, attribute, sharing, disposition, options, info): + path = self._dokanpath2pyfs(path) # Can't open files that are pending delete. if self._is_pending_delete(path): - return -ERROR_ACCESS_DENIED - # If no access rights are requestsed, only basic metadata is queried. - if not access: - if self.fs.isdir(path): - info.contents.IsDirectory = True - elif not self.fs.exists(path): - raise ResourceNotFoundError(path) - return - # This is where we'd convert the access mask into an appropriate - # mode string. Unfortunately, I can't seem to work out all the - # details. I swear MS Word is trying to write to files that it - # opens without asking for write permission. - # For now, just set the mode based on disposition flag. - retcode = 0 - if disposition == CREATE_ALWAYS: - if self.fs.exists(path): - retcode = ERROR_ALREADY_EXISTS - mode = "w+b" - elif disposition == OPEN_ALWAYS: - if not self.fs.exists(path): + return STATUS_ACCESS_DENIED + + retcode = STATUS_SUCCESS + if info.contents.IsDirectory: + exist = self.fs.exists(path) + if disposition == FILE_CREATE: + if self.fs.exists(path): + retcode = STATUS_OBJECT_NAME_COLLISION + self.fs.makedir(path) + elif disposition == FILE_OPEN_IF: + if not self.fs.exists(path): + retcode = STATUS_OBJECT_PATH_NOT_FOUND + else: + # If no access rights are requestsed, only basic metadata is queried. + if not access: + if self.fs.isdir(path): + info.contents.IsDirectory = True + elif not self.fs.exists(path): + return STATUS_OBJECT_NAME_NOT_FOUND + return STATUS_SUCCESS + # This is where we'd convert the access mask into an appropriate + # mode string. Unfortunately, I can't seem to work out all the + # details. I swear MS Word is trying to write to files that it + # opens without asking for write permission. + # For now, just set the mode based on disposition flag. + if disposition == FILE_OVERWRITE_IF or disposition == FILE_SUPERSEDE: + if self.fs.exists(path): + retcode = STATUS_OBJECT_NAME_COLLISION + mode = "w+b" + elif disposition == FILE_OPEN_IF: + if not self.fs.exists(path): + mode = "w+b" + else: + mode = "r+b" + elif disposition == FILE_OPEN: + if not self.fs.exists(path): + return STATUS_OBJECT_NAME_NOT_FOUND + mode = "r+b" + elif disposition == FILE_OVERWRITE: + if not self.fs.exists(path): + return STATUS_OBJECT_NAME_NOT_FOUND + mode = "w+b" + elif disposition == FILE_CREATE: + if self.fs.exists(path): + return STATUS_OBJECT_NAME_COLLISION mode = "w+b" else: - retcode = ERROR_ALREADY_EXISTS mode = "r+b" - elif disposition == OPEN_EXISTING: - mode = "r+b" - elif disposition == TRUNCATE_EXISTING: - if not self.fs.exists(path): - raise ResourceNotFoundError(path) - mode = "w+b" - elif disposition == CREATE_NEW: - if self.fs.exists(path): - return -ERROR_ALREADY_EXISTS - mode = "w+b" - else: - mode = "r+b" - # Try to open the requested file. It may actually be a directory. - info.contents.Context = 1 - try: - f = self.fs.open(path, mode) - print path, mode, repr(f) - except ResourceInvalidError: - info.contents.IsDirectory = True - except FSError: - # Sadly, win32 OSFS will raise all kinds of strange errors - # if you try to open() a directory. Need to check by hand. - if self.fs.isdir(path): + # Try to open the requested file. It may actually be a directory. + info.contents.Context = 1 + try: + f = self.fs.open(path, mode) + # print(path, mode, repr(f)) + except ResourceInvalidError: info.contents.IsDirectory = True + except FSError as e: + # Sadly, win32 OSFS will raise all kinds of strange errors + # if you try to open() a directory. Need to check by hand. + if self.fs.isdir(path): + info.contents.IsDirectory = True + else: + # print(e) + raise else: - raise - else: - info.contents.Context = self._reg_file(f, path) + info.contents.Context = self._reg_file(f, path) + if retcode == STATUS_SUCCESS and (options & FILE_DELETE_ON_CLOSE): + self._pending_delete.add(path) return retcode - @timeout_protect - @handle_fs_errors - def OpenDirectory(self, path, info): - path = normpath(path) - if self._is_pending_delete(path): - raise ResourceNotFoundError(path) - if not self.fs.isdir(path): - if not self.fs.exists(path): - raise ResourceNotFoundError(path) - else: - raise ResourceInvalidError(path) - info.contents.IsDirectory = True - - @timeout_protect - @handle_fs_errors - def CreateDirectory(self, path, info): - path = normpath(path) - if self._is_pending_delete(path): - return -ERROR_ACCESS_DENIED - self.fs.makedir(path) - info.contents.IsDirectory = True - @timeout_protect @handle_fs_errors def Cleanup(self, path, info): - path = normpath(path) + path = self._dokanpath2pyfs(path) if info.contents.IsDirectory: if info.contents.DeleteOnClose: self.fs.removedir(path) @@ -497,13 +538,13 @@ class FSOperations(object): @timeout_protect @handle_fs_errors def ReadFile(self, path, buffer, nBytesToRead, nBytesRead, offset, info): - path = normpath(path) + path = self._dokanpath2pyfs(path) (file, _, lock) = self._get_file(info.contents.Context) lock.acquire() try: - errno = self._check_lock(path, offset, nBytesToRead, info) - if errno: - return errno + status = self._check_lock(path, offset, nBytesToRead, info) + if status: + return status # This may be called after Cleanup, meaning we # need to re-open the file. if file.closed: @@ -515,18 +556,19 @@ class FSOperations(object): nBytesRead[0] = len(data) finally: lock.release() + return STATUS_SUCCESS @timeout_protect @handle_fs_errors def WriteFile(self, path, buffer, nBytesToWrite, nBytesWritten, offset, info): - path = normpath(path) + path = self._dokanpath2pyfs(path) fh = info.contents.Context (file, _, lock) = self._get_file(fh) lock.acquire() try: - errno = self._check_lock(path, offset, nBytesToWrite, info) - if errno: - return errno + status = self._check_lock(path, offset, nBytesToWrite, info) + if status: + return status # This may be called after Cleanup, meaning we # need to re-open the file. if file.closed: @@ -550,22 +592,24 @@ class FSOperations(object): self._files_size_written[path][fh] = new_size_written finally: lock.release() + return STATUS_SUCCESS @timeout_protect @handle_fs_errors def FlushFileBuffers(self, path, info): - path = normpath(path) + path = self._dokanpath2pyfs(path) (file, _, lock) = self._get_file(info.contents.Context) lock.acquire() try: file.flush() finally: lock.release() + return STATUS_SUCCESS @timeout_protect @handle_fs_errors def GetFileInformation(self, path, buffer, info): - path = normpath(path) + path = self._dokanpath2pyfs(path) finfo = self.fs.getinfo(path) data = buffer.contents self._info2finddataw(path, finfo, data, info) @@ -579,22 +623,24 @@ class FSOperations(object): data.nFileSizeHigh = written_size >> 32 data.nFileSizeLow = written_size & 0xffffffff data.nNumberOfLinks = 1 + return STATUS_SUCCESS @timeout_protect @handle_fs_errors def FindFiles(self, path, fillFindData, info): - path = normpath(path) + path = self._dokanpath2pyfs(path) for (nm, finfo) in self.fs.listdirinfo(path): fpath = pathjoin(path, nm) if self._is_pending_delete(fpath): continue data = self._info2finddataw(fpath, finfo) fillFindData(ctypes.byref(data), info) + return STATUS_SUCCESS @timeout_protect @handle_fs_errors def FindFilesWithPattern(self, path, pattern, fillFindData, info): - path = normpath(path) + path = self._dokanpath2pyfs(path) for (nm, finfo) in self.fs.listdirinfo(path): fpath = pathjoin(path, nm) if self._is_pending_delete(fpath): @@ -603,17 +649,19 @@ class FSOperations(object): continue data = self._info2finddataw(fpath, finfo, None) fillFindData(ctypes.byref(data), info) + return STATUS_SUCCESS @timeout_protect @handle_fs_errors def SetFileAttributes(self, path, attrs, info): - path = normpath(path) + path = self._dokanpath2pyfs(path) # TODO: decode various file attributes + return STATUS_SUCCESS @timeout_protect @handle_fs_errors def SetFileTime(self, path, ctime, atime, mtime, info): - path = normpath(path) + path = self._dokanpath2pyfs(path) # setting ctime is not supported if atime is not None: try: @@ -630,28 +678,31 @@ class FSOperations(object): self.fs.settimes(path, atime, mtime) except UnsupportedError: pass + return STATUS_SUCCESS @timeout_protect @handle_fs_errors def DeleteFile(self, path, info): - path = normpath(path) + path = self._dokanpath2pyfs(path) if not self.fs.isfile(path): if not self.fs.exists(path): - raise ResourceNotFoundError(path) + return STATUS_ACCESS_DENIED else: - raise ResourceInvalidError(path) + return STATUS_OBJECT_NAME_NOT_FOUND self._pending_delete.add(path) # the actual delete takes place in self.CloseFile() + return STATUS_SUCCESS @timeout_protect @handle_fs_errors def DeleteDirectory(self, path, info): - path = normpath(path) + path = self._dokanpath2pyfs(path) for nm in self.fs.listdir(path): if not self._is_pending_delete(pathjoin(path, nm)): - raise DirectoryNotEmptyError(path) + return STATUS_DIRECTORY_NOT_EMPTY self._pending_delete.add(path) # the actual delete takes place in self.CloseFile() + return STATUS_SUCCESS @timeout_protect @handle_fs_errors @@ -665,17 +716,18 @@ class FSOperations(object): self._del_file(info.contents.Context) finally: lock.release() - src = normpath(src) - dst = normpath(dst) + src = self._dokanpath2pyfs(src) + dst = self._dokanpath2pyfs(dst) if info.contents.IsDirectory: self.fs.movedir(src, dst, overwrite=overwrite) else: self.fs.move(src, dst, overwrite=overwrite) + return STATUS_SUCCESS @timeout_protect @handle_fs_errors def SetEndOfFile(self, path, length, info): - path = normpath(path) + self._dokanpath2pyfs(path) (file, _, lock) = self._get_file(info.contents.Context) lock.acquire() try: @@ -687,20 +739,22 @@ class FSOperations(object): file.seek(min(pos, length)) finally: lock.release() + return STATUS_SUCCESS @handle_fs_errors - def GetDiskFreeSpaceEx(self, nBytesAvail, nBytesTotal, nBytesFree, info): + def GetDiskFreeSpace(self, nBytesAvail, nBytesTotal, nBytesFree, info): # This returns a stupidly large number if not info is available. # It's better to pretend an operation is possible and have it fail # than to pretend an operation will fail when it's actually possible. - large_amount = 100 * 1024*1024*1024 + large_amount = 100 * 1024 * 1024 * 1024 nBytesFree[0] = self.fs.getmeta("free_space", large_amount) nBytesTotal[0] = self.fs.getmeta("total_space", 2 * large_amount) nBytesAvail[0] = nBytesFree[0] + return STATUS_SUCCESS @handle_fs_errors def GetVolumeInformation(self, vnmBuf, vnmSz, sNum, maxLen, flags, fnmBuf, fnmSz, info): - nm = ctypes.create_unicode_buffer(self.volname[:vnmSz-1]) + nm = ctypes.create_unicode_buffer(self.volname[:vnmSz - 1]) sz = (len(nm.value) + 1) * ctypes.sizeof(ctypes.c_wchar) ctypes.memmove(vnmBuf, nm, sz) if sNum: @@ -708,10 +762,11 @@ class FSOperations(object): if maxLen: maxLen[0] = 255 if flags: - flags[0] = 0 - nm = ctypes.create_unicode_buffer(self.fsname[:fnmSz-1]) + flags[0] = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES | FILE_SUPPORTS_REMOTE_STORAGE | FILE_UNICODE_ON_DISK | FILE_PERSISTENT_ACLS; + nm = ctypes.create_unicode_buffer(self.fsname[:fnmSz - 1]) sz = (len(nm.value) + 1) * ctypes.sizeof(ctypes.c_wchar) ctypes.memmove(fnmBuf, nm, sz) + return STATUS_SUCCESS @timeout_protect @handle_fs_errors @@ -719,7 +774,7 @@ class FSOperations(object): # I think this is supposed to reserve space for the file # but *not* actually move the end-of-file marker. # No way to do that in pyfs. - return 0 + return STATUS_SUCCESS @timeout_protect @handle_fs_errors @@ -731,21 +786,20 @@ class FSOperations(object): except KeyError: locks = self._active_locks[path] = [] else: - errno = self._check_lock(path, offset, length, None, locks) - if errno: - return errno + status = self._check_lock(path, offset, length, None, locks) + if status: + return status locks.append((info.contents.Context, offset, end)) - return 0 + return STATUS_SUCCESS @timeout_protect @handle_fs_errors def UnlockFile(self, path, offset, length, info): - end = offset + length with self._files_lock: try: locks = self._active_locks[path] except KeyError: - return -ERROR_NOT_LOCKED + return STATUS_NOT_LOCKED todel = [] for i, (lh, lstart, lend) in enumerate(locks): if info.contents.Context == lh: @@ -753,14 +807,45 @@ class FSOperations(object): if lend == offset + length: todel.append(i) if not todel: - return -ERROR_NOT_LOCKED + return STATUS_NOT_LOCKED for i in reversed(todel): del locks[i] - return 0 + return STATUS_SUCCESS + + @handle_fs_errors + def GetFileSecurity(self, path, securityinformation, securitydescriptor, securitydescriptorlength, neededlength, info): + securitydescriptor = ctypes.cast(securitydescriptor, libdokan.PSECURITY_DESCRIPTOR) + path = self._dokanpath2pyfs(path) + if self.fs.isdir(path): + res = libdokan.GetFileSecurity( + os.path.expanduser('~'), + ctypes.cast(securityinformation, libdokan.PSECURITY_INFORMATION)[0], + securitydescriptor, + securitydescriptorlength, + neededlength, + ) + return STATUS_SUCCESS if res else STATUS_BUFFER_OVERFLOW + return STATUS_NOT_IMPLEMENTED + + @handle_fs_errors + def SetFileSecurity(self, path, securityinformation, securitydescriptor, securitydescriptorlength, info): + return STATUS_NOT_IMPLEMENTED @handle_fs_errors - def Unmount(self, info): - pass + def Mounted(self, info): + return STATUS_SUCCESS + + @handle_fs_errors + def Unmounted(self, info): + return STATUS_SUCCESS + + @handle_fs_errors + def FindStreams(self, path, callback, info): + return STATUS_NOT_IMPLEMENTED + + def _dokanpath2pyfs(self, path): + path = path.replace('\\', '/') + return normpath(path) def _info2attrmask(self, path, info, hinfo=None): """Convert a file/directory info dict to a win32 file attribute mask.""" @@ -783,16 +868,16 @@ class FSOperations(object): attrs |= FILE_ATTRIBUTE_NORMAL return attrs - def _info2finddataw(self,path,info,data=None,hinfo=None): + def _info2finddataw(self, path, info, data=None, hinfo=None): """Convert a file/directory info dict into a WIN32_FIND_DATAW struct.""" if data is None: data = libdokan.WIN32_FIND_DATAW() - data.dwFileAttributes = self._info2attrmask(path,info,hinfo) - data.ftCreationTime = _datetime2filetime(info.get("created_time",None)) - data.ftLastAccessTime = _datetime2filetime(info.get("accessed_time",None)) - data.ftLastWriteTime = _datetime2filetime(info.get("modified_time",None)) - data.nFileSizeHigh = info.get("size",0) >> 32 - data.nFileSizeLow = info.get("size",0) & 0xffffffff + data.dwFileAttributes = self._info2attrmask(path, info, hinfo) + data.ftCreationTime = _datetime2filetime(info.get("created_time", None)) + data.ftLastAccessTime = _datetime2filetime(info.get("accessed_time", None)) + data.ftLastWriteTime = _datetime2filetime(info.get("modified_time", None)) + data.nFileSizeHigh = info.get("size", 0) >> 32 + data.nFileSizeLow = info.get("size", 0) & 0xffffffff data.cFileName = basename(path) data.cAlternateFileName = "" return data @@ -804,20 +889,22 @@ def _datetime2timestamp(dtime): t += dtime.microsecond / 1000000.0 return t -DATETIME_LOCAL_TO_UTC = _datetime2timestamp(datetime.datetime.utcnow()) - _datetime2timestamp(datetime.datetime.now()) def _timestamp2datetime(tstamp): """Convert a unix timestamp to a datetime object.""" return datetime.datetime.fromtimestamp(tstamp) + def _timestamp2filetime(tstamp): f = FILETIME_UNIX_EPOCH + int(tstamp * 10000000) - return libdokan.FILETIME(f & 0xffffffff,f >> 32) + return libdokan.FILETIME(f & 0xffffffff, f >> 32) + def _filetime2timestamp(ftime): f = ftime.dwLowDateTime | (ftime.dwHighDateTime << 32) return (f - FILETIME_UNIX_EPOCH) / 10000000.0 + def _filetime2datetime(ftime): """Convert a FILETIME struct info datetime.datetime object.""" if ftime is None: @@ -826,43 +913,37 @@ def _filetime2datetime(ftime): return DATETIME_ZERO return _timestamp2datetime(_filetime2timestamp(ftime)) + def _datetime2filetime(dtime): """Convert a FILETIME struct info datetime.datetime object.""" if dtime is None: - return libdokan.FILETIME(0,0) + return libdokan.FILETIME(0, 0) if dtime == DATETIME_ZERO: - return libdokan.FILETIME(0,0) + return libdokan.FILETIME(0, 0) return _timestamp2filetime(_datetime2timestamp(dtime)) def _errno2syserrcode(eno): """Convert an errno into a win32 system error code.""" if eno == errno.EEXIST: - return ERROR_FILE_EXISTS + return STATUS_OBJECT_NAME_COLLISION if eno == errno.ENOTEMPTY: - return ERROR_DIR_NOT_EMPTY + return STATUS_DIRECTORY_NOT_EMPTY if eno == errno.ENOSYS: - return ERROR_NOT_SUPPORTED + return STATUS_NOT_SUPPORTED if eno == errno.EACCES: - return ERROR_ACCESS_DENIED + return STATUS_ACCESS_DENIED return eno -def _normalise_drive_string(drive): - """Normalise drive string to a single letter.""" - if not drive: - raise ValueError("invalid drive letter: %r" % (drive,)) - if len(drive) > 3: - raise ValueError("invalid drive letter: %r" % (drive,)) - if not drive[0].isalpha(): - raise ValueError("invalid drive letter: %r" % (drive,)) - if not ":\\".startswith(drive[1:]): - raise ValueError("invalid drive letter: %r" % (drive,)) - return drive[0].upper() +def _check_path_string(path): # TODO Probably os.path has a better check for this... + """Check path string.""" + if not path or not path[0].isalpha() or not path[1:3] == ':\\': + raise ValueError("invalid path: %r" % (path,)) -def mount(fs, drive, foreground=False, ready_callback=None, unmount_callback=None, **kwds): - """Mount the given FS at the given drive letter, using Dokan. +def mount(fs, path, foreground=False, ready_callback=None, unmount_callback=None, **kwds): + """Mount the given FS at the given path letter, using Dokan. By default, this function spawns a new background process to manage the Dokan event loop. The return value in this case is an instance of the @@ -885,21 +966,23 @@ def mount(fs, drive, foreground=False, ready_callback=None, unmount_callback=Non """ if libdokan is None: raise OSError("the dokan library is not available") - drive = _normalise_drive_string(drive) + _check_path_string(path) # This function captures the logic of checking whether the Dokan mount # is up and running. Unfortunately I can't find a way to get this - # via a callback in the Dokan API. Instead we just check for the drive + # via a callback in the Dokan API. Instead we just check for the path # in a loop, polling the mount proc to make sure it hasn't died. + def check_alive(mp): - if mp and mp.poll() != None: + if mp and mp.poll() is not None: raise OSError("dokan mount process exited prematurely") + def check_ready(mp=None): if ready_callback is not False: check_alive(mp) for _ in xrange(100): try: - os.stat(drive+":\\") - except EnvironmentError, e: + os.stat(path) + except EnvironmentError: check_alive(mp) time.sleep(0.05) else: @@ -914,17 +997,17 @@ def mount(fs, drive, foreground=False, ready_callback=None, unmount_callback=Non # Running the the foreground is the final endpoint for the mount # operation, it's where we call DokanMain(). if foreground: - numthreads = kwds.pop("numthreads",0) - flags = kwds.pop("flags",0) - FSOperationsClass = kwds.pop("FSOperationsClass",FSOperations) - opts = libdokan.DOKAN_OPTIONS(drive[:1], numthreads, flags) + numthreads = kwds.pop("numthreads", 0) + flags = kwds.pop("flags", 0) + FSOperationsClass = kwds.pop("FSOperationsClass", FSOperations) + opts = libdokan.DOKAN_OPTIONS(libdokan.DOKAN_MINIMUM_COMPATIBLE_VERSION, numthreads, flags, 0, path, "", 2000, 512, 512) ops = FSOperationsClass(fs, **kwds) if ready_callback: check_thread = threading.Thread(target=check_ready) check_thread.daemon = True check_thread.start() opstruct = ops.get_ops_struct() - res = libdokan.DokanMain(ctypes.byref(opts),ctypes.byref(opstruct)) + res = libdokan.DokanMain(ctypes.byref(opts), ctypes.byref(opstruct)) if res != DOKAN_SUCCESS: raise OSError("Dokan failed with error: %d" % (res,)) if unmount_callback: @@ -932,10 +1015,11 @@ def mount(fs, drive, foreground=False, ready_callback=None, unmount_callback=Non # Running the background, spawn a subprocess and wait for it # to be ready before returning. else: - mp = MountProcess(fs, drive, kwds) + mp = MountProcess(fs, path, kwds) check_ready(mp) if unmount_callback: orig_unmount = mp.unmount + def new_unmount(): orig_unmount() unmount_callback() @@ -943,16 +1027,16 @@ def mount(fs, drive, foreground=False, ready_callback=None, unmount_callback=Non return mp -def unmount(drive): - """Unmount the given drive. +def unmount(path): + """Unmount the given path. - This function unmounts the dokan drive mounted at the given drive letter. + This function unmounts the dokan path mounted at the given path letter. It works but may leave dangling processes; its better to use the "unmount" method on the MountProcess class if you have one. """ - drive = _normalise_drive_string(drive) - if not libdokan.DokanUnmount(drive): - raise OSError("filesystem could not be unmounted: %s" % (drive,)) + _check_path_string(path) + if not libdokan.DokanRemoveMountPoint(path): + raise OSError("filesystem could not be unmounted: %s" % (path,)) class MountProcess(subprocess.Popen): @@ -960,14 +1044,14 @@ class MountProcess(subprocess.Popen): This is a subclass of subprocess.Popen, designed for easy management of a Dokan mount in a background process. Rather than specifying the command - to execute, pass in the FS object to be mounted, the target drive letter + to execute, pass in the FS object to be mounted, the target path and a dictionary of options for the Dokan process. In order to be passed successfully to the new process, the FS object must be pickleable. Since win32 has no fork() this restriction is not likely to be lifted (see also the "multiprocessing" module) - This class has an extra attribute 'drive' giving the drive of the mounted + This class has an extra attribute 'path' giving the path of the mounted filesystem, and an extra method 'unmount' that will cleanly unmount it and terminate the process. """ @@ -980,47 +1064,47 @@ class MountProcess(subprocess.Popen): unmount_timeout = 5 - def __init__(self, fs, drive, dokan_opts={}, nowait=False, **kwds): + def __init__(self, fs, path, dokan_opts={}, nowait=False, **kwds): if libdokan is None: raise OSError("the dokan library is not available") - self.drive = _normalise_drive_string(drive) - self.path = self.drive + ":\\" - cmd = "import cPickle; " + _check_path_string(path) + self.path = path + cmd = "try: import cPickle;\nexcept ImportError: import pickle as cPickle;\n" cmd = cmd + "data = cPickle.loads(%s); " cmd = cmd + "from fs.expose.dokan import MountProcess; " cmd = cmd + "MountProcess._do_mount(data)" - cmd = cmd % (repr(cPickle.dumps((fs,drive,dokan_opts,nowait),-1)),) - cmd = [sys.executable,"-c",cmd] - super(MountProcess,self).__init__(cmd,**kwds) + cmd = cmd % (repr(cPickle.dumps((fs, path, dokan_opts, nowait), -1)),) + cmd = [sys.executable, "-c", cmd] + super(MountProcess, self).__init__(cmd, **kwds) def unmount(self): """Cleanly unmount the Dokan filesystem, terminating this subprocess.""" - if not libdokan.DokanUnmount(self.drive): - raise OSError("the filesystem could not be unmounted: %s" %(self.drive,)) + if not libdokan.DokanRemoveMountPoint(self.path): + raise OSError("the filesystem could not be unmounted: %s" %(self.path,)) self.terminate() if not hasattr(subprocess.Popen, "terminate"): def terminate(self): """Gracefully terminate the subprocess.""" - kernel32.TerminateProcess(int(self._handle),-1) + kernel32.TerminateProcess(int(self._handle), -1) if not hasattr(subprocess.Popen, "kill"): def kill(self): """Forcibly terminate the subprocess.""" - kernel32.TerminateProcess(int(self._handle),-1) + kernel32.TerminateProcess(int(self._handle), -1) @staticmethod def _do_mount(data): """Perform the specified mount.""" - (fs,drive,opts,nowait) = data + (fs, path, opts, nowait) = data opts["foreground"] = True + def unmount_callback(): fs.close() opts["unmount_callback"] = unmount_callback if nowait: opts["ready_callback"] = False - mount(fs,drive,**opts) - + mount(fs, path, **opts) class Win32SafetyFS(WrapFS): @@ -1034,21 +1118,21 @@ class Win32SafetyFS(WrapFS): """ - def __init__(self,wrapped_fs,allow_autorun=False): + def __init__(self, wrapped_fs, allow_autorun=False): self.allow_autorun = allow_autorun - super(Win32SafetyFS,self).__init__(wrapped_fs) + super(Win32SafetyFS, self).__init__(wrapped_fs) - def _encode(self,path): + def _encode(self, path): path = relpath(normpath(path)) - path = path.replace(":","__colon__") + path = path.replace(":", "__colon__") if not self.allow_autorun: if path.lower().startswith("_autorun."): path = path[1:] return path - def _decode(self,path): + def _decode(self, path): path = relpath(normpath(path)) - path = path.replace("__colon__",":") + path = path.replace("__colon__", ":") if not self.allow_autorun: if path.lower().startswith("autorun."): path = "_" + path @@ -1056,7 +1140,7 @@ class Win32SafetyFS(WrapFS): if __name__ == "__main__": - import os, os.path + import os.path import tempfile from fs.osfs import OSFS from fs.memoryfs import MemoryFS @@ -1066,11 +1150,9 @@ if __name__ == "__main__": try: fs = OSFS(path) #fs = MemoryFS() - fs.setcontents("test1.txt",b("test one")) - flags = DOKAN_OPTION_DEBUG|DOKAN_OPTION_STDERR|DOKAN_OPTION_REMOVABLE - mount(fs, "Q", foreground=True, numthreads=1, flags=flags) + fs.setcontents("test1.txt", b("test one")) + flags = DOKAN_OPTION_DEBUG | DOKAN_OPTION_STDERR | DOKAN_OPTION_REMOVABLE + mount(fs, "Q:\\", foreground=True, numthreads=1, flags=flags) fs.close() finally: rmtree(path) - - diff --git a/fs/expose/dokan/libdokan.py b/fs/expose/dokan/libdokan.py index 5391d2c..b848316 100644 --- a/fs/expose/dokan/libdokan.py +++ b/fs/expose/dokan/libdokan.py @@ -1,4 +1,5 @@ # Copyright (c) 2009-2010, Cloud Matrix Pty. Ltd. +# Copyright (c) 2016-2016, Adrien J. . # All rights reserved; available under the terms of the MIT License. """ @@ -9,41 +10,49 @@ from ctypes import * try: - DokanMain = windll.Dokan.DokanMain - DokanVersion = windll.Dokan.DokanVersion + DokanMain = windll.Dokan1.DokanMain + DokanVersion = windll.Dokan1.DokanVersion except AttributeError: raise ImportError("Dokan DLL not found") from ctypes.wintypes import * ULONG64 = c_ulonglong -ULONGLONG = c_ulonglong -PULONGLONG = POINTER(ULONGLONG) +PULONGLONG = POINTER(c_ulonglong) +PVOID = c_void_p +PULONG = POINTER(c_ulong) UCHAR = c_ubyte -LPDWORD = POINTER(DWORD) +LPDWORD = POINTER(c_ulong) LONGLONG = c_longlong - -try: - USHORT = USHORT -except NameError: - # Not available in older python versions - USHORT = c_ushort +NTSTATUS = c_long +USHORT = c_ushort +WCHAR = c_wchar DokanVersion.restype = ULONG DokanVersion.argtypes = () -if DokanVersion() < 392: # ths is release 0.5.3 +DOKAN_MINIMUM_COMPATIBLE_VERSION = 100 # this is release 1.0.0 +if DokanVersion() < DOKAN_MINIMUM_COMPATIBLE_VERSION: raise ImportError("Dokan DLL is too old") MAX_PATH = 260 +class SECURITY_DESCRIPTOR(ctypes.Structure): pass + +PSECURITY_DESCRIPTOR = ctypes.POINTER(SECURITY_DESCRIPTOR) +PPSECURITY_DESCRIPTOR = ctypes.POINTER(PSECURITY_DESCRIPTOR) + +SECURITY_INFORMATION = DWORD +PSECURITY_INFORMATION = ctypes.POINTER(SECURITY_INFORMATION) + class FILETIME(Structure): _fields_ = [ ("dwLowDateTime", DWORD), ("dwHighDateTime", DWORD), ] + class WIN32_FIND_DATAW(Structure): _fields_ = [ ("dwFileAttributes", DWORD), @@ -58,6 +67,7 @@ class WIN32_FIND_DATAW(Structure): ("cAlternateFileName", WCHAR * 14), ] + class BY_HANDLE_FILE_INFORMATION(Structure): _fields_ = [ ('dwFileAttributes', DWORD), @@ -72,12 +82,18 @@ class BY_HANDLE_FILE_INFORMATION(Structure): ('nFileIndexLow', DWORD), ] + class DOKAN_OPTIONS(Structure): _fields_ = [ - ("DriveLetter", WCHAR), - ("ThreadCount", USHORT), - ("Options", ULONG), - ("GlobalContext", ULONG64), + ("Version", USHORT), + ("ThreadCount", USHORT), + ("Options", ULONG), + ("GlobalContext", ULONG64), + ("MountPoint", LPCWSTR), + ("UNCName", LPCWSTR), + ("Timeout", ULONG), + ("AllocationUnitSize", ULONG), + ("SectorSize", ULONG), ] @@ -93,137 +109,151 @@ class DOKAN_FILE_INFO(Structure): ("SyncronousIo", UCHAR), ("Nocache", UCHAR), ("WriteToEndOfFile", UCHAR), -] + ] PDOKAN_FILE_INFO = POINTER(DOKAN_FILE_INFO) -PFillFindData = WINFUNCTYPE(c_int,POINTER(WIN32_FIND_DATAW),PDOKAN_FILE_INFO) +PFillFindData = WINFUNCTYPE(c_int, POINTER(WIN32_FIND_DATAW), PDOKAN_FILE_INFO) + class DOKAN_OPERATIONS(Structure): _fields_ = [ - ("CreateFile", CFUNCTYPE(c_int, - LPCWSTR, # FileName - DWORD, # DesiredAccess - DWORD, # ShareMode - DWORD, # CreationDisposition - DWORD, # FlagsAndAttributes - PDOKAN_FILE_INFO)), - ("OpenDirectory", CFUNCTYPE(c_int, - LPCWSTR, # FileName - PDOKAN_FILE_INFO)), - ("CreateDirectory", CFUNCTYPE(c_int, - LPCWSTR, # FileName - PDOKAN_FILE_INFO)), - ("Cleanup", CFUNCTYPE(c_int, - LPCWSTR, # FileName - PDOKAN_FILE_INFO)), - ("CloseFile", CFUNCTYPE(c_int, - LPCWSTR, # FileName - PDOKAN_FILE_INFO)), - ("ReadFile", CFUNCTYPE(c_int, - LPCWSTR, # FileName - POINTER(c_char), # Buffer - DWORD, # NumberOfBytesToRead - LPDWORD, # NumberOfBytesRead - LONGLONG, # Offset - PDOKAN_FILE_INFO)), - ("WriteFile", CFUNCTYPE(c_int, - LPCWSTR, # FileName - POINTER(c_char), # Buffer - DWORD, # NumberOfBytesToWrite - LPDWORD, # NumberOfBytesWritten - LONGLONG, # Offset - PDOKAN_FILE_INFO)), - ("FlushFileBuffers", CFUNCTYPE(c_int, - LPCWSTR, # FileName - PDOKAN_FILE_INFO)), - ("GetFileInformation", CFUNCTYPE(c_int, - LPCWSTR, # FileName - POINTER(BY_HANDLE_FILE_INFORMATION), # Buffer - PDOKAN_FILE_INFO)), - ("FindFiles", CFUNCTYPE(c_int, - LPCWSTR, # PathName - PFillFindData, # call this function with PWIN32_FIND_DATAW - PDOKAN_FILE_INFO)), - ("FindFilesWithPattern", CFUNCTYPE(c_int, - LPCWSTR, # PathName - LPCWSTR, # SearchPattern - PFillFindData, #call this function with PWIN32_FIND_DATAW - PDOKAN_FILE_INFO)), - ("SetFileAttributes", CFUNCTYPE(c_int, - LPCWSTR, # FileName - DWORD, # FileAttributes - PDOKAN_FILE_INFO)), - ("SetFileTime", CFUNCTYPE(c_int, - LPCWSTR, # FileName - POINTER(FILETIME), # CreationTime - POINTER(FILETIME), # LastAccessTime - POINTER(FILETIME), # LastWriteTime - PDOKAN_FILE_INFO)), - ("DeleteFile", CFUNCTYPE(c_int, - LPCWSTR, # FileName - PDOKAN_FILE_INFO)), - ("DeleteDirectory", CFUNCTYPE(c_int, - LPCWSTR, # FileName - PDOKAN_FILE_INFO)), - ("MoveFile", CFUNCTYPE(c_int, - LPCWSTR, # ExistingFileName - LPCWSTR, # NewFileName - BOOL, # ReplaceExisiting - PDOKAN_FILE_INFO)), - ("SetEndOfFile", CFUNCTYPE(c_int, - LPCWSTR, # FileName - LONGLONG, # Length - PDOKAN_FILE_INFO)), - ("SetAllocationSize", CFUNCTYPE(c_int, - LPCWSTR, # FileName - LONGLONG, # Length - PDOKAN_FILE_INFO)), - ("LockFile", CFUNCTYPE(c_int, - LPCWSTR, # FileName - LONGLONG, # ByteOffset - LONGLONG, # Length - PDOKAN_FILE_INFO)), - ("UnlockFile", CFUNCTYPE(c_int, - LPCWSTR, # FileName - LONGLONG, # ByteOffset - LONGLONG, # Length - PDOKAN_FILE_INFO)), - ("GetDiskFreeSpaceEx", CFUNCTYPE(c_int, - PULONGLONG, # FreeBytesAvailable - PULONGLONG, # TotalNumberOfBytes - PULONGLONG, # TotalNumberOfFreeBytes - PDOKAN_FILE_INFO)), - ("GetVolumeInformation", CFUNCTYPE(c_int, - POINTER(c_wchar), # VolumeNameBuffer - DWORD, # VolumeNameSize in num of chars - LPDWORD, # VolumeSerialNumber - LPDWORD, # MaximumComponentLength in num of chars - LPDWORD, # FileSystemFlags - POINTER(c_wchar), # FileSystemNameBuffer - DWORD, # FileSystemNameSize in num of chars - PDOKAN_FILE_INFO)), - ("Unmount", CFUNCTYPE(c_int, - PDOKAN_FILE_INFO)), + ("ZwCreateFile", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + PVOID, # SecurityContext, see + # https://msdn.microsoft.com/en-us/library/windows/hardware/ff550613(v=vs.85).aspx + DWORD, # DesiredAccess + ULONG, # FileAttributes + ULONG, # ShareAccess + ULONG, # CreateDisposition + ULONG, # CreateOptions + PDOKAN_FILE_INFO)), + ("Cleanup", WINFUNCTYPE(None, + LPCWSTR, # FileName + PDOKAN_FILE_INFO)), + ("CloseFile", WINFUNCTYPE(None, + LPCWSTR, # FileName + PDOKAN_FILE_INFO)), + ("ReadFile", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + LPVOID, # Buffer + DWORD, # NumberOfBytesToRead + LPDWORD, # NumberOfBytesRead + LONGLONG, # Offset + PDOKAN_FILE_INFO)), + ("WriteFile", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + LPCVOID, # Buffer + DWORD, # NumberOfBytesToWrite + LPDWORD, # NumberOfBytesWritten + LONGLONG, # Offset + PDOKAN_FILE_INFO)), + ("FlushFileBuffers", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + PDOKAN_FILE_INFO)), + ("GetFileInformation", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + POINTER(BY_HANDLE_FILE_INFORMATION), # Buffer + PDOKAN_FILE_INFO)), + ("FindFiles", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # PathName + PFillFindData, # call this function with PWIN32_FIND_DATAW + PDOKAN_FILE_INFO)), + ("FindFilesWithPattern", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # PathName + LPCWSTR, # SearchPattern + PFillFindData, #call this function with PWIN32_FIND_DATAW + PDOKAN_FILE_INFO)), + ("SetFileAttributes", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + DWORD, # FileAttributes + PDOKAN_FILE_INFO)), + ("SetFileTime", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + POINTER(FILETIME), # CreationTime + POINTER(FILETIME), # LastAccessTime + POINTER(FILETIME), # LastWriteTime + PDOKAN_FILE_INFO)), + ("DeleteFile", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + PDOKAN_FILE_INFO)), + ("DeleteDirectory", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + PDOKAN_FILE_INFO)), + ("MoveFile", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # ExistingFileName + LPCWSTR, # NewFileName + BOOL, # ReplaceExisiting + PDOKAN_FILE_INFO)), + ("SetEndOfFile", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + LONGLONG, # Length + PDOKAN_FILE_INFO)), + ("SetAllocationSize", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + LONGLONG, # Length + PDOKAN_FILE_INFO)), + ("LockFile", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + LONGLONG, # ByteOffset + LONGLONG, # Length + PDOKAN_FILE_INFO)), + ("UnlockFile", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + LONGLONG, # ByteOffset + LONGLONG, # Length + PDOKAN_FILE_INFO)), + ("GetDiskFreeSpace", WINFUNCTYPE(NTSTATUS, + PULONGLONG, # FreeBytesAvailable + PULONGLONG, # TotalNumberOfBytes + PULONGLONG, # TotalNumberOfFreeBytes + PDOKAN_FILE_INFO)), + ("GetVolumeInformation", WINFUNCTYPE(NTSTATUS, + PVOID, # VolumeNameBuffer + DWORD, # VolumeNameSize in num of chars + LPDWORD, # VolumeSerialNumber + LPDWORD, # MaximumComponentLength in num of chars + LPDWORD, # FileSystemFlags + PVOID, # FileSystemNameBuffer + DWORD, # FileSystemNameSize in num of chars + PDOKAN_FILE_INFO)), + ("Mounted", WINFUNCTYPE(NTSTATUS, + PDOKAN_FILE_INFO)), + ("Unmounted", WINFUNCTYPE(NTSTATUS, + DOKAN_FILE_INFO)), + ("GetFileSecurity", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + PULONG, # A pointer to SECURITY_INFORMATION value being requested + PVOID, # A pointer to SECURITY_DESCRIPTOR buffer to be filled + ULONG, # Length of Security descriptor buffer + PULONG, # Length Needed + PDOKAN_FILE_INFO)), + ("SetFileSecurity", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + PVOID, # A pointer to SECURITY_INFORMATION value being + PVOID, # A pointer to SECURITY_DESCRIPTOR buffer + ULONG, # Length of Security descriptor buffer + PDOKAN_FILE_INFO)), + ("FindStreams", WINFUNCTYPE(NTSTATUS, + LPCWSTR, # FileName + PVOID, # call this function with PWIN32_FIND_STREAM_DATA + PDOKAN_FILE_INFO)) ] - DokanMain.restype = c_int DokanMain.argtypes = ( POINTER(DOKAN_OPTIONS), POINTER(DOKAN_OPERATIONS), ) - - -DokanUnmount = windll.Dokan.DokanUnmount -DokanUnmount.restype = BOOL -DokanUnmount.argtypes = ( - WCHAR, +DokanRemoveMountPoint = windll.Dokan1.DokanRemoveMountPoint +DokanRemoveMountPoint.restype = BOOL +DokanRemoveMountPoint.argtypes = ( + LPCWSTR, ) -DokanIsNameInExpression = windll.Dokan.DokanIsNameInExpression +DokanIsNameInExpression = windll.Dokan1.DokanIsNameInExpression DokanIsNameInExpression.restype = BOOL DokanIsNameInExpression.argtypes = ( LPCWSTR, # pattern @@ -231,16 +261,24 @@ DokanIsNameInExpression.argtypes = ( BOOL, # ignore case ) -DokanDriverVersion = windll.Dokan.DokanDriverVersion +DokanDriverVersion = windll.Dokan1.DokanDriverVersion DokanDriverVersion.restype = ULONG DokanDriverVersion.argtypes = ( ) -DokanResetTimeout = windll.Dokan.DokanResetTimeout +DokanResetTimeout = windll.Dokan1.DokanResetTimeout DokanResetTimeout.restype = BOOL DokanResetTimeout.argtypes = ( ULONG, #timeout PDOKAN_FILE_INFO, # file info pointer ) - +GetFileSecurity = ctypes.windll.advapi32.GetFileSecurityW +GetFileSecurity.restype = BOOL +GetFileSecurity.argtypes = ( + LPWSTR, # _In_ LPCTSTR lpFileName, + SECURITY_INFORMATION, # _In_ SECURITY_INFORMATION RequestedInformation, + PSECURITY_DESCRIPTOR, # _Out_opt_ PSECURITY_DESCRIPTOR pSecurityDescriptor, + DWORD, # _In_ DWORD nLength, + LPDWORD, # _Out_ LPDWORD lpnLengthNeeded +) -- cgit v1.2.1 From 993ba098865c9025cf2335b72881e593b12249dd Mon Sep 17 00:00:00 2001 From: Adrien JUND Date: Thu, 20 Oct 2016 16:10:11 +0200 Subject: Change "path letter" -> "path" Change import cPickle --- fs/expose/dokan/__init__.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fs/expose/dokan/__init__.py b/fs/expose/dokan/__init__.py index bfc5294..d0e2a8f 100644 --- a/fs/expose/dokan/__init__.py +++ b/fs/expose/dokan/__init__.py @@ -71,9 +71,9 @@ import time import stat as statinfo import subprocess try: - import cPickle + import pickle as pickle except ImportError: - import pickle as cPickle + pass import datetime import ctypes from collections import deque @@ -943,7 +943,7 @@ def _check_path_string(path): # TODO Probably os.path has a better check for th def mount(fs, path, foreground=False, ready_callback=None, unmount_callback=None, **kwds): - """Mount the given FS at the given path letter, using Dokan. + """Mount the given FS at the given path, using Dokan. By default, this function spawns a new background process to manage the Dokan event loop. The return value in this case is an instance of the @@ -1030,7 +1030,7 @@ def mount(fs, path, foreground=False, ready_callback=None, unmount_callback=None def unmount(path): """Unmount the given path. - This function unmounts the dokan path mounted at the given path letter. + This function unmounts the dokan path mounted at the given path. It works but may leave dangling processes; its better to use the "unmount" method on the MountProcess class if you have one. """ @@ -1069,11 +1069,11 @@ class MountProcess(subprocess.Popen): raise OSError("the dokan library is not available") _check_path_string(path) self.path = path - cmd = "try: import cPickle;\nexcept ImportError: import pickle as cPickle;\n" - cmd = cmd + "data = cPickle.loads(%s); " + cmd = "try: import pickle;\nexcept ImportError: import pickle as pickle;\n" + cmd = cmd + "data = pickle.loads(%s); " cmd = cmd + "from fs.expose.dokan import MountProcess; " cmd = cmd + "MountProcess._do_mount(data)" - cmd = cmd % (repr(cPickle.dumps((fs, path, dokan_opts, nowait), -1)),) + cmd = cmd % (repr(pickle.dumps((fs, path, dokan_opts, nowait), -1)),) cmd = [sys.executable, "-c", cmd] super(MountProcess, self).__init__(cmd, **kwds) -- cgit v1.2.1 From e2bf5a45ae5e5557182302d52ff282bb0b62972b Mon Sep 17 00:00:00 2001 From: Adrien JUND Date: Thu, 20 Oct 2016 16:55:50 +0200 Subject: Fix import pickle --- fs/expose/dokan/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/expose/dokan/__init__.py b/fs/expose/dokan/__init__.py index d0e2a8f..52052fb 100644 --- a/fs/expose/dokan/__init__.py +++ b/fs/expose/dokan/__init__.py @@ -71,9 +71,9 @@ import time import stat as statinfo import subprocess try: - import pickle as pickle + import cPickle as pickle except ImportError: - pass + import pickle import datetime import ctypes from collections import deque @@ -1069,7 +1069,7 @@ class MountProcess(subprocess.Popen): raise OSError("the dokan library is not available") _check_path_string(path) self.path = path - cmd = "try: import pickle;\nexcept ImportError: import pickle as pickle;\n" + cmd = "try: import cPickle as pickle; except ImportError: import pickle;\n" cmd = cmd + "data = pickle.loads(%s); " cmd = cmd + "from fs.expose.dokan import MountProcess; " cmd = cmd + "MountProcess._do_mount(data)" -- cgit v1.2.1 From 6bcc9d032a6671dd5687fb8a7dc24cc6c73410b3 Mon Sep 17 00:00:00 2001 From: Adrien JUND Date: Thu, 20 Oct 2016 17:34:41 +0200 Subject: fix import --- fs/expose/dokan/__init__.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/expose/dokan/__init__.py b/fs/expose/dokan/__init__.py index 52052fb..13de6a0 100644 --- a/fs/expose/dokan/__init__.py +++ b/fs/expose/dokan/__init__.py @@ -33,7 +33,7 @@ can be controlled through the returned subprocess.Popen object. To avoid spawning a new process, set the 'foreground' option:: >>> # This will block until the filesystem is unmounted - >>> dokan.mount(fs, "Q", foreground=True) + >>> dokan.mount(fs, "Q:\\", foreground=True) Any additional options for the Dokan process can be passed as keyword arguments to the 'mount' function. @@ -77,6 +77,7 @@ except ImportError: import datetime import ctypes from collections import deque +from six.moves import range from fs.base import threading from fs.errors import * @@ -100,9 +101,6 @@ else: import logging logger = logging.getLogger("fs.expose.dokan") -if six.PY3: - xrange = range - # Options controlling the behavior of the Dokan filesystem # Ouput debug message -- cgit v1.2.1 From 39e65036040fa31546ea0877e213d62821177d8b Mon Sep 17 00:00:00 2001 From: Adrien JUND Date: Thu, 20 Oct 2016 17:52:28 +0200 Subject: Change xrange to range --- fs/expose/dokan/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/expose/dokan/__init__.py b/fs/expose/dokan/__init__.py index 13de6a0..931b984 100644 --- a/fs/expose/dokan/__init__.py +++ b/fs/expose/dokan/__init__.py @@ -977,7 +977,7 @@ def mount(fs, path, foreground=False, ready_callback=None, unmount_callback=None def check_ready(mp=None): if ready_callback is not False: check_alive(mp) - for _ in xrange(100): + for _ in range(100): try: os.stat(path) except EnvironmentError: -- cgit v1.2.1 From 1a43556d85251b7caac8a54aa9c24f04adb58dee Mon Sep 17 00:00:00 2001 From: Liryna Date: Thu, 20 Oct 2016 20:52:16 +0200 Subject: Add GetFileSecurity path variable Fix libdokan import --- fs/expose/dokan/__init__.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/expose/dokan/__init__.py b/fs/expose/dokan/__init__.py index 931b984..5e4643f 100644 --- a/fs/expose/dokan/__init__.py +++ b/fs/expose/dokan/__init__.py @@ -61,7 +61,7 @@ systems with Dokan installed. # Copyright (c) 2016-2016, Adrien J. . # All rights reserved; available under the terms of the MIT License. -from __future__ import with_statement +from __future__ import with_statement, absolute_import import six import sys @@ -86,9 +86,7 @@ from fs.local_functools import wraps from fs.wrapfs import WrapFS try: - if six.PY2: import libdokan - elif six.PY3: from . import libdokan - else: raise + from . import libdokan except (NotImplementedError, EnvironmentError, ImportError, NameError,): is_available = False sys.modules.pop("fs.expose.dokan.libdokan", None) @@ -311,13 +309,14 @@ MIN_FH = 100 class FSOperations(object): """Object delegating all DOKAN_OPERATIONS pointers to an FS object.""" - def __init__(self, fs, fsname="NTFS", volname="Dokan Volume"): + def __init__(self, fs, fsname="NTFS", volname="Dokan Volume", securityfolder=os.path.expanduser('~')): if libdokan is None: msg = 'dokan library (http://dokan-dev.github.io/) is not available' raise OSError(msg) self.fs = fs self.fsname = fsname self.volname = volname + self.securityfolder = securityfolder self._files_by_handle = {} self._files_lock = threading.Lock() self._next_handle = MIN_FH @@ -816,7 +815,7 @@ class FSOperations(object): path = self._dokanpath2pyfs(path) if self.fs.isdir(path): res = libdokan.GetFileSecurity( - os.path.expanduser('~'), + self.securityfolder, ctypes.cast(securityinformation, libdokan.PSECURITY_INFORMATION)[0], securitydescriptor, securitydescriptorlength, -- cgit v1.2.1 From 8925c1c5a10afc13b5c603a4d3c4c08435a4d1e8 Mon Sep 17 00:00:00 2001 From: Adrien JUND Date: Fri, 21 Oct 2016 12:19:07 +0200 Subject: Add securityfolder doc --- fs/expose/dokan/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/expose/dokan/__init__.py b/fs/expose/dokan/__init__.py index 5e4643f..e10d0c0 100644 --- a/fs/expose/dokan/__init__.py +++ b/fs/expose/dokan/__init__.py @@ -958,6 +958,7 @@ def mount(fs, path, foreground=False, ready_callback=None, unmount_callback=None * numthreads: number of threads to use for handling Dokan requests * fsname: name to display in explorer etc * flags: DOKAN_OPTIONS bitmask + * securityfolder: folder path used to duplicate security rights on all folders * FSOperationsClass: custom FSOperations subclass to use """ -- cgit v1.2.1 From f3c396f454de47cfbf2cd2a894c7d54d3509cfae Mon Sep 17 00:00:00 2001 From: Adrien JUND Date: Mon, 24 Oct 2016 10:07:44 +0200 Subject: Fix import cPickle commandline --- fs/expose/dokan/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/expose/dokan/__init__.py b/fs/expose/dokan/__init__.py index e10d0c0..a447aaf 100644 --- a/fs/expose/dokan/__init__.py +++ b/fs/expose/dokan/__init__.py @@ -1067,7 +1067,8 @@ class MountProcess(subprocess.Popen): raise OSError("the dokan library is not available") _check_path_string(path) self.path = path - cmd = "try: import cPickle as pickle; except ImportError: import pickle;\n" + cmd = "try: import cPickle as pickle;\n" + cmd = cmd + "except ImportError: import pickle;\n" cmd = cmd + "data = pickle.loads(%s); " cmd = cmd + "from fs.expose.dokan import MountProcess; " cmd = cmd + "MountProcess._do_mount(data)" -- cgit v1.2.1 From e76d0184828c3da10e561bd1becf2f8e0f377e7f Mon Sep 17 00:00:00 2001 From: Adrien JUND Date: Mon, 24 Oct 2016 10:23:49 +0200 Subject: Add missing import ctypes --- fs/expose/dokan/libdokan.py | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/expose/dokan/libdokan.py b/fs/expose/dokan/libdokan.py index b848316..32ef3b3 100644 --- a/fs/expose/dokan/libdokan.py +++ b/fs/expose/dokan/libdokan.py @@ -7,6 +7,7 @@ """ +import ctypes from ctypes import * try: -- cgit v1.2.1 From be2afb4fd5fed5d845fe304d75d265f1eca37564 Mon Sep 17 00:00:00 2001 From: Adrien JUND Date: Mon, 24 Oct 2016 14:09:54 +0200 Subject: Move to import ctypes --- fs/expose/dokan/libdokan.py | 115 ++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 58 deletions(-) diff --git a/fs/expose/dokan/libdokan.py b/fs/expose/dokan/libdokan.py index 32ef3b3..fd85eee 100644 --- a/fs/expose/dokan/libdokan.py +++ b/fs/expose/dokan/libdokan.py @@ -8,26 +8,25 @@ """ import ctypes -from ctypes import * try: - DokanMain = windll.Dokan1.DokanMain - DokanVersion = windll.Dokan1.DokanVersion + DokanMain = ctypes.windll.Dokan1.DokanMain + DokanVersion = ctypes.windll.Dokan1.DokanVersion except AttributeError: raise ImportError("Dokan DLL not found") from ctypes.wintypes import * -ULONG64 = c_ulonglong -PULONGLONG = POINTER(c_ulonglong) -PVOID = c_void_p -PULONG = POINTER(c_ulong) -UCHAR = c_ubyte -LPDWORD = POINTER(c_ulong) -LONGLONG = c_longlong -NTSTATUS = c_long -USHORT = c_ushort -WCHAR = c_wchar +ULONG64 = ctypes.c_ulonglong +PULONGLONG = ctypes.POINTER(ctypes.c_ulonglong) +PVOID = ctypes.c_void_p +PULONG = ctypes.POINTER(ctypes.c_ulong) +UCHAR = ctypes.c_ubyte +LPDWORD = ctypes.POINTER(ctypes.c_ulong) +LONGLONG = ctypes.c_longlong +NTSTATUS = ctypes.c_long +USHORT = ctypes.c_ushort +WCHAR = ctypes.c_wchar DokanVersion.restype = ULONG @@ -47,14 +46,14 @@ PPSECURITY_DESCRIPTOR = ctypes.POINTER(PSECURITY_DESCRIPTOR) SECURITY_INFORMATION = DWORD PSECURITY_INFORMATION = ctypes.POINTER(SECURITY_INFORMATION) -class FILETIME(Structure): +class FILETIME(ctypes.Structure): _fields_ = [ ("dwLowDateTime", DWORD), ("dwHighDateTime", DWORD), ] -class WIN32_FIND_DATAW(Structure): +class WIN32_FIND_DATAW(ctypes.Structure): _fields_ = [ ("dwFileAttributes", DWORD), ("ftCreationTime", FILETIME), @@ -69,7 +68,7 @@ class WIN32_FIND_DATAW(Structure): ] -class BY_HANDLE_FILE_INFORMATION(Structure): +class BY_HANDLE_FILE_INFORMATION(ctypes.Structure): _fields_ = [ ('dwFileAttributes', DWORD), ('ftCreationTime', FILETIME), @@ -84,7 +83,7 @@ class BY_HANDLE_FILE_INFORMATION(Structure): ] -class DOKAN_OPTIONS(Structure): +class DOKAN_OPTIONS(ctypes.Structure): _fields_ = [ ("Version", USHORT), ("ThreadCount", USHORT), @@ -98,11 +97,11 @@ class DOKAN_OPTIONS(Structure): ] -class DOKAN_FILE_INFO(Structure): +class DOKAN_FILE_INFO(ctypes.Structure): _fields_ = [ ("Context", ULONG64), ("DokanContext", ULONG64), - ("DokanOptions", POINTER(DOKAN_OPTIONS)), + ("DokanOptions", ctypes.POINTER(DOKAN_OPTIONS)), ("ProcessId", ULONG), ("IsDirectory", UCHAR), ("DeleteOnClose", UCHAR), @@ -113,13 +112,13 @@ class DOKAN_FILE_INFO(Structure): ] -PDOKAN_FILE_INFO = POINTER(DOKAN_FILE_INFO) -PFillFindData = WINFUNCTYPE(c_int, POINTER(WIN32_FIND_DATAW), PDOKAN_FILE_INFO) +PDOKAN_FILE_INFO = ctypes.POINTER(DOKAN_FILE_INFO) +PFillFindData = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.POINTER(WIN32_FIND_DATAW), PDOKAN_FILE_INFO) -class DOKAN_OPERATIONS(Structure): +class DOKAN_OPERATIONS(ctypes.Structure): _fields_ = [ - ("ZwCreateFile", WINFUNCTYPE(NTSTATUS, + ("ZwCreateFile", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName PVOID, # SecurityContext, see # https://msdn.microsoft.com/en-us/library/windows/hardware/ff550613(v=vs.85).aspx @@ -129,87 +128,87 @@ class DOKAN_OPERATIONS(Structure): ULONG, # CreateDisposition ULONG, # CreateOptions PDOKAN_FILE_INFO)), - ("Cleanup", WINFUNCTYPE(None, + ("Cleanup", ctypes.WINFUNCTYPE(None, LPCWSTR, # FileName PDOKAN_FILE_INFO)), - ("CloseFile", WINFUNCTYPE(None, + ("CloseFile", ctypes.WINFUNCTYPE(None, LPCWSTR, # FileName PDOKAN_FILE_INFO)), - ("ReadFile", WINFUNCTYPE(NTSTATUS, + ("ReadFile", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName LPVOID, # Buffer DWORD, # NumberOfBytesToRead LPDWORD, # NumberOfBytesRead LONGLONG, # Offset PDOKAN_FILE_INFO)), - ("WriteFile", WINFUNCTYPE(NTSTATUS, + ("WriteFile", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName LPCVOID, # Buffer DWORD, # NumberOfBytesToWrite LPDWORD, # NumberOfBytesWritten LONGLONG, # Offset PDOKAN_FILE_INFO)), - ("FlushFileBuffers", WINFUNCTYPE(NTSTATUS, + ("FlushFileBuffers", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName PDOKAN_FILE_INFO)), - ("GetFileInformation", WINFUNCTYPE(NTSTATUS, + ("GetFileInformation", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName - POINTER(BY_HANDLE_FILE_INFORMATION), # Buffer + ctypes.POINTER(BY_HANDLE_FILE_INFORMATION), # Buffer PDOKAN_FILE_INFO)), - ("FindFiles", WINFUNCTYPE(NTSTATUS, + ("FindFiles", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # PathName PFillFindData, # call this function with PWIN32_FIND_DATAW PDOKAN_FILE_INFO)), - ("FindFilesWithPattern", WINFUNCTYPE(NTSTATUS, + ("FindFilesWithPattern", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # PathName LPCWSTR, # SearchPattern PFillFindData, #call this function with PWIN32_FIND_DATAW PDOKAN_FILE_INFO)), - ("SetFileAttributes", WINFUNCTYPE(NTSTATUS, + ("SetFileAttributes", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName DWORD, # FileAttributes PDOKAN_FILE_INFO)), - ("SetFileTime", WINFUNCTYPE(NTSTATUS, + ("SetFileTime", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName - POINTER(FILETIME), # CreationTime - POINTER(FILETIME), # LastAccessTime - POINTER(FILETIME), # LastWriteTime + ctypes.POINTER(FILETIME), # CreationTime + ctypes.POINTER(FILETIME), # LastAccessTime + ctypes.POINTER(FILETIME), # LastWriteTime PDOKAN_FILE_INFO)), - ("DeleteFile", WINFUNCTYPE(NTSTATUS, + ("DeleteFile", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName PDOKAN_FILE_INFO)), - ("DeleteDirectory", WINFUNCTYPE(NTSTATUS, + ("DeleteDirectory", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName PDOKAN_FILE_INFO)), - ("MoveFile", WINFUNCTYPE(NTSTATUS, + ("MoveFile", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # ExistingFileName LPCWSTR, # NewFileName BOOL, # ReplaceExisiting PDOKAN_FILE_INFO)), - ("SetEndOfFile", WINFUNCTYPE(NTSTATUS, + ("SetEndOfFile", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName LONGLONG, # Length PDOKAN_FILE_INFO)), - ("SetAllocationSize", WINFUNCTYPE(NTSTATUS, + ("SetAllocationSize", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName LONGLONG, # Length PDOKAN_FILE_INFO)), - ("LockFile", WINFUNCTYPE(NTSTATUS, + ("LockFile", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName LONGLONG, # ByteOffset LONGLONG, # Length PDOKAN_FILE_INFO)), - ("UnlockFile", WINFUNCTYPE(NTSTATUS, + ("UnlockFile", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName LONGLONG, # ByteOffset LONGLONG, # Length PDOKAN_FILE_INFO)), - ("GetDiskFreeSpace", WINFUNCTYPE(NTSTATUS, + ("GetDiskFreeSpace", ctypes.WINFUNCTYPE(NTSTATUS, PULONGLONG, # FreeBytesAvailable PULONGLONG, # TotalNumberOfBytes PULONGLONG, # TotalNumberOfFreeBytes PDOKAN_FILE_INFO)), - ("GetVolumeInformation", WINFUNCTYPE(NTSTATUS, + ("GetVolumeInformation", ctypes.WINFUNCTYPE(NTSTATUS, PVOID, # VolumeNameBuffer DWORD, # VolumeNameSize in num of chars LPDWORD, # VolumeSerialNumber @@ -218,43 +217,43 @@ class DOKAN_OPERATIONS(Structure): PVOID, # FileSystemNameBuffer DWORD, # FileSystemNameSize in num of chars PDOKAN_FILE_INFO)), - ("Mounted", WINFUNCTYPE(NTSTATUS, + ("Mounted", ctypes.WINFUNCTYPE(NTSTATUS, PDOKAN_FILE_INFO)), - ("Unmounted", WINFUNCTYPE(NTSTATUS, + ("Unmounted", ctypes.WINFUNCTYPE(NTSTATUS, DOKAN_FILE_INFO)), - ("GetFileSecurity", WINFUNCTYPE(NTSTATUS, + ("GetFileSecurity", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName PULONG, # A pointer to SECURITY_INFORMATION value being requested PVOID, # A pointer to SECURITY_DESCRIPTOR buffer to be filled ULONG, # Length of Security descriptor buffer PULONG, # Length Needed PDOKAN_FILE_INFO)), - ("SetFileSecurity", WINFUNCTYPE(NTSTATUS, + ("SetFileSecurity", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName PVOID, # A pointer to SECURITY_INFORMATION value being PVOID, # A pointer to SECURITY_DESCRIPTOR buffer ULONG, # Length of Security descriptor buffer PDOKAN_FILE_INFO)), - ("FindStreams", WINFUNCTYPE(NTSTATUS, + ("FindStreams", ctypes.WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName PVOID, # call this function with PWIN32_FIND_STREAM_DATA PDOKAN_FILE_INFO)) ] -DokanMain.restype = c_int +DokanMain.restype = ctypes.c_int DokanMain.argtypes = ( - POINTER(DOKAN_OPTIONS), - POINTER(DOKAN_OPERATIONS), + ctypes.POINTER(DOKAN_OPTIONS), + ctypes.POINTER(DOKAN_OPERATIONS), ) -DokanRemoveMountPoint = windll.Dokan1.DokanRemoveMountPoint +DokanRemoveMountPoint = ctypes.windll.Dokan1.DokanRemoveMountPoint DokanRemoveMountPoint.restype = BOOL DokanRemoveMountPoint.argtypes = ( LPCWSTR, ) -DokanIsNameInExpression = windll.Dokan1.DokanIsNameInExpression +DokanIsNameInExpression = ctypes.windll.Dokan1.DokanIsNameInExpression DokanIsNameInExpression.restype = BOOL DokanIsNameInExpression.argtypes = ( LPCWSTR, # pattern @@ -262,12 +261,12 @@ DokanIsNameInExpression.argtypes = ( BOOL, # ignore case ) -DokanDriverVersion = windll.Dokan1.DokanDriverVersion +DokanDriverVersion = ctypes.windll.Dokan1.DokanDriverVersion DokanDriverVersion.restype = ULONG DokanDriverVersion.argtypes = ( ) -DokanResetTimeout = windll.Dokan1.DokanResetTimeout +DokanResetTimeout = ctypes.windll.Dokan1.DokanResetTimeout DokanResetTimeout.restype = BOOL DokanResetTimeout.argtypes = ( ULONG, #timeout -- cgit v1.2.1 From bc2e17c8a222779c47143ea9a55a7a45674c201f Mon Sep 17 00:00:00 2001 From: Adrien JUND Date: Mon, 24 Oct 2016 15:28:33 +0200 Subject: Move to from ctypes import * --- fs/expose/dokan/libdokan.py | 126 ++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/fs/expose/dokan/libdokan.py b/fs/expose/dokan/libdokan.py index fd85eee..9d33be4 100644 --- a/fs/expose/dokan/libdokan.py +++ b/fs/expose/dokan/libdokan.py @@ -7,26 +7,26 @@ """ -import ctypes +from ctypes import * try: - DokanMain = ctypes.windll.Dokan1.DokanMain - DokanVersion = ctypes.windll.Dokan1.DokanVersion + DokanMain = windll.Dokan1.DokanMain + DokanVersion = windll.Dokan1.DokanVersion except AttributeError: raise ImportError("Dokan DLL not found") from ctypes.wintypes import * -ULONG64 = ctypes.c_ulonglong -PULONGLONG = ctypes.POINTER(ctypes.c_ulonglong) -PVOID = ctypes.c_void_p -PULONG = ctypes.POINTER(ctypes.c_ulong) -UCHAR = ctypes.c_ubyte -LPDWORD = ctypes.POINTER(ctypes.c_ulong) -LONGLONG = ctypes.c_longlong -NTSTATUS = ctypes.c_long -USHORT = ctypes.c_ushort -WCHAR = ctypes.c_wchar +ULONG64 = c_ulonglong +PULONGLONG = POINTER(c_ulonglong) +PVOID = c_void_p +PULONG = POINTER(c_ulong) +UCHAR = c_ubyte +LPDWORD = POINTER(c_ulong) +LONGLONG = c_longlong +NTSTATUS = c_long +USHORT = c_ushort +WCHAR = c_wchar DokanVersion.restype = ULONG @@ -38,22 +38,22 @@ if DokanVersion() < DOKAN_MINIMUM_COMPATIBLE_VERSION: MAX_PATH = 260 -class SECURITY_DESCRIPTOR(ctypes.Structure): pass +class SECURITY_DESCRIPTOR(Structure): pass -PSECURITY_DESCRIPTOR = ctypes.POINTER(SECURITY_DESCRIPTOR) -PPSECURITY_DESCRIPTOR = ctypes.POINTER(PSECURITY_DESCRIPTOR) +PSECURITY_DESCRIPTOR = POINTER(SECURITY_DESCRIPTOR) +PPSECURITY_DESCRIPTOR = POINTER(PSECURITY_DESCRIPTOR) SECURITY_INFORMATION = DWORD -PSECURITY_INFORMATION = ctypes.POINTER(SECURITY_INFORMATION) +PSECURITY_INFORMATION = POINTER(SECURITY_INFORMATION) -class FILETIME(ctypes.Structure): +class FILETIME(Structure): _fields_ = [ ("dwLowDateTime", DWORD), ("dwHighDateTime", DWORD), ] -class WIN32_FIND_DATAW(ctypes.Structure): +class WIN32_FIND_DATAW(Structure): _fields_ = [ ("dwFileAttributes", DWORD), ("ftCreationTime", FILETIME), @@ -68,7 +68,7 @@ class WIN32_FIND_DATAW(ctypes.Structure): ] -class BY_HANDLE_FILE_INFORMATION(ctypes.Structure): +class BY_HANDLE_FILE_INFORMATION(Structure): _fields_ = [ ('dwFileAttributes', DWORD), ('ftCreationTime', FILETIME), @@ -83,7 +83,7 @@ class BY_HANDLE_FILE_INFORMATION(ctypes.Structure): ] -class DOKAN_OPTIONS(ctypes.Structure): +class DOKAN_OPTIONS(Structure): _fields_ = [ ("Version", USHORT), ("ThreadCount", USHORT), @@ -97,11 +97,11 @@ class DOKAN_OPTIONS(ctypes.Structure): ] -class DOKAN_FILE_INFO(ctypes.Structure): +class DOKAN_FILE_INFO(Structure): _fields_ = [ ("Context", ULONG64), ("DokanContext", ULONG64), - ("DokanOptions", ctypes.POINTER(DOKAN_OPTIONS)), + ("DokanOptions", POINTER(DOKAN_OPTIONS)), ("ProcessId", ULONG), ("IsDirectory", UCHAR), ("DeleteOnClose", UCHAR), @@ -112,13 +112,13 @@ class DOKAN_FILE_INFO(ctypes.Structure): ] -PDOKAN_FILE_INFO = ctypes.POINTER(DOKAN_FILE_INFO) -PFillFindData = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.POINTER(WIN32_FIND_DATAW), PDOKAN_FILE_INFO) +PDOKAN_FILE_INFO = POINTER(DOKAN_FILE_INFO) +PFillFindData = WINFUNCTYPE(c_int, POINTER(WIN32_FIND_DATAW), PDOKAN_FILE_INFO) -class DOKAN_OPERATIONS(ctypes.Structure): +class DOKAN_OPERATIONS(Structure): _fields_ = [ - ("ZwCreateFile", ctypes.WINFUNCTYPE(NTSTATUS, + ("ZwCreateFile", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName PVOID, # SecurityContext, see # https://msdn.microsoft.com/en-us/library/windows/hardware/ff550613(v=vs.85).aspx @@ -128,87 +128,87 @@ class DOKAN_OPERATIONS(ctypes.Structure): ULONG, # CreateDisposition ULONG, # CreateOptions PDOKAN_FILE_INFO)), - ("Cleanup", ctypes.WINFUNCTYPE(None, + ("Cleanup", WINFUNCTYPE(None, LPCWSTR, # FileName PDOKAN_FILE_INFO)), - ("CloseFile", ctypes.WINFUNCTYPE(None, + ("CloseFile", WINFUNCTYPE(None, LPCWSTR, # FileName PDOKAN_FILE_INFO)), - ("ReadFile", ctypes.WINFUNCTYPE(NTSTATUS, + ("ReadFile", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName LPVOID, # Buffer DWORD, # NumberOfBytesToRead LPDWORD, # NumberOfBytesRead LONGLONG, # Offset PDOKAN_FILE_INFO)), - ("WriteFile", ctypes.WINFUNCTYPE(NTSTATUS, + ("WriteFile", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName LPCVOID, # Buffer DWORD, # NumberOfBytesToWrite LPDWORD, # NumberOfBytesWritten LONGLONG, # Offset PDOKAN_FILE_INFO)), - ("FlushFileBuffers", ctypes.WINFUNCTYPE(NTSTATUS, + ("FlushFileBuffers", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName PDOKAN_FILE_INFO)), - ("GetFileInformation", ctypes.WINFUNCTYPE(NTSTATUS, + ("GetFileInformation", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName - ctypes.POINTER(BY_HANDLE_FILE_INFORMATION), # Buffer + POINTER(BY_HANDLE_FILE_INFORMATION), # Buffer PDOKAN_FILE_INFO)), - ("FindFiles", ctypes.WINFUNCTYPE(NTSTATUS, + ("FindFiles", WINFUNCTYPE(NTSTATUS, LPCWSTR, # PathName PFillFindData, # call this function with PWIN32_FIND_DATAW PDOKAN_FILE_INFO)), - ("FindFilesWithPattern", ctypes.WINFUNCTYPE(NTSTATUS, + ("FindFilesWithPattern", WINFUNCTYPE(NTSTATUS, LPCWSTR, # PathName LPCWSTR, # SearchPattern PFillFindData, #call this function with PWIN32_FIND_DATAW PDOKAN_FILE_INFO)), - ("SetFileAttributes", ctypes.WINFUNCTYPE(NTSTATUS, + ("SetFileAttributes", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName DWORD, # FileAttributes PDOKAN_FILE_INFO)), - ("SetFileTime", ctypes.WINFUNCTYPE(NTSTATUS, + ("SetFileTime", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName - ctypes.POINTER(FILETIME), # CreationTime - ctypes.POINTER(FILETIME), # LastAccessTime - ctypes.POINTER(FILETIME), # LastWriteTime + POINTER(FILETIME), # CreationTime + POINTER(FILETIME), # LastAccessTime + POINTER(FILETIME), # LastWriteTime PDOKAN_FILE_INFO)), - ("DeleteFile", ctypes.WINFUNCTYPE(NTSTATUS, + ("DeleteFile", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName PDOKAN_FILE_INFO)), - ("DeleteDirectory", ctypes.WINFUNCTYPE(NTSTATUS, + ("DeleteDirectory", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName PDOKAN_FILE_INFO)), - ("MoveFile", ctypes.WINFUNCTYPE(NTSTATUS, + ("MoveFile", WINFUNCTYPE(NTSTATUS, LPCWSTR, # ExistingFileName LPCWSTR, # NewFileName BOOL, # ReplaceExisiting PDOKAN_FILE_INFO)), - ("SetEndOfFile", ctypes.WINFUNCTYPE(NTSTATUS, + ("SetEndOfFile", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName LONGLONG, # Length PDOKAN_FILE_INFO)), - ("SetAllocationSize", ctypes.WINFUNCTYPE(NTSTATUS, + ("SetAllocationSize", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName LONGLONG, # Length PDOKAN_FILE_INFO)), - ("LockFile", ctypes.WINFUNCTYPE(NTSTATUS, + ("LockFile", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName LONGLONG, # ByteOffset LONGLONG, # Length PDOKAN_FILE_INFO)), - ("UnlockFile", ctypes.WINFUNCTYPE(NTSTATUS, + ("UnlockFile", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName LONGLONG, # ByteOffset LONGLONG, # Length PDOKAN_FILE_INFO)), - ("GetDiskFreeSpace", ctypes.WINFUNCTYPE(NTSTATUS, + ("GetDiskFreeSpace", WINFUNCTYPE(NTSTATUS, PULONGLONG, # FreeBytesAvailable PULONGLONG, # TotalNumberOfBytes PULONGLONG, # TotalNumberOfFreeBytes PDOKAN_FILE_INFO)), - ("GetVolumeInformation", ctypes.WINFUNCTYPE(NTSTATUS, + ("GetVolumeInformation", WINFUNCTYPE(NTSTATUS, PVOID, # VolumeNameBuffer DWORD, # VolumeNameSize in num of chars LPDWORD, # VolumeSerialNumber @@ -217,43 +217,43 @@ class DOKAN_OPERATIONS(ctypes.Structure): PVOID, # FileSystemNameBuffer DWORD, # FileSystemNameSize in num of chars PDOKAN_FILE_INFO)), - ("Mounted", ctypes.WINFUNCTYPE(NTSTATUS, + ("Mounted", WINFUNCTYPE(NTSTATUS, PDOKAN_FILE_INFO)), - ("Unmounted", ctypes.WINFUNCTYPE(NTSTATUS, + ("Unmounted", WINFUNCTYPE(NTSTATUS, DOKAN_FILE_INFO)), - ("GetFileSecurity", ctypes.WINFUNCTYPE(NTSTATUS, + ("GetFileSecurity", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName PULONG, # A pointer to SECURITY_INFORMATION value being requested PVOID, # A pointer to SECURITY_DESCRIPTOR buffer to be filled ULONG, # Length of Security descriptor buffer PULONG, # Length Needed PDOKAN_FILE_INFO)), - ("SetFileSecurity", ctypes.WINFUNCTYPE(NTSTATUS, + ("SetFileSecurity", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName PVOID, # A pointer to SECURITY_INFORMATION value being PVOID, # A pointer to SECURITY_DESCRIPTOR buffer ULONG, # Length of Security descriptor buffer PDOKAN_FILE_INFO)), - ("FindStreams", ctypes.WINFUNCTYPE(NTSTATUS, + ("FindStreams", WINFUNCTYPE(NTSTATUS, LPCWSTR, # FileName PVOID, # call this function with PWIN32_FIND_STREAM_DATA PDOKAN_FILE_INFO)) ] -DokanMain.restype = ctypes.c_int +DokanMain.restype = c_int DokanMain.argtypes = ( - ctypes.POINTER(DOKAN_OPTIONS), - ctypes.POINTER(DOKAN_OPERATIONS), + POINTER(DOKAN_OPTIONS), + POINTER(DOKAN_OPERATIONS), ) -DokanRemoveMountPoint = ctypes.windll.Dokan1.DokanRemoveMountPoint +DokanRemoveMountPoint = windll.Dokan1.DokanRemoveMountPoint DokanRemoveMountPoint.restype = BOOL DokanRemoveMountPoint.argtypes = ( LPCWSTR, ) -DokanIsNameInExpression = ctypes.windll.Dokan1.DokanIsNameInExpression +DokanIsNameInExpression = windll.Dokan1.DokanIsNameInExpression DokanIsNameInExpression.restype = BOOL DokanIsNameInExpression.argtypes = ( LPCWSTR, # pattern @@ -261,19 +261,19 @@ DokanIsNameInExpression.argtypes = ( BOOL, # ignore case ) -DokanDriverVersion = ctypes.windll.Dokan1.DokanDriverVersion +DokanDriverVersion = windll.Dokan1.DokanDriverVersion DokanDriverVersion.restype = ULONG DokanDriverVersion.argtypes = ( ) -DokanResetTimeout = ctypes.windll.Dokan1.DokanResetTimeout +DokanResetTimeout = windll.Dokan1.DokanResetTimeout DokanResetTimeout.restype = BOOL DokanResetTimeout.argtypes = ( ULONG, #timeout PDOKAN_FILE_INFO, # file info pointer ) -GetFileSecurity = ctypes.windll.advapi32.GetFileSecurityW +GetFileSecurity = windll.advapi32.GetFileSecurityW GetFileSecurity.restype = BOOL GetFileSecurity.argtypes = ( LPWSTR, # _In_ LPCTSTR lpFileName, -- cgit v1.2.1 From 3e0a6cbc22ecdcda828b098d52ba860186d49854 Mon Sep 17 00:00:00 2001 From: Liryna Date: Thu, 10 Nov 2016 21:34:14 +0100 Subject: Change handle_fs_errors old error values --- fs/expose/dokan/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/expose/dokan/__init__.py b/fs/expose/dokan/__init__.py index a447aaf..d479ea7 100644 --- a/fs/expose/dokan/__init__.py +++ b/fs/expose/dokan/__init__.py @@ -214,9 +214,9 @@ def handle_fs_errors(func): res = func(*args, **kwds) except OSError as e: if e.errno: - res = -1 * _errno2syserrcode(e.errno) + res = _errno2syserrcode(e.errno) else: - res = -1 + res = STATUS_ACCESS_DENIED; except Exception as e: raise else: -- cgit v1.2.1 From f43b3a576c3a0d7f21f784c6c39280d2f87b196e Mon Sep 17 00:00:00 2001 From: Liryna Date: Fri, 11 Nov 2016 17:53:43 +0100 Subject: open as directory if isdir --- fs/expose/dokan/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/expose/dokan/__init__.py b/fs/expose/dokan/__init__.py index d479ea7..1a52074 100644 --- a/fs/expose/dokan/__init__.py +++ b/fs/expose/dokan/__init__.py @@ -432,7 +432,8 @@ class FSOperations(object): return STATUS_ACCESS_DENIED retcode = STATUS_SUCCESS - if info.contents.IsDirectory: + if self.fs.isdir(path) or info.contents.IsDirectory: + info.contents.IsDirectory = True exist = self.fs.exists(path) if disposition == FILE_CREATE: if self.fs.exists(path): -- cgit v1.2.1