summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiryna <liryna.stark@gmail.com>2016-07-27 20:55:20 +0200
committerAdrien JUND <adrien.jund@islog.com>2016-10-20 13:39:57 +0200
commit7a0e8f66785f165d76477105c2298e03eb5d0695 (patch)
treefb48bd467a4a991fd180a110196b758f9401e151
parent040e18b6f8f20b9dc86d5e85797f07f86f0aeba3 (diff)
downloadpyfilesystem-git-7a0e8f66785f165d76477105c2298e03eb5d0695.tar.gz
Update Dokan wrapper to 1.0.0
-rw-r--r--fs/expose/dokan/__init__.py548
-rw-r--r--fs/expose/dokan/libdokan.py304
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. <liryna.stark@gmail.com>.
# 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. <liryna.stark@gmail.com>.
# 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
+)