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
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
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)
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
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())
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)
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
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"}))
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:
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"))
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)
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)