From 29a06457d25bcea84ffc205fd534139d77783a22 Mon Sep 17 00:00:00 2001 From: david-sarah Date: Tue, 1 Jun 2010 20:26:41 -0700 Subject: [PATCH] dirnode.py: fix a bug in the no-write change for Adder, and improve test coverage. Add a 'metadata' argument to create_subdirectory, with documentation. Also update some comments in test_dirnode.py made stale by the ctime/mtime change. --- docs/frontends/webapi.txt | 12 ++++++--- src/allmydata/dirnode.py | 14 +++++----- src/allmydata/interfaces.py | 2 +- src/allmydata/test/test_dirnode.py | 42 ++++++++++++++++++++++++++---- 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/docs/frontends/webapi.txt b/docs/frontends/webapi.txt index 9678e010..9d5afd81 100644 --- a/docs/frontends/webapi.txt +++ b/docs/frontends/webapi.txt @@ -415,9 +415,11 @@ POST /uri?t=mkdir-with-children it is acceptable to set "rw_uri" to that cap and omit "ro_uri". The client must not put a write cap into a "ro_uri" field. - A file may have a "no-write" metadata field that affects how writes to - it are handled via the SFTP frontend; see docs/frontends/FTP-and-SFTP.txt - for details. + The metadata may have a "no-write" field. If this is set to true in the + metadata of a link, it will not be possible to open that link for writing + via the SFTP frontend; see docs/frontends/FTP-and-SFTP.txt for details. + Also, if the "no-write" field is set to true in the metadata of a link to + a mutable child, it will cause the link to be diminished to read-only. Note that the webapi-using client application must not provide the "Content-Type: multipart/form-data" header that usually accompanies HTML @@ -669,7 +671,9 @@ GET /uri/$DIRCAP/[SUBDIRS../]FILENAME?t=json In Tahoe earlier than v1.4.0, only the 'mtime'/'ctime' keys were populated. Starting in Tahoe v1.4.0, the 'linkmotime'/'linkcrtime' keys in the 'tahoe' - sub-dict are also populated. + sub-dict are also populated. However, prior to v1.7.0, a bug caused the + 'tahoe' sub-dict to be deleted by webapi requests in which new metadata + is specified, and not to be added to existing child links that lack it. The reason we added the new values in Tahoe v1.4.0 is that there is a "set_children" API (described below) which you can use to overwrite the diff --git a/src/allmydata/dirnode.py b/src/allmydata/dirnode.py index bfc151a3..6150fb23 100644 --- a/src/allmydata/dirnode.py +++ b/src/allmydata/dirnode.py @@ -122,9 +122,10 @@ class MetadataSetter: raise NoSuchChildError(name) now = time.time() - metadata = update_metadata(children[name][1].copy(), self.metadata, now) child = children[name][0] - if self.create_readonly_node and metadata and metadata.get('no-write', False): + + metadata = update_metadata(children[name][1].copy(), self.metadata, now) + if self.create_readonly_node and metadata.get('no-write', False): child = self.create_readonly_node(child, name) children[name] = (child, metadata) @@ -167,10 +168,11 @@ class Adder: raise ExistingChildError("child '%s' already exists" % name) metadata = children[name][1].copy() - if self.create_readonly_node and metadata and metadata.get('no-write', False): + metadata = update_metadata(metadata, new_metadata, now) + if self.create_readonly_node and metadata.get('no-write', False): child = self.create_readonly_node(child, name) - children[name] = (child, update_metadata(metadata, new_metadata, now)) + children[name] = (child, metadata) new_contents = self.node._pack_contents(children) return new_contents @@ -591,7 +593,7 @@ class DirectoryNode: return d def create_subdirectory(self, name, initial_children={}, overwrite=True, - mutable=True): + mutable=True, metadata=None): assert isinstance(name, unicode) if self.is_readonly(): return defer.fail(NotWriteableError()) @@ -600,7 +602,7 @@ class DirectoryNode: else: d = self._nodemaker.create_immutable_directory(initial_children) def _created(child): - entries = {name: (child, None)} + entries = {name: (child, metadata)} a = Adder(self, entries, overwrite=overwrite, create_readonly_node=self._create_readonly_node) d = self._node.modify(a.modify) diff --git a/src/allmydata/interfaces.py b/src/allmydata/interfaces.py index 0aab97fe..4cfe9c90 100644 --- a/src/allmydata/interfaces.py +++ b/src/allmydata/interfaces.py @@ -985,7 +985,7 @@ class IDirectoryNode(IFilesystemNode): is a file, or if must_be_file is True and the child is a directory, I raise ChildOfWrongTypeError.""" - def create_subdirectory(name, initial_children={}, overwrite=True): + def create_subdirectory(name, initial_children={}, overwrite=True, metadata=None): """I create and attach a directory at the given name. The new directory can be empty, or it can be populated with children according to 'initial_children', which takes a dictionary in the same diff --git a/src/allmydata/test/test_dirnode.py b/src/allmydata/test/test_dirnode.py index 46497ae6..56c9316f 100644 --- a/src/allmydata/test/test_dirnode.py +++ b/src/allmydata/test/test_dirnode.py @@ -775,8 +775,7 @@ class Dirnode(GridTestMixin, unittest.TestCase, d.addCallback(lambda metadata: self.failUnlessEqual(set(metadata.keys()), set(["tahoe", "ctime", "mtime"]))) - # or we can add specific metadata at set_uri() time, which - # overrides the timestamps + # we can also add specific metadata at set_uri() time d.addCallback(lambda res: n.set_uri(u"c4", fake_file_uri, fake_file_uri, {"key": "value"})) @@ -790,7 +789,7 @@ class Dirnode(GridTestMixin, unittest.TestCase, d.addCallback(lambda res: n.delete(u"c4")) # set_node + metadata - # it should be possible to add a child without any metadata + # it should be possible to add a child without any metadata except for timestamps d.addCallback(lambda res: n.set_node(u"d2", n, {})) d.addCallback(lambda res: c.create_dirnode()) d.addCallback(lambda n2: @@ -808,8 +807,7 @@ class Dirnode(GridTestMixin, unittest.TestCase, d.addCallback(lambda metadata: self.failUnlessEqual(set(metadata.keys()), set(["tahoe", "ctime", "mtime"]))) - # or we can add specific metadata at set_node() time, which - # overrides the timestamps + # we can also add specific metadata at set_node() time d.addCallback(lambda res: n.set_node(u"d4", n, {"key": "value"})) d.addCallback(lambda res: n.get_metadata_for(u"d4")) @@ -899,6 +897,11 @@ class Dirnode(GridTestMixin, unittest.TestCase, metadata["tags"] == ["web2.0-compatible"] and "bad" not in metadata["tahoe"], metadata)) + d.addCallback(lambda res: + self.shouldFail(NoSuchChildError, "set_metadata_for-nosuch", "", + n.set_metadata_for, u"nosuch", {})) + + def _start(res): self._start_timestamp = time.time() d.addCallback(_start) @@ -1047,6 +1050,35 @@ class Dirnode(GridTestMixin, unittest.TestCase, self.failUnlessEqual(child.get_uri(), other_file_uri)) + + # Setting the no-write field should diminish a mutable cap to read-only + # (for both files and directories). + + d.addCallback(lambda ign: n.set_uri(u"mutable", other_file_uri, other_file_uri)) + d.addCallback(lambda ign: n.get(u"mutable")) + d.addCallback(lambda mutable: self.failIf(mutable.is_readonly(), mutable)) + d.addCallback(lambda ign: n.set_metadata_for(u"mutable", {"no-write": True})) + d.addCallback(lambda ign: n.get(u"mutable")) + d.addCallback(lambda mutable: self.failUnless(mutable.is_readonly(), mutable)) + d.addCallback(lambda ign: n.set_metadata_for(u"mutable", {"no-write": True})) + d.addCallback(lambda ign: n.get(u"mutable")) + d.addCallback(lambda mutable: self.failUnless(mutable.is_readonly(), mutable)) + + d.addCallback(lambda ign: n.get(u"subdir2")) + d.addCallback(lambda subdir2: self.failIf(subdir2.is_readonly())) + d.addCallback(lambda ign: n.set_metadata_for(u"subdir2", {"no-write": True})) + d.addCallback(lambda ign: n.get(u"subdir2")) + d.addCallback(lambda subdir2: self.failUnless(subdir2.is_readonly(), subdir2)) + + d.addCallback(lambda ign: n.set_uri(u"mutable_ro", other_file_uri, other_file_uri, + metadata={"no-write": True})) + d.addCallback(lambda ign: n.get(u"mutable_ro")) + d.addCallback(lambda mutable_ro: self.failUnless(mutable_ro.is_readonly(), mutable_ro)) + + d.addCallback(lambda ign: n.create_subdirectory(u"subdir_ro", metadata={"no-write": True})) + d.addCallback(lambda ign: n.get(u"subdir_ro")) + d.addCallback(lambda subdir_ro: self.failUnless(subdir_ro.is_readonly(), subdir_ro)) + return d d.addCallback(_then) -- 2.45.2