Miscellaneous documentation, test, and code formatting tweaks.

This commit is contained in:
david-sarah
2010-01-26 23:03:09 -08:00
parent 6057bc02cc
commit 56c00cb381
7 changed files with 88 additions and 51 deletions

View File

@ -906,7 +906,7 @@ def stat_to_dict(statobj, fields=None):
class TStat(fuse.Stat): class TStat(fuse.Stat):
# in fuse 0.2, these are set by fuse.Stat.__init__ # in fuse 0.2, these are set by fuse.Stat.__init__
# in fuse 0.2-pre3 (hardy) they are not. badness unsues if they're missing # in fuse 0.2-pre3 (hardy) they are not. badness ensues if they're missing
st_mode = None st_mode = None
st_ino = 0 st_ino = 0
st_dev = 0 st_dev = 0
@ -1019,6 +1019,7 @@ class Directory(object):
def get_uri(self): def get_uri(self):
return self.rw_uri or self.ro_uri return self.rw_uri or self.ro_uri
# TODO: rename to 'is_writeable', or switch sense to 'is_readonly', for consistency with Tahoe code
def writable(self): def writable(self):
return self.rw_uri and self.rw_uri != self.ro_uri return self.rw_uri and self.rw_uri != self.ro_uri

View File

@ -70,20 +70,20 @@ delete), because otherwise a regular web browser has no way to accomplish
these tasks. In general, everything that can be done with a PUT or DELETE can these tasks. In general, everything that can be done with a PUT or DELETE can
also be done with a POST. also be done with a POST.
Tahoe's web API is designed for two different consumers. The first is a Tahoe's web API is designed for two different kinds of consumer. The first is
program that needs to manipulate the virtual file system. Such programs are a program that needs to manipulate the virtual file system. Such programs are
expected to use the RESTful interface described above. The second is a human expected to use the RESTful interface described above. The second is a human
using a standard web browser to work with the filesystem. This user is given using a standard web browser to work with the filesystem. This user is given
a series of HTML pages with links to download files, and forms that use POST a series of HTML pages with links to download files, and forms that use POST
actions to upload, rename, and delete files. actions to upload, rename, and delete files.
When an error occurs, the HTTP response code will be set to an appropriate When an error occurs, the HTTP response code will be set to an appropriate
400-series code (like 404 for an unknown childname, or 400 Gone when a file 400-series code (like 404 Not Found for an unknown childname, or 400 Bad Request
is unrecoverable due to insufficient shares), and the HTTP response body will when the parameters to a webapi operation are invalid), and the HTTP response
usually contain a few lines of explanation as to the cause of the error and body will usually contain a few lines of explanation as to the cause of the
possible responses. Unusual exceptions may result in a 500 Internal Server error and possible responses. Unusual exceptions may result in a
Error as a catch-all, with a default response body will contain a 500 Internal Server Error as a catch-all, with a default response body containing
Nevow-generated HTML-ized representation of the Python exception stack trace a Nevow-generated HTML-ized representation of the Python exception stack trace
that caused the problem. CLI programs which want to copy the response body to that caused the problem. CLI programs which want to copy the response body to
stderr should provide an "Accept: text/plain" header to their requests to get stderr should provide an "Accept: text/plain" header to their requests to get
a plain text stack trace instead. If the Accept header contains */*, or a plain text stack trace instead. If the Accept header contains */*, or
@ -108,9 +108,9 @@ give read-only access to a directory. Finally there are also mutable file
read- and write- caps, which start with "URI:SSK", and give access to mutable read- and write- caps, which start with "URI:SSK", and give access to mutable
files. files.
(later versions of Tahoe will make these strings shorter, and will remove the (Later versions of Tahoe will make these strings shorter, and will remove the
unfortunate colons, which must be escaped when these caps are embedded in unfortunate colons, which must be escaped when these caps are embedded in
URLs). URLs.)
To refer to any Tahoe object through the web API, you simply need to combine To refer to any Tahoe object through the web API, you simply need to combine
a prefix (which indicates the HTTP server to use) with the cap (which a prefix (which indicates the HTTP server to use) with the cap (which
@ -121,7 +121,7 @@ listening on this port:
http://127.0.0.1:3456/uri/ + $CAP http://127.0.0.1:3456/uri/ + $CAP
So, to access the directory named above (which happens to be the So, to access the directory named above (which happens to be the
publically-writable sample directory on the Tahoe test grid, described at publically-writeable sample directory on the Tahoe test grid, described at
http://allmydata.org/trac/tahoe/wiki/TestGrid), the URL would be: http://allmydata.org/trac/tahoe/wiki/TestGrid), the URL would be:
http://127.0.0.1:3456/uri/URI%3ADIR2%3Adjrdkfawoqihigoett4g6auz6a%3Ajx5mplfpwexnoqff7y5e4zjus4lidm76dcuarpct7cckorh2dpgq/ http://127.0.0.1:3456/uri/URI%3ADIR2%3Adjrdkfawoqihigoett4g6auz6a%3Ajx5mplfpwexnoqff7y5e4zjus4lidm76dcuarpct7cckorh2dpgq/
@ -199,9 +199,9 @@ contain unicode filenames, and cannot contain binary strings that are not
representable as such. representable as such.
All Tahoe operations that refer to existing files or directories must include All Tahoe operations that refer to existing files or directories must include
a suitable read- or write- cap in the URL: the wapi server won't add one a suitable read- or write- cap in the URL: the webapi server won't add one
for you. If you don't know the cap, you can't access the file. This allows for you. If you don't know the cap, you can't access the file. This allows
the security properties of Tahoe caps to be extended across the wapi the security properties of Tahoe caps to be extended across the webapi
interface. interface.
== Slow Operations, Progress, and Cancelling == == Slow Operations, Progress, and Cancelling ==
@ -275,7 +275,7 @@ If a retain-for= argument is not used, the default handle lifetimes are:
since the operation completed) will remain valid for ten minutes. since the operation completed) will remain valid for ten minutes.
Many "slow" operations can begin to use unacceptable amounts of memory when Many "slow" operations can begin to use unacceptable amounts of memory when
operation on large directory structures. The memory usage increases when the operating on large directory structures. The memory usage increases when the
ophandle is polled, as the results must be copied into a JSON string, sent ophandle is polled, as the results must be copied into a JSON string, sent
over the wire, then parsed by a client. So, as an alternative, many "slow" over the wire, then parsed by a client. So, as an alternative, many "slow"
operations have streaming equivalents. These equivalents do not use operation operations have streaming equivalents. These equivalents do not use operation
@ -315,10 +315,10 @@ PUT /uri/$DIRCAP/[SUBDIRS../]FILENAME
retrieve the same contents that were just uploaded. This will create any retrieve the same contents that were just uploaded. This will create any
necessary intermediate subdirectories. necessary intermediate subdirectories.
To use the /uri/$FILECAP form, $FILECAP be a write-cap for a mutable file. To use the /uri/$FILECAP form, $FILECAP must be a write-cap for a mutable file.
In the /uri/$DIRCAP/[SUBDIRS../]FILENAME form, if the target file is a In the /uri/$DIRCAP/[SUBDIRS../]FILENAME form, if the target file is a
writable mutable file, that files contents will be overwritten in-place. If writeable mutable file, that file's contents will be overwritten in-place. If
it is a read-cap for a mutable file, an error will occur. If it is an it is a read-cap for a mutable file, an error will occur. If it is an
immutable file, the old file will be discarded, and a new one will be put in immutable file, the old file will be discarded, and a new one will be put in
its place. its place.
@ -337,7 +337,7 @@ PUT /uri/$DIRCAP/[SUBDIRS../]FILENAME
PUT /uri PUT /uri
This uploads a file, and produces a file-cap for the contents, but does not This uploads a file, and produces a file-cap for the contents, but does not
attach the file into the virtual drive. No directories will be modified by attach the file into the filesystem. No directories will be modified by
this operation. The file-cap is returned as the body of the HTTP response. this operation. The file-cap is returned as the body of the HTTP response.
If "mutable=true" is in the query arguments, the operation will create a If "mutable=true" is in the query arguments, the operation will create a
@ -351,7 +351,7 @@ PUT /uri?t=mkdir
Create a new empty directory and return its write-cap as the HTTP response Create a new empty directory and return its write-cap as the HTTP response
body. This does not make the newly created directory visible from the body. This does not make the newly created directory visible from the
virtual drive. The "PUT" operation is provided for backwards compatibility: filesystem. The "PUT" operation is provided for backwards compatibility:
new code should use POST. new code should use POST.
POST /uri?t=mkdir-with-children POST /uri?t=mkdir-with-children
@ -392,7 +392,7 @@ POST /uri?t=mkdir-with-children
"linkcrtime": 1202777696.7564139, "linkcrtime": 1202777696.7564139,
"linkmotime": 1202777696.7564139, "linkmotime": 1202777696.7564139,
} } } ] } } } ]
} }
For forward-compatibility, a mutable directory can also contain caps in For forward-compatibility, a mutable directory can also contain caps in
a format that is unknown to the webapi server. When such caps are retrieved a format that is unknown to the webapi server. When such caps are retrieved
@ -522,7 +522,7 @@ POST /uri/$DIRCAP/[SUBDIRS../]?t=mkdir-with-children&name=NAME
the immediate parent directory already has a a child named NAME. the immediate parent directory already has a a child named NAME.
Note that the name= argument must be passed as a queryarg, because the POST Note that the name= argument must be passed as a queryarg, because the POST
request body is used for the initial children JSON. request body is used for the initial children JSON.
POST /uri/$DIRCAP/[SUBDIRS../]?t=mkdir-immutable&name=NAME POST /uri/$DIRCAP/[SUBDIRS../]?t=mkdir-immutable&name=NAME
@ -625,7 +625,7 @@ GET /uri/$DIRCAP/[SUBDIRS../]FILENAME?t=json
Then the rw_uri field will be present in the information about a directory Then the rw_uri field will be present in the information about a directory
if and only if you have read-write access to that directory. The verify_uri if and only if you have read-write access to that directory. The verify_uri
field will be presend if and only if the object has a verify-cap field will be present if and only if the object has a verify-cap
(non-distributed LIT files do not have verify-caps). (non-distributed LIT files do not have verify-caps).
==== About the metadata ==== ==== About the metadata ====
@ -701,11 +701,11 @@ GET /uri/$DIRCAP/[SUBDIRS../]FILENAME?t=json
link points. link points.
4. Also, quite apart from Tahoe, you might be confused about the meaning of 4. Also, quite apart from Tahoe, you might be confused about the meaning of
the 'ctime' in unix local filesystems, which people sometimes think means the 'ctime' in UNIX local filesystems, which people sometimes think means
file creation time, but which actually means, in unix local filesystems, the file creation time, but which actually means, in UNIX local filesystems, the
most recent time that the file contents or the file metadata (such as owner, most recent time that the file contents or the file metadata (such as owner,
permission bits, extended attributes, etc.) has changed. Note that although permission bits, extended attributes, etc.) has changed. Note that although
'ctime' does not mean file creation time in Unix, it does mean link creation 'ctime' does not mean file creation time in UNIX, it does mean link creation
time in Tahoe, unless the "tahoe backup" command has been used on that link, time in Tahoe, unless the "tahoe backup" command has been used on that link,
in which case it means something about the local filesystem file which in which case it means something about the local filesystem file which
corresponds to the Tahoe file which is pointed at by the link. It means corresponds to the Tahoe file which is pointed at by the link. It means
@ -713,7 +713,6 @@ GET /uri/$DIRCAP/[SUBDIRS../]FILENAME?t=json
Windows) or file-contents-or-metadata-update-time of the local file (if Windows) or file-contents-or-metadata-update-time of the local file (if
"tahoe backup" was run on a different operating system). "tahoe backup" was run on a different operating system).
=== Attaching an existing File or Directory by its read- or write- cap === === Attaching an existing File or Directory by its read- or write- cap ===
PUT /uri/$DIRCAP/[SUBDIRS../]CHILDNAME?t=uri PUT /uri/$DIRCAP/[SUBDIRS../]CHILDNAME?t=uri
@ -737,10 +736,15 @@ PUT /uri/$DIRCAP/[SUBDIRS../]CHILDNAME?t=uri
if there is already an object at the given location, rather than if there is already an object at the given location, rather than
overwriting the existing object. To allow the operation to overwrite a overwriting the existing object. To allow the operation to overwrite a
file, but return an error when trying to overwrite a directory, use file, but return an error when trying to overwrite a directory, use
"replace=only-files" (this behavior is closer to the traditional unix "mv" "replace=only-files" (this behavior is closer to the traditional UNIX "mv"
command). Note that "true", "t", and "1" are all synonyms for "True", and command). Note that "true", "t", and "1" are all synonyms for "True", and
"false", "f", and "0" are synonyms for "False", and the parameter is "false", "f", and "0" are synonyms for "False", and the parameter is
case-insensitive. case-insensitive.
Note that this operation does not take its child cap in the form of
separate "rw_uri" and "ro_uri" fields. Therefore, it cannot accept a
child cap in a format unknown to the webapi server, because the server
is not able to attenuate an unknown write cap to a read cap.
=== Adding multiple files or directories to a parent directory at once === === Adding multiple files or directories to a parent directory at once ===
@ -802,7 +806,7 @@ DELETE /uri/$DIRCAP/[SUBDIRS../]CHILDNAME
The object will only become completely unreachable once 1: there are no The object will only become completely unreachable once 1: there are no
reachable directories that reference it, and 2: nobody is holding a read- reachable directories that reference it, and 2: nobody is holding a read-
or write- cap to the object. (This behavior is very similar to the way or write- cap to the object. (This behavior is very similar to the way
hardlinks and anonymous files work in traditional unix filesystems). hardlinks and anonymous files work in traditional UNIX filesystems).
This operation will not modify more than a single directory. Intermediate This operation will not modify more than a single directory. Intermediate
directories which were implicitly created by PUT or POST methods will *not* directories which were implicitly created by PUT or POST methods will *not*
@ -931,7 +935,7 @@ POST /uri/$DIRCAP/[SUBDIRS../]?t=mkdir&name=CHILDNAME
POST /uri?t=upload POST /uri?t=upload
This uploads a file, and produces a file-cap for the contents, but does not This uploads a file, and produces a file-cap for the contents, but does not
attach the file into the virtual drive. No directories will be modified by attach the file into the filesystem. No directories will be modified by
this operation. this operation.
The file must be provided as the "file" field of an HTML encoded form body, The file must be provided as the "file" field of an HTML encoded form body,
@ -1677,7 +1681,7 @@ GET / (introducer status)
== Static Files in /public_html == == Static Files in /public_html ==
The wapi server will take any request for a URL that starts with /static The webapi server will take any request for a URL that starts with /static
and serve it from a configurable directory which defaults to and serve it from a configurable directory which defaults to
$BASEDIR/public_html . This is configured by setting the "[node]web.static" $BASEDIR/public_html . This is configured by setting the "[node]web.static"
value in $BASEDIR/tahoe.cfg . If this is left at the default value of value in $BASEDIR/tahoe.cfg . If this is left at the default value of
@ -1685,10 +1689,10 @@ value in $BASEDIR/tahoe.cfg . If this is left at the default value of
served with the contents of the file $BASEDIR/public_html/subdir/foo.html . served with the contents of the file $BASEDIR/public_html/subdir/foo.html .
This can be useful to serve a javascript application which provides a This can be useful to serve a javascript application which provides a
prettier front-end to the rest of the Tahoe wapi. prettier front-end to the rest of the Tahoe webapi.
== safety and security issues -- names vs. URIs == == Safety and security issues -- names vs. URIs ==
Summary: use explicit file- and dir- caps whenever possible, to reduce the Summary: use explicit file- and dir- caps whenever possible, to reduce the
potential for surprises when the filesystem structure is changed. potential for surprises when the filesystem structure is changed.
@ -1774,7 +1778,7 @@ Coordination Directive.
Tahoe nodes implement internal serialization to make sure that a single Tahoe Tahoe nodes implement internal serialization to make sure that a single Tahoe
node cannot conflict with itself. For example, it is safe to issue two node cannot conflict with itself. For example, it is safe to issue two
directory modification requests to a single tahoe node's wapi server at the directory modification requests to a single tahoe node's webapi server at the
same time, because the Tahoe node will internally delay one of them until same time, because the Tahoe node will internally delay one of them until
after the other has finished being applied. (This feature was introduced in after the other has finished being applied. (This feature was introduced in
Tahoe-1.1; back with Tahoe-1.0 the web client was responsible for serializing Tahoe-1.1; back with Tahoe-1.0 the web client was responsible for serializing

View File

@ -1,7 +1,7 @@
ANNOUNCING Tahoe, the Lofty-Atmospheric Filesystem, v1.5 ANNOUNCING Tahoe, the Lofty-Atmospheric Filesystem, v1.6
The Tahoe-LAFS team is pleased to announce the immediate The Tahoe-LAFS team is pleased to announce the immediate
availability of version 1.5 of Tahoe, the Lofty Atmospheric availability of version 1.6 of Tahoe, the Lofty Atmospheric
File System. File System.
Tahoe-LAFS is the first cloud storage technology which offers Tahoe-LAFS is the first cloud storage technology which offers
@ -29,15 +29,20 @@ and more. See the Related Projects page on the wiki [3].
COMPATIBILITY COMPATIBILITY
Version 1.5 is fully compatible with the version 1 series of Version 1.6 is fully compatible with the version 1 series of
Tahoe-LAFS. Files written by v1.5 clients can be read by Tahoe-LAFS. Files written by v1.6 clients can be read by
clients of all versions back to v1.0. v1.5 clients can read clients of all versions back to v1.0. v1.6 clients can read
files produced by clients of all versions since v1.0. v1.5 files produced by clients of all versions since v1.0. v1.6
servers can serve clients of all versions back to v1.0 and v1.5 servers can serve clients of all versions back to v1.0 and v1.6
clients can use servers of all versions back to v1.0. clients can use servers of all versions back to v1.0.
This is the sixth release in the version 1 series. The version In addition, version 1.6 improves forward-compatibility with
1 series of Tahoe-LAFS will be actively supported and planned future cap formats, allowing updates to a directory
containing both current and future caps, without loss of
information.
This is the seventh major release in the version 1 series. The
version 1 series of Tahoe-LAFS will be actively supported and
maintained for the forseeable future, and future versions of maintained for the forseeable future, and future versions of
Tahoe-LAFS will retain the ability to read and write files Tahoe-LAFS will retain the ability to read and write files
compatible with Tahoe-LAFS v1. compatible with Tahoe-LAFS v1.

View File

@ -23,6 +23,11 @@ from allmydata.uri import LiteralFileURI, from_string, wrap_dirnode_cap
from pycryptopp.cipher.aes import AES from pycryptopp.cipher.aes import AES
from allmydata.util.dictutil import AuxValueDict from allmydata.util.dictutil import AuxValueDict
# TODO: {Deleter,MetadataSetter,Adder}.modify all start by unpacking the
# contents and end by repacking them. It might be better to apply them to
# the unpacked contents.
class Deleter: class Deleter:
def __init__(self, node, name, must_exist=True): def __init__(self, node, name, must_exist=True):
self.node = node self.node = node

View File

@ -426,6 +426,7 @@ class IURI(Interface):
"""Return True if the data can be modified by *somebody* (perhaps """Return True if the data can be modified by *somebody* (perhaps
someone who has a more powerful URI than this one).""" someone who has a more powerful URI than this one)."""
# TODO: rename to get_read_cap()
def get_readonly(): def get_readonly():
"""Return another IURI instance, which represents a read-only form of """Return another IURI instance, which represents a read-only form of
this one. If is_readonly() is True, this returns self.""" this one. If is_readonly() is True, this returns self."""

View File

@ -700,11 +700,15 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, unittest.TestCase):
self.PUT, base, "") self.PUT, base, "")
return d return d
# TODO: version of this with a Unicode filename
def test_GET_FILEURL_save(self): def test_GET_FILEURL_save(self):
d = self.GET(self.public_url + "/foo/bar.txt?filename=bar.txt&save=true") d = self.GET(self.public_url + "/foo/bar.txt?filename=bar.txt&save=true",
# TODO: look at the headers, expect a Content-Disposition: attachment return_response=True)
# header. def _got((res, statuscode, headers)):
d.addCallback(self.failUnlessIsBarDotTxt) content_disposition = headers["content-disposition"][0]
self.failUnless(content_disposition == 'attachment; filename="bar.txt"', content_disposition)
self.failUnlessIsBarDotTxt(res)
d.addCallback(_got)
return d return d
def test_GET_FILEURL_missing(self): def test_GET_FILEURL_missing(self):
@ -2258,7 +2262,12 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, unittest.TestCase):
# Fetch the welcome page. # Fetch the welcome page.
d = self.GET("/") d = self.GET("/")
def _after_get_welcome_page(res): def _after_get_welcome_page(res):
MKDIR_BUTTON_RE=re.compile('<form action="([^"]*)" method="post".*?<input type="hidden" name="t" value="([^"]*)" /><input type="hidden" name="([^"]*)" value="([^"]*)" /><input type="submit" value="Create a directory" />', re.I) MKDIR_BUTTON_RE = re.compile(
'<form action="([^"]*)" method="post".*?'
'<input type="hidden" name="t" value="([^"]*)" />'
'<input type="hidden" name="([^"]*)" value="([^"]*)" />'
'<input type="submit" value="Create a directory" />',
re.I)
mo = MKDIR_BUTTON_RE.search(res) mo = MKDIR_BUTTON_RE.search(res)
formaction = mo.group(1) formaction = mo.group(1)
formt = mo.group(2) formt = mo.group(2)

View File

@ -11,9 +11,16 @@ from allmydata.interfaces import IURI, IDirnodeURI, IFileURI, IImmutableFileURI,
class BadURIError(CapConstraintError): class BadURIError(CapConstraintError):
pass pass
# the URI shall be an ascii representation of the file. It shall contain # The URI shall be an ASCII representation of a reference to the file/directory.
# enough information to retrieve and validate the contents. It shall be # It shall contain enough information to retrieve and validate the contents.
# expressed in a limited character set (namely [TODO]). # It shall be expressed in a limited character set (currently base32 plus ':' and
# capital letters, but future URIs might use a larger charset).
# TODO:
# - rename all of the *URI classes/interfaces to *Cap
# - make variable and method names consistently use _uri for an URI string,
# and _cap for a Cap object (decoded URI)
# - remove the human_encoding methods?
BASE32STR_128bits = '(%s{25}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_3bits) BASE32STR_128bits = '(%s{25}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_3bits)
BASE32STR_256bits = '(%s{51}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_1bits) BASE32STR_256bits = '(%s{51}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_1bits)
@ -22,30 +29,35 @@ SEP='(?::|%3A)'
NUMBER='([0-9]+)' NUMBER='([0-9]+)'
NUMBER_IGNORE='(?:[0-9]+)' NUMBER_IGNORE='(?:[0-9]+)'
# URIs (soon to be renamed "caps") are always allowed to come with a leading # "human-encoded" URIs are allowed to come with a leading
# 'http://127.0.0.1:(8123|3456)/uri/' that will be ignored. # 'http://127.0.0.1:(8123|3456)/uri/' that will be ignored.
# Note that nothing in the Tahoe code currently uses the human encoding.
OPTIONALHTTPLEAD=r'(?:https?://(?:[^:/]+)(?::%s)?/uri/)?' % NUMBER_IGNORE OPTIONALHTTPLEAD=r'(?:https?://(?:[^:/]+)(?::%s)?/uri/)?' % NUMBER_IGNORE
class _BaseURI: class _BaseURI:
def __hash__(self): def __hash__(self):
return self.to_string().__hash__() return self.to_string().__hash__()
def __eq__(self, them): def __eq__(self, them):
if isinstance(them, _BaseURI): if isinstance(them, _BaseURI):
return self.to_string() == them.to_string() return self.to_string() == them.to_string()
else: else:
return False return False
def __ne__(self, them): def __ne__(self, them):
if isinstance(them, _BaseURI): if isinstance(them, _BaseURI):
return self.to_string() != them.to_string() return self.to_string() != them.to_string()
else: else:
return True return True
def to_human_encoding(self): def to_human_encoding(self):
return 'http://127.0.0.1:3456/uri/'+self.to_string() return 'http://127.0.0.1:3456/uri/'+self.to_string()
def get_storage_index(self): def get_storage_index(self):
return self.storage_index return self.storage_index
class CHKFileURI(_BaseURI): class CHKFileURI(_BaseURI):
implements(IURI, IImmutableFileURI) implements(IURI, IImmutableFileURI)