-
Notifications
You must be signed in to change notification settings - Fork 301
/
upspin.go
860 lines (740 loc) · 36 KB
/
upspin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package upspin // import "upspin.io/upspin"
import (
"crypto/elliptic"
"errors"
"math/big"
"time"
)
// A UserName is just an e-mail address representing a user.
// It is given a unique type so the API is clear.
// The user part may contain an optional suffix after a plus sign.
// Examples: gopher@google.com, me+you@forever.com
type UserName string
// A PathName is just a string representing a full path name.
// It is given a unique type so the API is clear.
// Example: gopher@google.com/burrow/hoard
type PathName string
// Transport identifies the type of access required to reach the data, that is, the
// realm in which the network address within a Location is to be interpreted.
type Transport uint8
const (
// Unassigned denotes a connection to a service that returns an error
// from every method call. It is useful when a component wants to
// guarantee it does not access another service.
// It is also the zero value for Transport.
Unassigned Transport = iota
// InProcess denotes that contents are located in the current process,
// typically in memory.
InProcess
// Remote denotes a connection to a remote server through RPC.
// (Although called remote, the service may be running on the same machine.)
// The Endpoint's NetAddr contains the HTTP address of the server.
Remote
)
// A Location identifies where a piece of data is stored and how to retrieve it.
type Location struct {
// Endpoint identifies the machine or service where the data resides.
Endpoint Endpoint
// Reference is the key that will retrieve the data from the endpoint.
Reference Reference
}
// An Endpoint identifies an instance of a service, encompassing an address
// such as a domain name and information (the Transport) about how to interpret
// that address.
type Endpoint struct {
// Transport specifies how the network address is to be interpreted,
// for instance that it is the URL of an HTTP service.
Transport Transport
// NetAddr returns the (typically) network address of the data.
NetAddr NetAddr
}
// A NetAddr is the network address of service. It is interpreted by Dialer's
// Dial method to connect to the service.
type NetAddr string
// A Reference is the string identifying an item in a StoreServer. It must
// be a valid UTF-8-encoded Unicode string and not contain U+FFFD.
type Reference string
// These special references are used to obtain out-of-band information from
// StoreServer implementations. StoreServers are not required to support them,
// in which case they may return errors.NotExist.
var (
// HealthMetadata is used to check that a StoreServer is up. Servers
// need not return any special response when asked for this reference.
HealthMetadata Reference = "metadata:Health"
// HTTPBaseMetadata is used to obtain a base URL from which references
// may be requested by HTTP(S). The server may return a URL to which
// a reference may be appended to obtain that reference's data.
HTTPBaseMetadata Reference = "metadata:HTTP-Base"
// FlushWritebacksMetadata is used as a signal to flush the cache.
// A Get will return only after all writebacks have completed.
FlushWritebacksMetadata Reference = "metadata:FlushWritebacks"
// ListRefsMetadata is used by administrators to enumerate the
// references held by a StoreServer. Callers pass this value verbatim
// for the initial request and append a pagination token for subsequent
// requests. The response from such a request is a JSON-encoded
// ListRefsResponse.
ListRefsMetadata Reference = "metadata:ListRefs:"
)
// ListRefsResponse describes a response from a StoreServer.Get
// call for ListRefsMetadata.
type ListRefsResponse struct {
// Refs holds the reference information.
Refs []ListRefsItem
// Next holds the token to fetch the next page,
// or the empty string if this is the last page.
Next string
}
// ListRefsItem describes a reference in a StoreServer,
// returned as part of a ListRefsResponse.
type ListRefsItem struct {
// Ref holds the reference name.
Ref Reference
// Size the length of the reference data.
Size int64
}
// Signature is an ECDSA signature.
type Signature struct {
R, S *big.Int
}
// DEHash is a hash of the Name, Link, Attribute, Packing, and Time
// fields of a DirEntry. When using EE or EEIntegrity the hash also includes the
// block checksums; when using EE it includes the block encryption key.
type DEHash []byte
// Factotum implements an agent, potentially remote, to handle private key operations.
type Factotum interface {
// DirEntryHash is a summary used in signing and verifying directory entries.
DirEntryHash(n, l PathName, a Attribute, p Packing, t Time, dkey, hash []byte) DEHash
// FileSign ECDSA-signs DirEntry fields and, in some packings, file contents.
FileSign(hash DEHash) (Signature, error)
// ScalarMult is the bare private key operator, used in unwrapping packed data.
// Each call needs security review to ensure it cannot be abused as a signing
// oracle. Read https://en.wikipedia.org/wiki/Confused_deputy_problem.
ScalarMult(keyHash []byte, c elliptic.Curve, x, y *big.Int) (sx, sy *big.Int, err error)
// Sign signs a slice of bytes with the factotum's private key.
// The argument hash should be a cryptographic hash of the message you want to sign,
// no longer than your key's curve order. Don't use without a security consult.
Sign(hash []byte) (Signature, error)
// HKDF cryptographically mixes salt, info, and the Factotum secret and
// writes the result to out, which may be of any length but is typically
// 8 or 16 bytes. The result is unguessable without the secret, and does
// not leak the secret. For more information, see package
// golang.org/x/crypto/hkdf.
HKDF(salt, info, out []byte) error
// Pop derives a Factotum that defaults to the previous key.
Pop() Factotum
// PublicKey returns the user's public key in canonical string format.
PublicKey() PublicKey
// PublicKeyFromHash returns the matching public key or an error.
PublicKeyFromHash(keyHash []byte) (PublicKey, error)
}
// A Packing identifies the technique for turning the data pointed to by
// a key into the user's data. This may involve checksum verification,
// decrypting, signature checking, or nothing at all.
// Secondary data such as encryption keys may be required to implement
// the packing. That data appears in the API as arguments and struct fields
// called packdata.
type Packing uint8
// BlockPacker operates on a DirEntry, packing and signing DirBlocks.
type BlockPacker interface {
// Pack takes cleartext data and returns the packed ciphertext,
// appending a DirBlock to the DirEntry.
//
// The ciphertext slice remains valid until the next call to Pack.
Pack(cleartext []byte) (ciphertext []byte, err error)
// SetLocation updates the Location field of the last-Packed DirBlock.
SetLocation(Location)
// Close updates the Signature for the DirEntry and releases any
// resources associated with the packing operation.
Close() error
}
// BlockUnpacker operates on a DirEntry, unpacking and verifying its DirBlocks.
type BlockUnpacker interface {
// NextBlock returns the next DirBlock in the sequence and true,
// or a zero DirBlock and false if there are no more blocks to unpack.
NextBlock() (DirBlock, bool)
// SeekBlock returns the nth DirBlock and true,
// or a zero DirBlock and false if the nth block does not exist.
SeekBlock(n int) (DirBlock, bool)
// Unpack takes ciphertext returns the cleartext. If appropriate, the
// result is verified as correct according to the block's Packdata.
//
// The cleartext slice remains valid until the next call to Unpack.
Unpack(ciphertext []byte) (cleartext []byte, err error)
// Close releases any resources associated with the unpacking
// operation.
Close() error
}
// Packer provides the implementation of a Packing. The pack package binds
// Packing values to the concrete implementations of this interface.
type Packer interface {
// Packing returns the integer identifier of this Packing algorithm.
Packing() Packing
// String returns the name of this packer.
String() string
// Pack returns a BlockPacker that packs blocks
// into the given DirEntry.
Pack(Config, *DirEntry) (BlockPacker, error)
// Unpack returns a BlockUnpacker that unpacks blocks
// from the given DirEntry.
Unpack(Config, *DirEntry) (BlockUnpacker, error)
// PackLen returns an upper bound on the number of bytes required
// to store the cleartext after packing.
// PackLen might update the entry's Packdata field.
// PackLen returns -1 if there is an error.
PackLen(config Config, cleartext []byte, entry *DirEntry) int
// UnpackLen returns an upper bound on the number of bytes
// required to store the unpacked cleartext.
// UnpackLen might update the entry's Packdata field.
// UnpackLen returns -1 if there is an error.
UnpackLen(config Config, ciphertext []byte, entry *DirEntry) int
// ReaderHashes returns SHA-256 hashes of the public keys able to decrypt the
// associated ciphertext.
ReaderHashes(packdata []byte) ([][]byte, error)
// Share updates each packdata element to enable all the readers,
// and only those readers, to be able to decrypt the associated ciphertext,
// which is held separate from this call. It is used to repair incorrect
// key wrapping after access rights are changed.
// In case of error, Share skips processing for that reader or packdata.
// If packdata[i] is nil on return, it was skipped.
// Share trusts the caller to check the arguments are not malicious.
// To enable all Upspin users to decrypt the ciphertext, include
// AllReadersKey among the provided reader keys.
//
// TODO: It would be nice if DirServer provided a method to report
// which items need updates, so this could be automated.
Share(config Config, readers []PublicKey, packdata []*[]byte)
// Name updates the DirEntry to refer to a new path. If the new
// path is in a different directory, the wrapped keys are reduced to
// only that of the Upspin user invoking the method. The Packdata
// in entry must contain a wrapped key for that user.
Name(config Config, entry *DirEntry, path PathName) error
// SetTime changes the Time field in a DirEntry and recomputes
// its signature.
SetTime(config Config, entry *DirEntry, time Time) error
// Countersign updates the signatures in the DirEntry when a writer
// is in the process of switching to a new key. It checks that
// the first existing signature verifies under the old key, copies
// that one over the second existing signature, and creates a new
// first signature using the key from factotum.
Countersign(oldKey PublicKey, f Factotum, d *DirEntry) error
// UnpackableByAll reports whether the packed data may be unpacked by
// all Upspin users. Access and Group files must have this property, as
// should any files for which access.AllUsers have the read permission.
UnpackableByAll(d *DirEntry) (bool, error)
}
const (
// UnassignedPack is not a packer, but a special value indicating no
// packer was chosen. It is an error to use this value in a DirEntry
// except when creating a directory, in which case the DirServer will
// assign the proper packing.
UnassignedPack Packing = 0
// PlainPack is a no-encryption, no-integrity packing. Bytes are copied
// untouched. DirEntry fields SignedName and so on are signed.
PlainPack Packing = 1
// Packings from 2 through 19 are not for production use. This region
// is reserved for debugging and other temporary packing implementations.
// Packings from 20 and above (as well as PlainPack) are fixed in
// value and semantics and may be used in production.
// EEPack provides elliptic-curve end-to-end confidentiality and
// integrity protection. It stores AES-encrypted data, with metadata
// including an ECDSA signature and ECDH-wrapped keys.
// (NIST SP 800-57 Pt.1 Rev.4 section 5.6.1)
// A signature and a per-file symmetric encryption key, wrapped
// for each reader, are encoded in Packdata.
// User keys specify a curve:
// "p256": AES-256, SHA-256, and curve P256; strength 128.
// "p384": AES-256, SHA-512, and curve P384; strength 192.
// "p521": AES-256, SHA-512, and curve P521; strength 256.
// TODO(ehg) add "25519": x/crypto/curve25519, github.com/agl/ed25519
EEPack Packing = 20
// EEIntegrityPack provides elliptic-curve end-to-end integrity protection,
// like EEPack, but provides no confidentiality.
// It is typically used when read access is "all".
EEIntegrityPack Packing = 22
)
// User represents all the public information about an Upspin user as returned by KeyServer.
type User struct {
// Name represents the user's name as an e-mail address, such as ann@example.com.
Name UserName
// Dirs is a slice of DirServer endpoints where the user's root directory may be located,
// which should be contacted in order when trying to access the user's tree. Multiple
// entries represent mirrors, which are as yet unimplemented. TODO.
Dirs []Endpoint
// Stores is a slice of StoreServer endpoints where the user's data is primarily written to,
// which should be contacted in order when trying to access the user's data. Multiple
// entries represent mirrors, which are as yet unimplemented. TODO.
Stores []Endpoint
// PublicKey is the user's current public key.
PublicKey PublicKey
}
// The KeyServer interface provides access to public information about users.
type KeyServer interface {
Dialer
Service
// Lookup returns all public information about a user.
Lookup(userName UserName) (*User, error)
// Put sets or updates information about a user. The user's name must
// match the authenticated user. The call can update any field except
// the user name.
// To add new users, see the signup subcommand of cmd/upspin.
Put(user *User) error
}
// A PublicKey can be seen by anyone and is used for authenticating a user.
type PublicKey string
// AllUsersKey is a sentinel PublicKey value used to indicate that a
// Packer.Share operation should make the data readable to anyone.
var AllUsersKey = PublicKey("read: all")
var (
// ErrFollowLink indicates that all or part of a path name has
// evaluated to a DirEntry that is a link. In that case, the returned
// DirEntry will be that of the link, and its Name field is guaranteed
// to be an element-wise prefix of the argument path name. The caller
// should retry the operation, substituting that prefix (which may be
// the entire name) with the contents of the Link field of the returned
// DirEntry.
ErrFollowLink = errors.New("action incomplete: must follow link")
// ErrNotSupported indicates that the server does not support the
// requested operation.
ErrNotSupported = errors.New("not supported")
)
// MaxLinkHops is the maximum number of links that will be followed
// when evaluating a single path name.
const MaxLinkHops = 20
// Special Sequence values for Watch that can be used in place of the
// Sequence argument in the Watch function.
const (
// WatchStart returns all known events.
WatchStart = -iota
// WatchCurrent first sends a sequence of events describing the entire
// tree rooted at name. The Events are sent in sequence such that a
// directory is sent before its contents. After the full tree has been
// sent, the operation proceeds as normal.
WatchCurrent
// WatchNew returns only new events.
WatchNew
)
// DirServer manages the name space for one or more users.
type DirServer interface {
Dialer
Service
// Lookup returns the directory entry for the named file.
//
// If the returned error is ErrFollowLink, the caller should
// retry the operation as outlined in the description for
// ErrFollowLink. Otherwise in the case of error the
// returned DirEntry will be nil.
Lookup(name PathName) (*DirEntry, error)
// Put stores the DirEntry in the directory server. The entry
// may be a plain file, a link, or a directory. (Only one of
// these attributes may be set.)
// In practice the data for the file should be stored in
// a StoreServer as specified by the blocks in the entry,
// all of which should be stored with the same packing.
//
// Within the DirEntry, several fields have special properties.
// Time represents a timestamp for the item. It is advisory only
// but is included in the packing signature and so should usually
// be set to a non-zero value.
//
// Sequence represents a sequence number that is incremented
// after each Put. If it is neither 0 nor -1, the DirServer will
// reject the Put operation if the file does not exist or, for an
// existing item, if the Sequence is not the same as that
// stored in the metadata. If it is -1, Put will fail if there
// is already an item with that name.
//
// The Name field of the DirEntry identifies where in the directory
// tree the entry belongs. The SignedName field, which usually has the
// same value, is the name used to sign the DirEntry to guarantee its
// security. They may differ if an entry appears in multiple locations,
// such as in its original location plus within a second tree holding
// a snapshot of the original tree but starting from a different root.
//
// Most software will concern itself only with the Name field unless
// generating or validating the entry's signature.
//
// All but the last element of the path name must already exist
// and be directories or links. The final element, if it exists,
// must not be a directory. If something is already stored under
// the path, the new location and packdata replace the old.
//
// If the returned error is ErrFollowLink, the caller should
// retry the operation as outlined in the description for
// ErrFollowLink (with the added step of updating the
// Name field of the argument DirEntry). For any other error,
// the return DirEntry will be nil.
//
// A successful Put returns an incomplete DirEntry (see the
// description of AttrIncomplete) containing only the
// new sequence number.
Put(entry *DirEntry) (*DirEntry, error)
// Glob matches the pattern against the file names of the full
// rooted tree. That is, the pattern must look like a full path
// name, but elements of the path may contain metacharacters.
// Matching is done using Go's path.Match elementwise. The user
// name must be present in the pattern and is treated as a literal
// even if it contains metacharacters.
// If the caller has no read permission for the items named in the
// DirEntries, the returned Location and Packdata fields are cleared.
//
// If the returned error is ErrFollowLink, one or more of the
// returned DirEntries is a link (the others are completely
// evaluated). The caller should retry the operation for those
// DirEntries as outlined in the description for ErrFollowLink,
// updating the pattern as appropriate. Note that any returned
// links may only partially match the original argument pattern.
//
// If the pattern evaluates to one or more name that identifies
// a link, the DirEntry for the link is returned, not the target.
// This is analogous to passing false as the second argument
// to Client.Lookup.
Glob(pattern string) ([]*DirEntry, error)
// Delete deletes the DirEntry for a name from the directory service.
// It does not delete the data it references; use StoreServer.Delete
// for that. If the name identifies a link, Delete will delete the
// link itself, not its target.
//
// If the returned error is ErrFollowLink, the caller should
// retry the operation as outlined in the description for
// ErrFollowLink. (And in that case, the DirEntry will never
// represent the full path name of the argument.) Otherwise, the
// returned DirEntry will be nil whether the operation succeeded
// or not.
Delete(name PathName) (*DirEntry, error)
// WhichAccess returns the DirEntry of the Access file that is
// responsible for the access rights defined for the named item.
// WhichAccess requires that the calling user have at least one access
// right granted for the argument name. If not, WhichAccess will return
// a "does not exist" error, even if the item and/or the Access file
// exist.
//
// If the returned error is ErrFollowLink, the caller should
// retry the operation as outlined in the description for
// ErrFollowLink. Otherwise, in the case of error the returned
// DirEntry will be nil.
WhichAccess(name PathName) (*DirEntry, error)
// Watch returns a channel of Events that describe operations that
// affect the specified path and any of its descendants, beginning
// at the specified sequence number for the corresponding user root.
//
// If sequence is 0, all events known to the DirServer are sent.
//
// If sequence is WatchCurrent, the server first sends a sequence
// of events describing the entire tree rooted at name. The Events are
// sent in sequence such that a directory is sent before its contents.
// After the full tree has been sent, the operation proceeds as normal.
//
// If sequence is WatchNew, the server sends only new events.
//
// If the sequence is otherwise invalid, this is reported by the
// server sending a single event with a non-nil Error field with
// Kind=errors.Invalid. The events channel is then closed.
//
// When the provided done channel is closed the event channel
// is closed by the server.
//
// To receive an event for a given path under name, the caller must have
// one or more of the Upspin access rights to that path. Events for
// which the caller does not have enough rights to watch will not be
// sent. If the caller has rights but not Read, the entry will be
// present but incomplete (see the description of AttrIncomplete). If
// the name does not exist, Watch will succeed and report events if and
// when it is created.
//
// If the caller does not consume events in a timely fashion
// the server will close the event channel.
//
// If this server does not support this method it returns
// ErrNotSupported.
//
// The only errors returned by the Watch method itself are
// to report that the name is invalid or refers to a non-existent
// root, or that the operation is not supported.
Watch(name PathName, sequence int64, done <-chan struct{}) (<-chan Event, error)
}
// Event represents the creation, modification, or deletion of a DirEntry
// within a DirServer.
type Event struct {
// Entry is the DirEntry to which the event pertains. Its Sequence
// field captures the ordering of events for this user.
Entry *DirEntry
// Delete is true only if the entry is being deleted;
// otherwise it is being created or modified.
Delete bool
// Error is non-nil if an error occurred while waiting for events.
// In that case, all other fields are zero.
Error error
}
// Time represents a timestamp in units of seconds since
// the Unix epoch, Jan 1 1970 0:00 UTC.
type Time int64
// DirEntry represents the directory information for a file.
// The blocks of a file represent contiguous data. There are no
// holes and no overlaps and the first block always has offset 0.
// Name and SignedName must not be empty. See comments in DirServer.Put.
type DirEntry struct {
// Fields contributing to the signature.
SignedName PathName // The full path name of the file used for signing.
Link PathName // The link target, iff the DirEntry has Attr=AttrLink.
Attr Attribute // Attributes for the DirEntry.
Packing Packing // Packing used for every block in file.
Time Time // Time associated with file; might be when it was last written.
Blocks []DirBlock // Descriptors for each block. A nil or empty slice represents an empty file.
Packdata []byte // Information maintained by the packing algorithm.
// Field determining the key used for the signature, hence also tamper-resistant.
Writer UserName // Writer of the file, often the same as owner.
// Fields not included in the signature.
Name PathName // The full path name of the file. Only the last element can be a link.
Sequence int64 // The sequence (version) number of the item.
}
// BlockSize is an arbitrarily chosen size that packers use when breaking
// data into blocks for storage. Clients are free to use any size, but this
// value is used in various helper libraries.
// This is also the default value for flags.BlockSize, but must be kept in
// sync manually because the flags package cannot import this package.
const BlockSize = 1024 * 1024
// MaxBlockSize is the maximum size permitted for a block. The limit
// guarantees that 32-bit machines can process the data without problems.
const MaxBlockSize = 1024 * 1024 * 1024
// DirBlock describes a block of data representing a contiguous section of a file.
// The block may be of any non-negative size, but in large files is usually
// BlockSize long.
type DirBlock struct {
Location Location // Location of data in store.
Offset int64 // Byte offset of start of block's data in file.
Size int64 // Length of block data in bytes.
Packdata []byte // Information maintained by the packing algorithm.
}
// Attribute defines the attributes for a DirEntry.
type Attribute byte
// Supported Attributes.
const (
// AttrNone is the default attribute, identifying a plain data object.
AttrNone = Attribute(0)
// AttrDirectory identifies a directory. It must be the only attribute.
AttrDirectory = Attribute(1 << 0)
// AttrLink identifies a link. It must be the only attribute.
// A link is a path name whose DirEntry identifies another
// "target" item in the tree, similar to a Unix symbolic link.
// The target of a link may be another link.
// The target path is stored in the Link field of the DirEntry.
// A link DirEntry holds zero DirBlocks.
AttrLink = Attribute(1 << 1)
// AttrIncomplete identifies a DirEntry whose Blocks and Packdata
// fields are elided for access control purposes, or the reply to
// a successful Put containing only the updated sequence number.
AttrIncomplete = Attribute(1 << 2)
)
// Sequence numbers.
// Sequence numbers are controlled by the DirServer. For a given user root they
// start at SeqBase and grow monotonically (typically but not necessarily by
// one) with each Put or Delete operation in that user's tree. After an item is
// Put to or Deleted from the DirServer, the Sequence of that item (or its
// directory, for a Delete) and all of the directories on its path will be set
// to the next Sequence number for the user tree. Thus, as a corollary, any
// directory but in particular the user root always has the Sequence number of
// the most recently modified item at that level or deeper in the tree.
//
// When a file or directory is being created, the sequence number in the
// DirEntry provided to Put must be either SeqNotExist or SeqIgnore.
const (
SeqNotExist = -1 // Put will fail if item exists.
SeqIgnore = 0 // Put will not check sequence number, but will update it.
SeqBase = 1 // Base at which valid sequence numbers start.
)
// Refdata attaches information about cacheability to a Reference. A Refdata is
// returned by a StoreServer to describe the lifetime of the data associated with
// a Reference.
type Refdata struct {
Reference Reference // The reference itself.
Volatile bool // If true, the data might change on every Get and cannot be cached.
Duration time.Duration // For non-volatile data, the predicted cacheable lifetime; 0 means forever.
}
// The StoreServer saves and retrieves data without interpretation.
type StoreServer interface {
Dialer
Service
// Get attempts to retrieve the data identified by the reference.
// Three things might happen:
// 1. The data is in this StoreServer. It is returned. The Location slice
// and error are nil. Refdata contains information about the data.
// 2. The data is not in this StoreServer, but may be in one or more
// other locations known to the store. The slice of Locations
// is returned. The data, Refdata, Locations, and error are nil.
// 3. An error occurs. The data, Locations and Refdata are nil
// and the error describes the problem.
Get(ref Reference) ([]byte, *Refdata, []Location, error)
// Put puts the data into the store and returns the reference
// to be used to retrieve it.
Put(data []byte) (*Refdata, error)
// Delete permanently removes all storage space associated
// with the reference. After a successful Delete, calls to Get with the
// same reference will fail. If the reference is not found, an error is
// returned. Implementations may disable this method except for
// privileged users.
Delete(ref Reference) error
}
// Client API.
// The Client interface provides a higher-level API suitable for applications
// that wish to access Upspin's name space. Most Upspin programs should
// use the Client interface to talk to Upspin services.
//
// When Client evaluates a path name and encounters a link, it evaluates the
// link, iteratively if necessary, until it reaches an item that is not a link.
// (The DirServer interface does not evaluate links.)
//
// In methods where a name is evaluated and a DirEntry returned,
// if links were evaluated in processing the operation, the Name field
// of the DirEntry will be different from the argument path name and
// will hold the link-free path to item.
type Client interface {
// Get returns the clear, decrypted data stored under the given name.
// It is intended only for special purposes, since it will allocate memory
// for the entire "blob" to return. The file-like API below can be less
// memory-intensive.
Get(name PathName) ([]byte, error)
// Lookup returns the directory entry for the named file. The
// boolean determines whether, if the final path element is a link,
// to return the DirEntry for the link (false) or for the target of
// the link (true).
Lookup(name PathName, followFinal bool) (*DirEntry, error)
// Put stores the data at the given name. If something is already
// stored with that name, it will no longer be available using the
// name, although it may still exist in the storage server. (See
// the documentation for Delete.) Like Get, it is not the usual
// access method. The file-like API is preferred.
//
// A successful Put returns an incomplete DirEntry (see the
// description of AttrIncomplete) containing only the
// new sequence number.
Put(name PathName, data []byte) (*DirEntry, error)
// PutLink creates a link from the new name to the old name. The
// new name must not look like the path to an Access or Group file.
// If something is already stored with the new name, it is first
// deleted from the directory but its storage is not deleted from
// the Store. (See the documentation for Delete.) The old name is
// not evaluated, that is, the resulting link will hold the
// argument to PutLink even if it refers to a path that itself
// contains links. The name is canonicalized, however (see
// path.Clean).
//
// A successful PutLink returns an incomplete DirEntry (see the
// description of AttrIncomplete) containing only the
// new sequence number.
PutLink(oldName, newName PathName) (*DirEntry, error)
// PutDuplicate creates a new name for the references referred to
// by the old name. Subsequent Puts to either name do not effect
// the contents referred to by the other. There must be no existing
// item with the new name. If the final element of the path name
// is a link, PutDuplicate will duplicate the link and not the
// link target.
//
// A successful PutDuplicate returns an incomplete DirEntry (see the
// description of AttrIncomplete) containing only the
// new sequence number.
PutDuplicate(oldName, newName PathName) (*DirEntry, error)
// MakeDirectory creates a directory with the given name, which
// must not already exist. All but the last element of the path
// name must already exist and be directories.
//
// A successful MakeDirectory returns an incomplete DirEntry (see the
// description of AttrIncomplete) containing only the
// new sequence number.
MakeDirectory(dirName PathName) (*DirEntry, error)
// Rename renames oldName to newName. The old name is no longer valid.
// If the final element of the path name is a link, Rename will
// Rename the link itself, not the link target.
Rename(oldName, newName PathName) error
// SetTime sets the time in name's DirEntry. If the final element
// of the path name is a link, SetTime will affect the link itself,
// not the link target.
SetTime(name PathName, t Time) error
// Delete deletes the DirEntry associated with the name. The
// storage referenced by the DirEntry is not deleted,
// although the storage server may garbage collect unreferenced
// data independently. If the final element of the path name is a
// link, Delete will delete the link itself, not the link target.
Delete(name PathName) error
// Glob matches the pattern against the file names of the full
// rooted tree. That is, the pattern must look like a full path
// name, but elements of the path may contain metacharacters.
// Matching is done using Go's path.Match elementwise. The user
// name must be present in the pattern and is treated as a literal
// even if it contains metacharacters. Note that if links are
// evaluated while executing Glob, the Name fields of the returned
// DirEntries might not match the original argument pattern.
Glob(pattern string) ([]*DirEntry, error)
// Open and Create are file-like methods similar to Go's os.File API.
// The name, however, is a fully-qualified upspin PathName.
Create(name PathName) (File, error)
Open(name PathName) (File, error)
// DirServer returns an error or a reachable bound DirServer for the user.
DirServer(name PathName) (DirServer, error)
}
// The File interface has semantics and an API that parallels a subset
// of Go's os.File. The main semantic difference, besides the limited
// method set, is that a Read will only return once the entire contents
// have been decrypted and verified.
type File interface {
// Close releases the resources. For a writable file, it also
// writes the accumulated data in a StoreServer. After a
// Close, successful or not, all methods of File except Name
// will fail.
Close() error
// Name returns the full path name of the File.
Name() PathName
// Read, ReadAt, Write, WriteAt and Seek implement
// the standard Go interfaces io.Reader, etc.
// Because of the nature of Upspin storage, the entire
// item might need to be read into memory by the
// implementation before Read can return any data.
// Similarly, Write might accumulate all data and only
// flush to storage once Close is called.
Read(b []byte) (n int, err error)
ReadAt(b []byte, off int64) (n int, err error)
Write(b []byte) (n int, err error)
WriteAt(b []byte, off int64) (n int, err error)
Seek(offset int64, whence int) (ret int64, err error)
}
// Config contains client information such as the user's keys and
// preferred KeyServer, DirServer, and StoreServer endpoints.
type Config interface {
// The name of the user requesting access.
UserName() UserName
// Factotum holds the user's cryptographic keys and encapsulates crypto operations.
Factotum() Factotum
// Packing is the Packing to use when creating new data items, although
// it may be overridden by the implementation, such as to use a cleartext
// packing for a globally readable ("read:all") file.
Packing() Packing
// KeyEndpoint is the endpoint of the KeyServer to contact to retrieve keys.
KeyEndpoint() Endpoint
// DirEndpoint is the endpoint of the DirServer in which to place new data items. It is
// usually the location of the user's root.
DirEndpoint() Endpoint
// StoreEndpoint is the endpoint of the StoreServer in which to place new data items.
StoreEndpoint() Endpoint
// Value returns the value for the given configuration key.
Value(key string) string
}
// Dialer defines how to connect and authenticate to a server. Each
// service type (KeyServer, DirServer, StoreServer) implements the methods of
// the Dialer interface. These methods are not used directly by
// clients. Instead, clients should use the methods of
// the Upspin "bind" package to connect to services.
type Dialer interface {
// Dial connects to the service and performs any needed authentication.
Dial(Config, Endpoint) (Service, error)
}
// Service is the general interface returned by a dialer. It includes
// methods to configure the service and report its setup.
type Service interface {
// Endpoint returns the network endpoint of the server.
Endpoint() Endpoint
// Close closes the connection to the service and releases all resources used.
// A Service may not be re-used after close.
Close()
}