Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
pyfs
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
pyfs
Commits
25868cb2
Commit
25868cb2
authored
Aug 04, 2010
by
rfkelly0
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
dokan: basic outputting of file/directory attributes
parent
ab0c29e9
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
198 additions
and
49 deletions
+198
-49
fs/expose/dokan/__init__.py
+182
-42
fs/expose/dokan/dokan_ctypes.py
+16
-7
No files found.
fs/expose/dokan/__init__.py
View file @
25868cb2
...
...
@@ -64,13 +64,13 @@ from fs.path import *
from
fs.functools
import
wraps
try
:
import
dokan_ctypes
as
dokan
import
dokan_ctypes
as
lib
dokan
except
NotImplementedError
:
raise
ImportError
(
"Dokan found but not usable"
)
DokanMain
=
dokan
.
DokanMain
DokanOperations
=
dokan
.
DokanOperations
DokanMain
=
lib
dokan
.
DokanMain
DokanOperations
=
lib
dokan
.
DokanOperations
# Options controlling the behaiour of the Dokan filesystem
DOKAN_OPTION_DEBUG
=
1
...
...
@@ -95,6 +95,16 @@ FILE_SHARE_WRITE = 0x02
FILE_FLAG_BACKUP_SEMANTICS
=
0x02000000
FILE_FLAG_OVERLAPPED
=
0x40000000
FILE_ATTRIBUTE_ARCHIVE
=
32
FILE_ATTRIBUTE_COMPRESSED
=
2048
FILE_ATTRIBUTE_DIRECTORY
=
16
FILE_ATTRIBUTE_HIDDEN
=
2
FILE_ATTRIBUTE_NORMAL
=
128
FILE_ATTRIBUTE_OFFLINE
=
4096
FILE_ATTRIBUTE_READONLY
=
1
FILE_ATTRIBUTE_SYSTEM
=
4
FILE_ATTRIBUTE_TEMPORARY
=
4
CREATE_NEW
=
1
CREATE_ALWAYS
=
2
OPEN_EXISTING
=
3
...
...
@@ -104,9 +114,12 @@ TRUNCATE_EXISTING = 5
GENERIC_READ
=
128
GENERIC_WRITE
=
1180054
STARTUP_TIME
=
time
.
time
()
# Some useful per-process global information
NATIVE_ENCODING
=
sys
.
getfilesystemencoding
()
DATETIME_ZERO
=
datetime
.
datetime
(
1
,
1
,
1
,
0
,
0
,
0
)
DATETIME_STARTUP
=
datetime
.
datetime
.
now
()
def
handle_fs_errors
(
func
):
"""Method decorator to report FS errors in the appropriate way.
...
...
@@ -148,6 +161,7 @@ class FSOperations(DokanOperations):
self
.
_files_by_handle
=
{}
self
.
_files_lock
=
threading
.
Lock
()
self
.
_next_handle
=
100
# TODO: do we need this for dokan? It's a hangover from FUSE.
# Dokan expects a succesful write() to be reflected in the file's
# reported size, but the FS might buffer writes and prevent this.
# We explicitly keep track of the size FUSE expects a file to be.
...
...
@@ -184,7 +198,8 @@ class FSOperations(DokanOperations):
finally
:
self
.
_files_lock
.
release
()
def
unmount
(
self
,
info
):
def
Unmount
(
self
,
info
):
raise
ValueError
(
"HERE"
)
if
self
.
_on_unmount
:
self
.
_on_unmount
()
...
...
@@ -199,6 +214,7 @@ class FSOperations(DokanOperations):
raise
ResourceNotFoundError
(
path
)
return
# Convert the various access rights into an appropriate mode string.
# TODO: I'm sure this misses some important semantics.
retcode
=
0
if
access
&
GENERIC_READ
:
if
access
&
GENERIC_WRITE
:
...
...
@@ -239,7 +255,7 @@ class FSOperations(DokanOperations):
info
.
contents
.
IsDirectory
=
True
except
FSError
:
# Sadly, win32 OSFS will raise all kinds of strange errors
# if you try to open() a directory.
# if you try to open() a directory.
Need to check by hand.
if
self
.
fs
.
isdir
(
path
):
info
.
contents
.
IsDirectory
=
True
else
:
...
...
@@ -325,34 +341,146 @@ class FSOperations(DokanOperations):
def
GetFileInformation
(
self
,
path
,
buffer
,
info
):
path
=
normpath
(
path
)
info
=
self
.
fs
.
getinfo
(
path
)
if
"name"
not
in
info
:
info
[
"name"
]
=
basename
(
path
)
data
=
buffer
.
contents
data
.
dwFileAttributes
=
0
data
.
ftCreationTime
=
dokan
.
FILETIME
(
0
,
0
)
data
.
ftCreationTime
=
dokan
.
FILETIME
(
0
,
0
)
data
.
ftAccessTime
=
dokan
.
FILETIME
(
0
,
0
)
data
.
ftWriteTime
=
dokan
.
FILETIME
(
0
,
0
)
data
.
nFileSizeHigh
=
0
data
.
nFileSizeLow
=
7
data
.
cFileName
=
basename
(
path
)
data
.
cAlternateFileName
=
None
_info2finddataw
(
info
,
data
)
@handle_fs_errors
def
FindFiles
(
self
,
path
,
fillFindData
,
info
):
path
=
normpath
(
path
)
for
nm
in
self
.
fs
.
listdir
(
path
):
fpath
=
pathjoin
(
path
,
nm
)
data
=
_info2finddataw
(
self
.
fs
.
getinfo
(
fpath
)
)
fillFindData
(
ctypes
.
byref
(
data
),
info
)
@handle_fs_errors
def
FindFilesWithPattern
(
self
,
path
,
pattern
,
fillFindData
,
info
):
path
=
normpath
(
path
)
datas
=
[]
for
nm
in
self
.
fs
.
listdir
(
path
,
wildcard
=
pattern
):
data
=
dokan
.
WIN32_FIND_DATAW
()
data
.
dwFileAttributes
=
0
data
.
ftCreateTime
=
dokan
.
FILETIME
(
0
,
0
)
data
.
ftAccessTime
=
dokan
.
FILETIME
(
0
,
0
)
data
.
ftWriteTime
=
dokan
.
FILETIME
(
0
,
0
)
data
.
nFileSizeHigh
=
0
data
.
nFileSizeLow
=
0
data
.
cFileName
=
nm
data
.
cAlternateFileName
=
""
infolist
=
[]
try
:
for
finfo
in
self
.
fs
.
listdir
(
path
,
info
=
True
):
nm
=
finfo
[
"name"
]
if
not
libdokan
.
DokanIsNameInExpression
(
pattern
,
nm
,
True
):
continue
infolist
.
append
(
finfo
)
except
(
TypeError
,
KeyError
,
UnsupportedError
):
filtered
=
True
for
nm
in
self
.
fs
.
listdir
(
path
):
if
not
libdokan
.
DokanIsNameInExpression
(
pattern
,
nm
,
True
):
continue
finfo
=
self
.
fs
.
getinfo
(
pathjoin
(
path
,
nm
))
finfo
[
"name"
]
=
nm
infolist
.
append
(
finfo
)
for
finfo
in
infolist
:
data
=
_info2finddataw
(
finfo
)
fillFindData
(
ctypes
.
byref
(
data
),
info
)
datas
.
append
(
data
)
@handle_fs_errors
def
SetFileAttributes
(
self
,
path
,
attrs
,
info
):
path
=
normpath
(
path
)
# TODO: decode various file attributes
@handle_fs_errors
def
SetFileTime
(
self
,
path
,
ctime
,
atime
,
mtime
,
info
):
path
=
normpath
(
path
)
if
ctime
is
not
None
:
raise
UnsupportedError
(
"cannot set creation time"
)
if
atime
is
not
None
:
atime
=
_filetime_to_datetime
(
atime
)
if
mtime
is
not
None
:
mtime
=
_filetime_to_datetime
(
mtime
)
self
.
fs
.
settimes
(
path
,
atime
,
mtime
,
ctime
)
@handle_fs_errors
def
DeleteFile
(
self
,
path
,
info
):
path
=
normpath
(
path
)
self
.
fs
.
remove
(
path
)
@handle_fs_errors
def
DeleteDirectory
(
self
,
path
,
info
):
path
=
normpath
(
path
)
self
.
fs
.
removedir
(
path
)
@handle_fs_errors
def
MoveFile
(
self
,
src
,
dst
,
overwrite
,
info
):
src
=
normpath
(
src
)
dst
=
normpath
(
dst
)
try
:
self
.
fs
.
move
(
src
,
dst
,
overwrite
=
overwrite
)
except
ResourceInvalidError
:
self
.
fs
.
movedir
(
src
,
dst
,
overwrite
=
overwrite
)
@handle_fs_errors
def
SetEndOfFile
(
self
,
path
,
dst
,
overwrite
,
info
):
path
=
normpath
(
path
)
(
file
,
_
,
lock
)
=
self
.
_get_file
(
info
.
contents
.
Context
)
lock
.
acquire
()
try
:
f
.
truncate
()
finally
:
lock
.
release
()
@handle_fs_errors
def
GetDiskFreeSpaceEx
(
self
,
nBytesAvail
,
nBytesTotal
,
nBytesFree
,
info
):
nBytesAvail
[
0
]
=
8
*
1024
*
1024
nBytesTotal
[
0
]
=
20
*
1024
*
1024
nBytesFree
[
0
]
=
12
*
1024
*
1024
@handle_fs_errors
def
GetVolumeInformation
(
self
,
vnmBuf
,
vnmSz
,
sNum
,
maxLen
,
flags
,
fnmBuf
,
fnmSz
,
info
):
vnmBuf
vnm
=
ctypes
.
create_unicode_buffer
(
"fs.expose.dokan"
[:
vnmSz
-
1
])
ctypes
.
memmove
(
vnmBuf
,
vnm
,
len
(
vnm
.
value
))
fnm
=
ctypes
.
create_unicode_buffer
(
"fs.expose.dokan"
[:
fnmSz
-
1
])
ctypes
.
memmove
(
fnmBuf
,
fnm
,
len
(
fnm
.
value
))
sNum
[
0
]
=
0
maxLen
[
0
]
=
libdokan
.
MAX_PATH
flags
=
0
def
_info2attrmask
(
info
):
"""Convert a file/directory info dict to a win32 file attributes mask."""
attrs
=
0
st_mode
=
info
.
get
(
"st_mode"
,
None
)
if
st_mode
:
if
statinfo
.
S_ISDIR
(
st_mode
):
attrs
|=
FILE_ATTRIBUTE_DIRECTORY
elif
statinfo
.
S_ISREG
(
st_mode
):
attrs
|=
FILE_ATTRIBUTE_NORMAL
return
attrs
def
_info2finddataw
(
info
,
data
=
None
):
"""Convert a file/directory info dict into a WIN32_FIND_DATAW struct."""
if
data
is
None
:
data
=
libdokan
.
WIN32_FIND_DATAW
()
data
.
dwFileAttributes
=
_info2attrmask
(
info
)
data
.
ftCreateTime
=
_datetime2filetime
(
info
.
get
(
"created_time"
,
None
))
data
.
ftAccessTime
=
_datetime2filetime
(
info
.
get
(
"accessed_time"
,
None
))
data
.
ftWriteTime
=
_datetime2filetime
(
info
.
get
(
"modified_time"
,
None
))
data
.
nFileSizeHigh
=
info
.
get
(
"size"
,
0
)
>>
32
data
.
nFileSizeLow
=
info
.
get
(
"size"
,
0
)
&
0xffffff
data
.
cFileName
=
info
.
get
(
"name"
,
""
)
data
.
cAlternateFileName
=
""
return
data
def
_filetime2datetime
(
ftime
):
"""Convert a FILETIME struct info datetime.datetime object."""
if
ftime
is
None
:
return
DATETIME_ZERO
if
ftime
.
dwLowDateTime
==
0
and
ftime
.
dwHighDateTime
==
0
:
return
DATETIME_ZERO
t
=
ftime
.
dwLowDateTime
&
(
ftime
.
dwHighDateTime
<<
32
)
return
datetime
.
datetime
.
fromtimestamp
(
t
)
def
_datetime2filetime
(
dtime
):
"""Convert a FILETIME struct info datetime.datetime object."""
if
dtime
is
None
:
return
libdokan
.
FILETIME
(
0
,
0
)
if
dtime
==
DATETIME_ZERO
:
return
libdokan
.
FILETIME
(
0
,
0
)
# TODO: this doesn't work on win32
t
=
int
(
dtime
.
strftime
(
"
%
s"
))
return
libdokan
.
FILETIME
(
t
>>
32
,
t
&
0xffffff
)
def
mount
(
fs
,
drive
,
foreground
=
False
,
ready_callback
=
None
,
unmount_callback
=
None
,
**
kwds
):
...
...
@@ -371,22 +499,34 @@ def mount(fs, drive, foreground=False, ready_callback=None, unmount_callback=Non
keyword arguments will be passed through as options to the underlying
Dokan library. Some interesting options include:
* TODO: what options?
* numthreads: number of threads to use for handling Dokan requests
* fsname: name to display in explorer etc
* flags: DOKAN_OPTIONS bitmask
"""
def
check_ready
(
mp
=
None
):
if
ready_callback
:
for
_
in
xrange
(
100
):
try
:
os
.
stat
(
mp
.
path
)
except
EnvironmentError
:
time
.
sleep
(
0.01
)
else
:
if
mp
and
mp
.
poll
()
!=
None
:
raise
OSError
(
"dokan mount process exited prematurely"
)
return
ready_callback
()
if
foreground
:
# We use OPTION_REMOVABLE for now as it gives an "eject" option
# in the context menu. Will remove this later.
# We use a single thread, also for debugging.
op
ts
=
dokan
.
DOKAN_OPTIONS
(
drive
,
1
,
DOKAN_OPTION_DEBUG
|
DOKAN_OPTION_STDERR
|
DOKAN_OPTION_REMOVABLE
)
ops
=
FSOperations
(
fs
,
on_init
=
ready_callback
,
on_unmount
=
unmount_callback
)
numthreads
=
kwds
.
pop
(
"numthreads"
,
0
)
flags
=
kwds
.
pop
(
"flags"
,
0
)
opts
=
libdokan
.
DOKAN_OPTIONS
(
drive
,
numthreads
,
flags
)
op
s
=
FSOperations
(
fs
,
on_unmount
=
unmount_callback
)
threading
.
Thread
(
target
=
check_ready
)
.
start
(
)
res
=
DokanMain
(
ctypes
.
byref
(
opts
),
ctypes
.
byref
(
ops
.
buffer
))
if
res
!=
DOKAN_SUCCESS
:
raise
Runtime
Error
(
"Dokan failed with error:
%
d"
%
(
res
,))
raise
OS
Error
(
"Dokan failed with error:
%
d"
%
(
res
,))
else
:
mp
=
MountProcess
(
fs
,
drive
,
kwds
)
if
ready_callback
:
ready_callback
()
check_ready
(
mp
)
if
unmount_callback
:
orig_unmount
=
mp
.
unmount
def
new_unmount
():
...
...
@@ -403,7 +543,7 @@ def unmount(drive):
It works but may leave dangling processes; its better to use the "unmount"
method on the MountProcess class if you have one.
"""
if
not
dokan
.
DokanUnmount
(
drive
):
if
not
lib
dokan
.
DokanUnmount
(
drive
):
raise
OSError
(
"filesystem could not be unmounted:
%
s"
%
(
drive
,))
...
...
@@ -442,7 +582,7 @@ class MountProcess(subprocess.Popen):
def
unmount
(
self
):
"""Cleanly unmount the Dokan filesystem, terminating this subprocess."""
if
not
dokan
.
DokanUnmount
(
self
.
drive
):
if
not
lib
dokan
.
DokanUnmount
(
self
.
drive
):
raise
OSError
(
"the filesystem could not be unmounted:
%
s"
%
(
self
.
drive
,))
self
.
terminate
()
...
...
@@ -470,9 +610,9 @@ class MountProcess(subprocess.Popen):
if
__name__
==
"__main__"
:
import
os
,
os
.
path
from
fs.tempfs
import
TempFS
def
ready_callback
():
print
"READY"
fs
=
TempFS
()
fs
.
setcontents
(
"test1.txt"
,
"test one"
)
mount
(
fs
,
"Q"
,
foreground
=
True
,
ready_callback
=
ready_callback
)
flags
=
DOKAN_OPTION_DEBUG
|
DOKAN_OPTION_STDERR
|
DOKAN_OPTION_REMOVABLE
mount
(
fs
,
"Q"
,
foreground
=
True
,
numthreads
=
1
,
flags
=
flags
)
fs/expose/dokan/dokan_ctypes.py
View file @
25868cb2
"""
fs.expose.dokan.dokan_ctypes: low-level ctypes interface to Dokan
"""
from
ctypes
import
*
try
:
DokanMain
=
windll
.
Dokan
.
DokanMain
DokanVersion
=
windll
.
Dokan
.
DokanVersion
except
AttributeError
:
raise
ImportError
(
"Dokan DLL not found"
)
...
...
@@ -15,6 +21,13 @@ UCHAR = c_ubyte
LPDWORD
=
POINTER
(
DWORD
)
LONGLONG
=
c_longlong
DokanVersion
.
restype
=
ULONG
DokanVersion
.
argtypes
=
()
if
DokanVersion
()
<
0
:
# TODO: find min supported version
raise
ImportError
(
"Dokan DLL is too old"
)
MAX_PATH
=
260
class
FILETIME
(
Structure
):
...
...
@@ -197,7 +210,9 @@ class DokanOperations(object):
try
:
setattr
(
self
.
buffer
,
nm
,
typ
(
getattr
(
self
,
nm
)))
except
AttributeError
:
setattr
(
self
.
buffer
,
nm
,
typ
(
self
.
_noop
))
#setattr(self.buffer,nm,typ(self._noop))
# This bizarre syntax creates a NULL function pointer.
setattr
(
self
.
buffer
,
nm
,
typ
())
def
_noop
(
self
,
*
args
):
return
-
1
...
...
@@ -226,12 +241,6 @@ DokanUnmount.argtypes = (
BOOL
,
# ignore case
)
DokanVersion
=
windll
.
Dokan
.
DokanVersion
DokanVersion
.
restype
=
ULONG
DokanVersion
.
argtypes
=
(
)
DokanDriverVersion
=
windll
.
Dokan
.
DokanDriverVersion
DokanDriverVersion
.
restype
=
ULONG
DokanDriverVersion
.
argtypes
=
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment