SFTP: ignore permissions when opening a file (needed for sshfs interoperability).
authordavid-sarah <david-sarah@jacaranda.org>
Tue, 8 Jun 2010 05:57:00 +0000 (22:57 -0700)
committerdavid-sarah <david-sarah@jacaranda.org>
Tue, 8 Jun 2010 05:57:00 +0000 (22:57 -0700)
src/allmydata/frontends/sftpd.py
src/allmydata/test/test_sftp.py

index de401fd1ea739ac01fda84b1346565c107a5370c..e09d845c58ec7cf3d352d87f77124b5be35eafe6 100644 (file)
@@ -191,7 +191,7 @@ def _lsLine(name, attrs):
     return l
 
 
-def _no_write(parent_readonly, child, metadata):
+def _no_write(parent_readonly, child, metadata=None):
     """Whether child should be listed as having read-only permissions in parent."""
 
     if child.is_unknown():
@@ -201,7 +201,7 @@ def _no_write(parent_readonly, child, metadata):
     elif parent_readonly or IDirectoryNode.providedBy(child):
         return True
     else:
-        return metadata.get('no-write', False)
+        return metadata is not None and metadata.get('no-write', False)
 
 
 def _populate_attrs(childnode, metadata, size=None):
@@ -1326,10 +1326,6 @@ class SFTPUserHandler(ConchUser, PrefixingLogMixin):
                 if (flags & FXF_WRITE) and root.is_readonly():
                     raise SFTPError(FX_PERMISSION_DENIED,
                                     "cannot write to a non-writeable filecap without a parent directory")
-                if (flags & FXF_WRITE) and root.is_mutable() and desired_metadata.get('no-write', False):
-                    raise SFTPError(FX_PERMISSION_DENIED,
-                                    "cannot write to a mutable filecap without a parent directory, when the "
-                                    "specified permissions would require the link from the parent to be made read-only")
                 if flags & FXF_EXCL:
                     raise SFTPError(FX_FAILURE,
                                     "cannot create a file exclusively when it already exists")
@@ -1346,7 +1342,7 @@ class SFTPUserHandler(ConchUser, PrefixingLogMixin):
                 # reported as r--r--r--, which is appropriate because an immutable file can't be
                 # written via this path.
 
-                metadata['no-write'] = _no_write(True, root, metadata)
+                metadata['no-write'] = _no_write(True, root)
                 return self._make_file(file, userpath, flags, filenode=root, metadata=metadata)
             else:
                 # case 2
@@ -1398,7 +1394,10 @@ class SFTPUserHandler(ConchUser, PrefixingLogMixin):
                         if noisy: self.log("_got_child( (%r, %r) )" % (filenode, current_metadata), level=NOISY)
 
                         metadata = update_metadata(current_metadata, desired_metadata, time())
-                        metadata['no-write'] = _no_write(parent_readonly, filenode, metadata)
+
+                        # Ignore the permissions of the desired_metadata in an open call. The permissions
+                        # can only be set by setAttrs.
+                        metadata['no-write'] = _no_write(parent_readonly, filenode, current_metadata)
 
                         if filenode.is_unknown():
                             raise SFTPError(FX_PERMISSION_DENIED,
index efba99e239f48c4af730d4b917ec83947a5a453f..0bc20e8789e9f0315f6e42b980757969314676a7 100644 (file)
@@ -573,12 +573,6 @@ class Handler(GridTestMixin, ShouldFailMixin, ReallyEqualMixin, unittest.TestCas
             self.shouldFailWithSFTPError(sftp.FX_PERMISSION_DENIED, "openFile readonly uri WRITE denied",
                                          self.handler.openFile, "uri/"+self.readonly_uri, sftp.FXF_WRITE, {}))
 
-        # cannot write to a mutable file by uri when no-write permissions are specified
-        d.addCallback(lambda ign:
-            self.shouldFailWithSFTPError(sftp.FX_PERMISSION_DENIED, "openFile mutable uri permissions:0444 WRITE denied",
-                                         self.handler.openFile, "uri/"+self.mutable_uri, sftp.FXF_WRITE,
-                                         {'permissions': 0444}))
-
         # cannot create a file with the EXCL flag if it already exists
         d.addCallback(lambda ign:
             self.shouldFailWithSFTPError(sftp.FX_FAILURE, "openFile small WRITE|CREAT|EXCL failure",
@@ -696,6 +690,16 @@ class Handler(GridTestMixin, ShouldFailMixin, ReallyEqualMixin, unittest.TestCas
         d.addCallback(lambda node: download_to_data(node))
         d.addCallback(lambda data: self.failUnlessReallyEqual(data, "01234"))
 
+        # test WRITE | TRUNC with permissions: 0
+        d.addCallback(lambda ign:
+                      self.handler.openFile("newfile", sftp.FXF_WRITE | sftp.FXF_TRUNC, {'permissions': 0}))
+        d.addCallback(_write_trunc)
+        d.addCallback(lambda ign: self.root.get(u"newfile"))
+        d.addCallback(lambda node: download_to_data(node))
+        d.addCallback(lambda data: self.failUnlessReallyEqual(data, "01234"))
+        d.addCallback(lambda ign: self.root.get_metadata_for(u"newfile"))
+        d.addCallback(lambda metadata: self.failIf(metadata.get('no-write', False), metadata))
+
         # test EXCL flag
         d.addCallback(lambda ign:
                       self.handler.openFile("excl", sftp.FXF_WRITE | sftp.FXF_CREAT |
@@ -824,6 +828,14 @@ class Handler(GridTestMixin, ShouldFailMixin, ReallyEqualMixin, unittest.TestCas
         d.addCallback(_check_same_file)
         d.addCallback(lambda data: self.failUnlessReallyEqual(data, "mutable new! contents"))
 
+        # ... and with permissions, which should be ignored
+        d.addCallback(lambda ign:
+                      self.handler.openFile("mutable", sftp.FXF_WRITE, {'permissions': 0}))
+        d.addCallback(_write_mutable)
+        d.addCallback(lambda ign: self.root.get(u"mutable"))
+        d.addCallback(_check_same_file)
+        d.addCallback(lambda data: self.failUnlessReallyEqual(data, "mutable new! contents"))
+
         # test READ | WRITE without CREAT or TRUNC
         d.addCallback(lambda ign:
                       self.handler.openFile("small", sftp.FXF_READ | sftp.FXF_WRITE, {}))