Commit 07499b3e by rfkelly0

Fix various DAVFS issues from issue #40

  * don't close the socket when handing off to RemoteFileBuffer
  * remove redundant check for "w" mode in the 404 case
  * properly close the socket in the isdir() case
  * add readline() method to RemoteFileBuffer
parent 803816d3
...@@ -271,16 +271,16 @@ class DAVFS(FS): ...@@ -271,16 +271,16 @@ class DAVFS(FS):
raise_generic_error(resp,"setcontents",path) raise_generic_error(resp,"setcontents",path)
def open(self,path,mode="r"): def open(self,path,mode="r"):
# Truncate the file if requested
mode = mode.replace("b","").replace("t","") mode = mode.replace("b","").replace("t","")
# Truncate the file if requested
contents = "" contents = ""
if "w" in mode: if "w" in mode:
self.setcontents(path,contents) self.setcontents(path,contents)
else: else:
contents = self._request(path,"GET") contents = self._request(path,"GET")
if contents.status == 404: if contents.status == 404:
# Create the file if it's missing # Create the file if it's missing in append mode.
if "w" not in mode and "a" not in mode: if "a" not in mode:
contents.close() contents.close()
raise ResourceNotFoundError(path) raise ResourceNotFoundError(path)
contents = "" contents = ""
...@@ -292,7 +292,9 @@ class DAVFS(FS): ...@@ -292,7 +292,9 @@ class DAVFS(FS):
contents.close() contents.close()
raise_generic_error(contents,"open",path) raise_generic_error(contents,"open",path)
elif self.isdir(path): elif self.isdir(path):
contents.close()
raise ResourceInvalidError(path) raise ResourceInvalidError(path)
# For streaming reads, return the socket contents directly.
if mode == "r-": if mode == "r-":
contents.size = contents.getheader("Content-Length",None) contents.size = contents.getheader("Content-Length",None)
if contents.size is not None: if contents.size is not None:
...@@ -301,11 +303,9 @@ class DAVFS(FS): ...@@ -301,11 +303,9 @@ class DAVFS(FS):
except ValueError: except ValueError:
contents.size = None contents.size = None
return contents return contents
try: # For everything else, use a RemoteFileBuffer.
# This will take care of closing the socket when it's done.
return RemoteFileBuffer(self,path,mode,contents) return RemoteFileBuffer(self,path,mode,contents)
finally:
if hasattr(contents,"close"):
contents.close()
def exists(self,path): def exists(self,path):
response = self._request(path,"PROPFIND","",{"Depth":"0"}) response = self._request(path,"PROPFIND","",{"Depth":"0"})
......
...@@ -240,6 +240,8 @@ def convert_os_errors(func): ...@@ -240,6 +240,8 @@ def convert_os_errors(func):
raise StorageSpaceError(opname,path=path,details=e),None,tb raise StorageSpaceError(opname,path=path,details=e),None,tb
if e.errno == errno.EPERM: if e.errno == errno.EPERM:
raise PermissionDeniedError(opname,path=path,details=e),None,tb raise PermissionDeniedError(opname,path=path,details=e),None,tb
if e.errno == errno.ENONET:
raise RemoteConnectionError(opname,path=path,details=e),None,tb
if e.errno == errno.EACCES: if e.errno == errno.EACCES:
if sys.platform == "win32": if sys.platform == "win32":
if e.args[0] and e.args[0] == 32: if e.args[0] and e.args[0] == 32:
......
...@@ -170,7 +170,17 @@ def handle_fs_errors(func): ...@@ -170,7 +170,17 @@ def handle_fs_errors(func):
def timeout_protect(func): def timeout_protect(func):
"""Method decorator to enable timeout protection during call.""" """Method decorator to enable timeout protection during call.
During long-running operations, Dokan requires that the DokanResetTimeout
function be called periodically to indicate the progress is still being
made. Unfortunately we don't have an facility for the underlying FS
to make these calls for us, so we have to hack around it.
The idea is to use a single background thread to monitor all active Dokan
method calls, checking that they haven't deadlocked and resetting the
appropriate timeout.
"""
@wraps(func) @wraps(func)
def wrapper(self,*args): def wrapper(self,*args):
info = args[-1] info = args[-1]
......
...@@ -159,6 +159,11 @@ class RemoteFileBuffer(object): ...@@ -159,6 +159,11 @@ class RemoteFileBuffer(object):
self._fillbuffer() self._fillbuffer()
return self.file.__iter__() return self.file.__iter__()
def readline(self):
# TODO: implement this with on-demand loading.
self._fillbuffer()
return self.file.readline()
def _read(self, length=None): def _read(self, length=None):
"""Read data from the remote file into the local buffer.""" """Read data from the remote file into the local buffer."""
chunklen = 1024 * 256 chunklen = 1024 * 256
......
...@@ -210,6 +210,7 @@ class S3FS(FS): ...@@ -210,6 +210,7 @@ class S3FS(FS):
if not self.isdir(dirname(path)): if not self.isdir(dirname(path)):
raise ParentDirectoryMissingError(path) raise ParentDirectoryMissingError(path)
k = self._sync_set_contents(s3path,"") k = self._sync_set_contents(s3path,"")
# TODO: support streaming reads
return RemoteFileBuffer(self,path,mode,k) return RemoteFileBuffer(self,path,mode,k)
def exists(self,path): def exists(self,path):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment