| 1 | diff -rN -u old-timestamps/docs/frontends/webapi.txt new-timestamps/docs/frontends/webapi.txt |
|---|
| 2 | --- old-timestamps/docs/frontends/webapi.txt 2009-04-08 21:17:06.000000000 -0600 |
|---|
| 3 | +++ new-timestamps/docs/frontends/webapi.txt 2009-04-08 21:17:08.000000000 -0600 |
|---|
| 4 | @@ -381,28 +381,44 @@ |
|---|
| 5 | GET /uri/$DIRCAP/[SUBDIRS../]FILENAME?t=json |
|---|
| 6 | |
|---|
| 7 | This returns a machine-parseable JSON-encoded description of the given |
|---|
| 8 | - object. The JSON always contains a list, and the first element of the list |
|---|
| 9 | - is always a flag that indicates whether the referenced object is a file or a |
|---|
| 10 | - directory. If it is a file, then the information includes file size and URI, |
|---|
| 11 | - like this: |
|---|
| 12 | + object. The JSON always contains a list, and the first element of the list is |
|---|
| 13 | + always a flag that indicates whether the referenced object is a file or a |
|---|
| 14 | + directory. If it is a capability to a file, then the information includes |
|---|
| 15 | + file size and URI, like this: |
|---|
| 16 | |
|---|
| 17 | GET /uri/$FILECAP?t=json : |
|---|
| 18 | + |
|---|
| 19 | + [ "filenode", { "ro_uri": file_uri, |
|---|
| 20 | + "verify_uri": verify_uri, |
|---|
| 21 | + "size": bytes, |
|---|
| 22 | + "mutable": false, |
|---|
| 23 | + } ] |
|---|
| 24 | + |
|---|
| 25 | + If it is a capability to a directory followed by a path from that directory |
|---|
| 26 | + to a file, then the information also includes metadata from the link to the |
|---|
| 27 | + file in the parent directory, like this: |
|---|
| 28 | + |
|---|
| 29 | GET /uri/$DIRCAP/[SUBDIRS../]FILENAME?t=json : |
|---|
| 30 | |
|---|
| 31 | [ "filenode", { "ro_uri": file_uri, |
|---|
| 32 | "verify_uri": verify_uri, |
|---|
| 33 | "size": bytes, |
|---|
| 34 | "mutable": false, |
|---|
| 35 | - "metadata": {"ctime": 1202777696.7564139, |
|---|
| 36 | - "mtime": 1202777696.7564139 |
|---|
| 37 | + "metadata": { |
|---|
| 38 | + "ctime": 1202777696.7564139, |
|---|
| 39 | + "mtime": 1202777696.7564139, |
|---|
| 40 | + "__sys": { |
|---|
| 41 | + "linkcrtime": 1202777696.7564139, |
|---|
| 42 | + "linkmotime": 1202777696.7564139, |
|---|
| 43 | } |
|---|
| 44 | + } |
|---|
| 45 | } ] |
|---|
| 46 | |
|---|
| 47 | If it is a directory, then it includes information about the children of |
|---|
| 48 | this directory, as a mapping from child name to a set of data about the |
|---|
| 49 | child (the same data that would appear in a corresponding GET?t=json of the |
|---|
| 50 | child itself). The child entries also include metadata about each child, |
|---|
| 51 | - including creation- and modification- timestamps. The output looks like |
|---|
| 52 | + including link-creation- and link-change- timestamps. The output looks like |
|---|
| 53 | this: |
|---|
| 54 | |
|---|
| 55 | GET /uri/$DIRCAP?t=json : |
|---|
| 56 | @@ -418,13 +434,21 @@ |
|---|
| 57 | "metadata": { |
|---|
| 58 | "ctime": 1202777696.7564139, |
|---|
| 59 | "mtime": 1202777696.7564139 |
|---|
| 60 | + "__sys": { |
|---|
| 61 | + "linkcrtime": 1202777696.7564139, |
|---|
| 62 | + "linkmotime": 1202777696.7564139, |
|---|
| 63 | } |
|---|
| 64 | + } |
|---|
| 65 | } ], |
|---|
| 66 | "subdir": [ "dirnode", { "rw_uri": rwuri, |
|---|
| 67 | "ro_uri": rouri, |
|---|
| 68 | "metadata": { |
|---|
| 69 | "ctime": 1202778102.7589991, |
|---|
| 70 | "mtime": 1202778111.2160511, |
|---|
| 71 | + "__sys": { |
|---|
| 72 | + "linkcrtime": 1202777696.7564139, |
|---|
| 73 | + "linkmotime": 1202777696.7564139, |
|---|
| 74 | + } |
|---|
| 75 | } |
|---|
| 76 | } ] |
|---|
| 77 | } } ] |
|---|
| 78 | diff -rN -u old-timestamps/docs/specifications/dirnodes.txt new-timestamps/docs/specifications/dirnodes.txt |
|---|
| 79 | --- old-timestamps/docs/specifications/dirnodes.txt 2009-04-08 21:17:06.000000000 -0600 |
|---|
| 80 | +++ new-timestamps/docs/specifications/dirnodes.txt 2009-04-08 21:17:08.000000000 -0600 |
|---|
| 81 | @@ -176,30 +176,29 @@ |
|---|
| 82 | netstring(cap) = 4+len(cap) |
|---|
| 83 | encrypted(cap) = 16+cap+32 |
|---|
| 84 | JSON({}) = 2 |
|---|
| 85 | - JSON({ctime=float,mtime=float}): 57 |
|---|
| 86 | - netstring(metadata) = 4+57 = 61 |
|---|
| 87 | + JSON({ctime=float,mtime=float,'__sys':{linkcrtime=float,linkmotime=float}}): 137 |
|---|
| 88 | + netstring(metadata) = 4+137 = 141 |
|---|
| 89 | |
|---|
| 90 | so a CHK entry is: |
|---|
| 91 | - 5+ 4+len(name) + 4+97 + 5+16+97+32 + 4+57 |
|---|
| 92 | -And a 15-byte filename gives a 336-byte entry. When the entry points at a |
|---|
| 93 | + 5+ 4+len(name) + 4+97 + 5+16+97+32 + 4+137 |
|---|
| 94 | +And a 15-byte filename gives a 416-byte entry. When the entry points at a |
|---|
| 95 | subdirectory instead of a file, the entry is a little bit smaller. So an |
|---|
| 96 | -empty directory uses 0 bytes, a directory with one child uses about 336 |
|---|
| 97 | -bytes, a directory with two children uses about 672, etc. |
|---|
| 98 | +empty directory uses 0 bytes, a directory with one child uses about 416 |
|---|
| 99 | +bytes, a directory with two children uses about 832, etc. |
|---|
| 100 | |
|---|
| 101 | When the dirnode data is encoding using our default 3-of-10, that means we |
|---|
| 102 | -get 112ish bytes of data in each share per child. |
|---|
| 103 | +get 139ish bytes of data in each share per child. |
|---|
| 104 | |
|---|
| 105 | The pubkey, signature, and hashes form the first 935ish bytes of the |
|---|
| 106 | container, then comes our data, then about 1216 bytes of encprivkey. So if we |
|---|
| 107 | read the first: |
|---|
| 108 | |
|---|
| 109 | 1kB: we get 65bytes of dirnode data : only empty directories |
|---|
| 110 | - 1kiB: 89bytes of dirnode data : maybe one short-named subdir |
|---|
| 111 | - 2kB: 1065bytes: about 9 entries |
|---|
| 112 | - 3kB: 2065bytes: about 18 entries, or 7.5 entries plus the encprivkey |
|---|
| 113 | - 4kB: 3065bytes: about 27 entries, or about 16.5 plus the encprivkey |
|---|
| 114 | + 2kB: 1065bytes: about 8 |
|---|
| 115 | + 3kB: 2065bytes: about 15 entries, or 6 entries plus the encprivkey |
|---|
| 116 | + 4kB: 3065bytes: about 22 entries, or about 13 plus the encprivkey |
|---|
| 117 | |
|---|
| 118 | -So we've written the code to do an initial read of 2kB from each share when |
|---|
| 119 | +So we've written the code to do an initial read of 4kB from each share when |
|---|
| 120 | we read the mutable file, which should give good performance (one RTT) for |
|---|
| 121 | small directories. |
|---|
| 122 | |
|---|
| 123 | diff -rN -u old-timestamps/src/allmydata/dirnode.py new-timestamps/src/allmydata/dirnode.py |
|---|
| 124 | --- old-timestamps/src/allmydata/dirnode.py 2009-04-08 21:17:06.000000000 -0600 |
|---|
| 125 | +++ new-timestamps/src/allmydata/dirnode.py 2009-04-08 21:17:08.000000000 -0600 |
|---|
| 126 | @@ -83,15 +83,41 @@ |
|---|
| 127 | metadata = children[name][1].copy() |
|---|
| 128 | else: |
|---|
| 129 | metadata = {"ctime": now, |
|---|
| 130 | - "mtime": now} |
|---|
| 131 | - if new_metadata is None: |
|---|
| 132 | - # update timestamps |
|---|
| 133 | + "mtime": now, |
|---|
| 134 | + "__sys": { |
|---|
| 135 | + "linkcrtime": now, |
|---|
| 136 | + "linkmotime": now, |
|---|
| 137 | + } |
|---|
| 138 | + } |
|---|
| 139 | + |
|---|
| 140 | + if new_metadata is not None: |
|---|
| 141 | + # Overwrite all metadata. |
|---|
| 142 | + newmd = new_metadata.copy() |
|---|
| 143 | + |
|---|
| 144 | + # Except '__sys'. |
|---|
| 145 | + if newmd.has_key('__sys'): |
|---|
| 146 | + del newmd['__sys'] |
|---|
| 147 | + if metadata.has_key('__sys'): |
|---|
| 148 | + newmd['__sys'] = metadata['__sys'] |
|---|
| 149 | + |
|---|
| 150 | + metadata = newmd |
|---|
| 151 | + else: |
|---|
| 152 | + # For backwards compatibility with Tahoe < 1.4.0: |
|---|
| 153 | if "ctime" not in metadata: |
|---|
| 154 | metadata["ctime"] = now |
|---|
| 155 | metadata["mtime"] = now |
|---|
| 156 | - else: |
|---|
| 157 | - # just replace it |
|---|
| 158 | - metadata = new_metadata.copy() |
|---|
| 159 | + |
|---|
| 160 | + # update timestamps |
|---|
| 161 | + sysmd = metadata.get('__sys', {}) |
|---|
| 162 | + if not 'linkcrtime' in sysmd: |
|---|
| 163 | + if "ctime" in metadata: |
|---|
| 164 | + # In Tahoe < 1.4.0 we used the word "ctime" to mean what Tahoe >= 1.4.0 |
|---|
| 165 | + # calls "linkcrtime". |
|---|
| 166 | + sysmd["linkcrtime"] = metadata["ctime"] |
|---|
| 167 | + else: |
|---|
| 168 | + sysmd["linkcrtime"] = now |
|---|
| 169 | + sysmd["linkmotime"] = now |
|---|
| 170 | + |
|---|
| 171 | children[name] = (child, metadata) |
|---|
| 172 | new_contents = self.node._pack_contents(children) |
|---|
| 173 | return new_contents |
|---|
| 174 | diff -rN -u old-timestamps/src/allmydata/mutable/servermap.py new-timestamps/src/allmydata/mutable/servermap.py |
|---|
| 175 | --- old-timestamps/src/allmydata/mutable/servermap.py 2009-04-08 21:17:07.000000000 -0600 |
|---|
| 176 | +++ new-timestamps/src/allmydata/mutable/servermap.py 2009-04-08 21:17:08.000000000 -0600 |
|---|
| 177 | @@ -374,7 +374,7 @@ |
|---|
| 178 | # fixed-size slots so we can retrieve less data. For now, we'll just |
|---|
| 179 | # read 2000 bytes, which also happens to read enough actual data to |
|---|
| 180 | # pre-fetch a 9-entry dirnode. |
|---|
| 181 | - self._read_size = 2000 |
|---|
| 182 | + self._read_size = 4000 |
|---|
| 183 | if mode == MODE_CHECK: |
|---|
| 184 | # we use unpack_prefix_and_signature, so we need 1k |
|---|
| 185 | self._read_size = 1000 |
|---|
| 186 | diff -rN -u old-timestamps/src/allmydata/scripts/tahoe_ls.py new-timestamps/src/allmydata/scripts/tahoe_ls.py |
|---|
| 187 | --- old-timestamps/src/allmydata/scripts/tahoe_ls.py 2009-04-08 21:17:07.000000000 -0600 |
|---|
| 188 | +++ new-timestamps/src/allmydata/scripts/tahoe_ls.py 2009-04-08 21:17:08.000000000 -0600 |
|---|
| 189 | @@ -65,8 +65,20 @@ |
|---|
| 190 | name = unicode(name) |
|---|
| 191 | child = children[name] |
|---|
| 192 | childtype = child[0] |
|---|
| 193 | - ctime = child[1]["metadata"].get("ctime") |
|---|
| 194 | - mtime = child[1]["metadata"].get("mtime") |
|---|
| 195 | + |
|---|
| 196 | + # linkcrtime is not really what unix filesystems mean by "ctime", but |
|---|
| 197 | + # it *is* apparently what many or even most unix programmers and users |
|---|
| 198 | + # think that a unix filesystem means by "ctime"... |
|---|
| 199 | + ctime = child[1].get("metadata", {}).get('__sys', {}).get("linkcrtime") |
|---|
| 200 | + if not ctime: |
|---|
| 201 | + ctime = child[1]["metadata"].get("ctime") |
|---|
| 202 | + |
|---|
| 203 | + # linkmotime is not really what unix filesystems mean by "mtime", |
|---|
| 204 | + # because linkmotime is a property of the link and mtime is a property |
|---|
| 205 | + # of the file contents... |
|---|
| 206 | + mtime = child[1].get("metadata", {}).get('__sys', {}).get("linkmotime") |
|---|
| 207 | + if not mtime: |
|---|
| 208 | + mtime = child[1]["metadata"].get("mtime") |
|---|
| 209 | rw_uri = child[1].get("rw_uri") |
|---|
| 210 | ro_uri = child[1].get("ro_uri") |
|---|
| 211 | if ctime: |
|---|
| 212 | diff -rN -u old-timestamps/src/allmydata/test/test_dirnode.py new-timestamps/src/allmydata/test/test_dirnode.py |
|---|
| 213 | --- old-timestamps/src/allmydata/test/test_dirnode.py 2009-04-08 21:17:07.000000000 -0600 |
|---|
| 214 | +++ new-timestamps/src/allmydata/test/test_dirnode.py 2009-04-08 21:17:08.000000000 -0600 |
|---|
| 215 | @@ -416,7 +416,7 @@ |
|---|
| 216 | d.addCallback(lambda res: n.get_metadata_for(u"child")) |
|---|
| 217 | d.addCallback(lambda metadata: |
|---|
| 218 | self.failUnlessEqual(sorted(metadata.keys()), |
|---|
| 219 | - ["ctime", "mtime"])) |
|---|
| 220 | + ["__sys", "ctime", "mtime"])) |
|---|
| 221 | |
|---|
| 222 | d.addCallback(lambda res: |
|---|
| 223 | self.shouldFail(NoSuchChildError, "gcamap-no", |
|---|
| 224 | diff -rN -u old-timestamps/src/allmydata/util/time_format.py new-timestamps/src/allmydata/util/time_format.py |
|---|
| 225 | --- old-timestamps/src/allmydata/util/time_format.py 2009-04-08 21:17:07.000000000 -0600 |
|---|
| 226 | +++ new-timestamps/src/allmydata/util/time_format.py 2009-04-08 21:17:08.000000000 -0600 |
|---|
| 227 | @@ -19,6 +19,11 @@ |
|---|
| 228 | now = t() |
|---|
| 229 | return datetime.datetime.utcfromtimestamp(now).isoformat(sep) |
|---|
| 230 | |
|---|
| 231 | +def iso_local(now=None, sep='_', t=time.time): |
|---|
| 232 | + if now is None: |
|---|
| 233 | + now = t() |
|---|
| 234 | + return datetime.datetime.fromtimestamp(now).isoformat(sep) |
|---|
| 235 | + |
|---|
| 236 | def iso_utc_time_to_seconds(isotime, _conversion_re=re.compile(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})[T_ ](?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})(?P<subsecond>\.\d+)?")): |
|---|
| 237 | """ |
|---|
| 238 | The inverse of iso_utc(). |
|---|
| 239 | diff -rN -u old-timestamps/src/allmydata/web/directory.py new-timestamps/src/allmydata/web/directory.py |
|---|
| 240 | --- old-timestamps/src/allmydata/web/directory.py 2009-04-08 21:17:07.000000000 -0600 |
|---|
| 241 | +++ new-timestamps/src/allmydata/web/directory.py 2009-04-08 21:17:08.000000000 -0600 |
|---|
| 242 | @@ -13,7 +13,7 @@ |
|---|
| 243 | |
|---|
| 244 | from foolscap.eventual import fireEventually |
|---|
| 245 | |
|---|
| 246 | -from allmydata.util import base32 |
|---|
| 247 | +from allmydata.util import base32, time_format |
|---|
| 248 | from allmydata.uri import from_string_dirnode |
|---|
| 249 | from allmydata.interfaces import IDirectoryNode, IFileNode, IMutableFileNode, \ |
|---|
| 250 | ExistingChildError, NoSuchChildError |
|---|
| 251 | @@ -592,16 +592,25 @@ |
|---|
| 252 | ctx.fillSlots("rename", rename) |
|---|
| 253 | |
|---|
| 254 | times = [] |
|---|
| 255 | - TIME_FORMAT = "%H:%M:%S %d-%b-%Y" |
|---|
| 256 | - if "ctime" in metadata: |
|---|
| 257 | - ctime = time.strftime(TIME_FORMAT, |
|---|
| 258 | - time.localtime(metadata["ctime"])) |
|---|
| 259 | - times.append("c: " + ctime) |
|---|
| 260 | - if "mtime" in metadata: |
|---|
| 261 | - mtime = time.strftime(TIME_FORMAT, |
|---|
| 262 | - time.localtime(metadata["mtime"])) |
|---|
| 263 | + linkcrtime = metadata.get('__sys', {}).get("linkcrtime") |
|---|
| 264 | + if linkcrtime is not None: |
|---|
| 265 | + times.append("lcr: " + time_format.iso_local(linkcrtime)) |
|---|
| 266 | + else: |
|---|
| 267 | + # For backwards-compatibility with links last modified by Tahoe < 1.4.0: |
|---|
| 268 | + if "ctime" in metadata: |
|---|
| 269 | + ctime = time_format.iso_local(metadata["ctime"]) |
|---|
| 270 | + times.append("c: " + ctime) |
|---|
| 271 | + linkmotime = metadata.get('__sys', {}).get("linkmotime") |
|---|
| 272 | + if linkmotime is not None: |
|---|
| 273 | if times: |
|---|
| 274 | times.append(T.br()) |
|---|
| 275 | + times.append("lmo: " + time_format.iso_local(linkmotime)) |
|---|
| 276 | + else: |
|---|
| 277 | + # For backwards-compatibility with links last modified by Tahoe < 1.4.0: |
|---|
| 278 | + if "mtime" in metadata: |
|---|
| 279 | + mtime = time_format.iso_local(metadata["mtime"]) |
|---|
| 280 | + if times: |
|---|
| 281 | + times.append(T.br()) |
|---|
| 282 | times.append("m: " + mtime) |
|---|
| 283 | ctx.fillSlots("times", times) |
|---|
| 284 | |
|---|
| 285 | |
|---|