We have the invalid bit to indicate that a file isn't good. That's enough for remote devices. For ourselves, it would be good to know sometimes why the file isn't good - because it's an unsupported type, because it matches an ignore pattern, or because we detected the data is bad and we need to rescan it. Or, and this is the main future reason for the PR, because it's a change detected on a receive only device. We will want something like the invalid flag for those changes, but marking them as invalid today means the scanner will rehash them. Hence something more fine grained is required. This introduces a LocalFlags fields to the FileInfo where we can stash things that we care about locally. For example, FlagLocalUnsupported = 1 << 0 // The kind is unsupported, e.g. symlinks on Windows FlagLocalIgnored = 1 << 1 // Matches local ignore patterns FlagLocalMustRescan = 1 << 2 // Doesn't match content on disk, must be rechecked fully The LocalFlags fields isn't sent over the wire; instead the Invalid attribute is calculated based on the flags at index sending time. It's on the FileInfo anyway because that's what we serialize to database etc. The actual Invalid flag should after this just be considered when building the global state and figuring out availability for remote devices. It is not used for local file index entries.
202 lines
6.8 KiB
Protocol Buffer
202 lines
6.8 KiB
Protocol Buffer
syntax = "proto3";
|
|
|
|
package protocol;
|
|
|
|
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
|
|
|
|
option (gogoproto.goproto_getters_all) = false;
|
|
option (gogoproto.sizer_all) = false;
|
|
option (gogoproto.protosizer_all) = true;
|
|
option (gogoproto.goproto_enum_stringer_all) = true;
|
|
option (gogoproto.goproto_enum_prefix_all) = false;
|
|
|
|
// --- Pre-auth ---
|
|
|
|
message Hello {
|
|
string device_name = 1;
|
|
string client_name = 2;
|
|
string client_version = 3;
|
|
}
|
|
|
|
// --- Header ---
|
|
|
|
message Header {
|
|
MessageType type = 1;
|
|
MessageCompression compression = 2;
|
|
}
|
|
|
|
enum MessageType {
|
|
CLUSTER_CONFIG = 0 [(gogoproto.enumvalue_customname) = "messageTypeClusterConfig"];
|
|
INDEX = 1 [(gogoproto.enumvalue_customname) = "messageTypeIndex"];
|
|
INDEX_UPDATE = 2 [(gogoproto.enumvalue_customname) = "messageTypeIndexUpdate"];
|
|
REQUEST = 3 [(gogoproto.enumvalue_customname) = "messageTypeRequest"];
|
|
RESPONSE = 4 [(gogoproto.enumvalue_customname) = "messageTypeResponse"];
|
|
DOWNLOAD_PROGRESS = 5 [(gogoproto.enumvalue_customname) = "messageTypeDownloadProgress"];
|
|
PING = 6 [(gogoproto.enumvalue_customname) = "messageTypePing"];
|
|
CLOSE = 7 [(gogoproto.enumvalue_customname) = "messageTypeClose"];
|
|
}
|
|
|
|
enum MessageCompression {
|
|
NONE = 0 [(gogoproto.enumvalue_customname) = "MessageCompressionNone"];
|
|
LZ4 = 1 [(gogoproto.enumvalue_customname) = "MessageCompressionLZ4"];
|
|
}
|
|
|
|
// --- Actual messages ---
|
|
|
|
// Cluster Config
|
|
|
|
message ClusterConfig {
|
|
repeated Folder folders = 1 [(gogoproto.nullable) = false];
|
|
}
|
|
|
|
message Folder {
|
|
string id = 1 [(gogoproto.customname) = "ID"];
|
|
string label = 2;
|
|
bool read_only = 3;
|
|
bool ignore_permissions = 4;
|
|
bool ignore_delete = 5;
|
|
bool disable_temp_indexes = 6;
|
|
bool paused = 7;
|
|
|
|
repeated Device devices = 16 [(gogoproto.nullable) = false];
|
|
}
|
|
|
|
message Device {
|
|
bytes id = 1 [(gogoproto.customname) = "ID", (gogoproto.customtype) = "DeviceID", (gogoproto.nullable) = false];
|
|
string name = 2;
|
|
repeated string addresses = 3;
|
|
Compression compression = 4;
|
|
string cert_name = 5;
|
|
int64 max_sequence = 6;
|
|
bool introducer = 7;
|
|
uint64 index_id = 8 [(gogoproto.customname) = "IndexID", (gogoproto.customtype) = "IndexID", (gogoproto.nullable) = false];
|
|
bool skip_introduction_removals = 9;
|
|
}
|
|
|
|
enum Compression {
|
|
METADATA = 0 [(gogoproto.enumvalue_customname) = "CompressMetadata"];
|
|
NEVER = 1 [(gogoproto.enumvalue_customname) = "CompressNever"];
|
|
ALWAYS = 2 [(gogoproto.enumvalue_customname) = "CompressAlways"];
|
|
}
|
|
|
|
// Index and Index Update
|
|
|
|
message Index {
|
|
string folder = 1;
|
|
repeated FileInfo files = 2 [(gogoproto.nullable) = false];
|
|
}
|
|
|
|
message IndexUpdate {
|
|
string folder = 1;
|
|
repeated FileInfo files = 2 [(gogoproto.nullable) = false];
|
|
}
|
|
|
|
message FileInfo {
|
|
option (gogoproto.goproto_stringer) = false;
|
|
string name = 1;
|
|
FileInfoType type = 2;
|
|
int64 size = 3;
|
|
uint32 permissions = 4;
|
|
int64 modified_s = 5;
|
|
int32 modified_ns = 11;
|
|
uint64 modified_by = 12 [(gogoproto.customtype) = "ShortID", (gogoproto.nullable) = false];
|
|
bool deleted = 6;
|
|
bool invalid = 7 [(gogoproto.customname) = "RawInvalid"];
|
|
bool no_permissions = 8;
|
|
Vector version = 9 [(gogoproto.nullable) = false];
|
|
int64 sequence = 10;
|
|
int32 block_size = 13 [(gogoproto.customname) = "RawBlockSize"];
|
|
repeated BlockInfo Blocks = 16 [(gogoproto.nullable) = false];
|
|
string symlink_target = 17;
|
|
|
|
// The local_flags fields stores flags that are relevant to the local
|
|
// host only. It is not part of the protocol, doesn't get sent or
|
|
// received (we make sure to zero it), nonetheless we need it on our
|
|
// struct and to be able to serialize it to/from the database.
|
|
uint32 local_flags = 1000;
|
|
}
|
|
|
|
enum FileInfoType {
|
|
FILE = 0 [(gogoproto.enumvalue_customname) = "FileInfoTypeFile"];
|
|
DIRECTORY = 1 [(gogoproto.enumvalue_customname) = "FileInfoTypeDirectory"];
|
|
SYMLINK_FILE = 2 [(gogoproto.enumvalue_customname) = "FileInfoTypeDeprecatedSymlinkFile", deprecated = true];
|
|
SYMLINK_DIRECTORY = 3 [(gogoproto.enumvalue_customname) = "FileInfoTypeDeprecatedSymlinkDirectory", deprecated = true];
|
|
SYMLINK = 4 [(gogoproto.enumvalue_customname) = "FileInfoTypeSymlink"];
|
|
}
|
|
|
|
message BlockInfo {
|
|
option (gogoproto.goproto_stringer) = false;
|
|
int64 offset = 1;
|
|
int32 size = 2;
|
|
bytes hash = 3;
|
|
uint32 weak_hash = 4;
|
|
}
|
|
|
|
message Vector {
|
|
repeated Counter counters = 1 [(gogoproto.nullable) = false];
|
|
}
|
|
|
|
message Counter {
|
|
uint64 id = 1 [(gogoproto.customname) = "ID", (gogoproto.customtype) = "ShortID", (gogoproto.nullable) = false];
|
|
uint64 value = 2;
|
|
}
|
|
|
|
// Request
|
|
|
|
message Request {
|
|
int32 id = 1 [(gogoproto.customname) = "ID"];
|
|
string folder = 2;
|
|
string name = 3;
|
|
int64 offset = 4;
|
|
int32 size = 5;
|
|
bytes hash = 6;
|
|
bool from_temporary = 7;
|
|
uint32 weak_hash = 8;
|
|
}
|
|
|
|
// Response
|
|
|
|
message Response {
|
|
int32 id = 1 [(gogoproto.customname) = "ID"];
|
|
bytes data = 2;
|
|
ErrorCode code = 3;
|
|
}
|
|
|
|
enum ErrorCode {
|
|
NO_ERROR = 0 [(gogoproto.enumvalue_customname) = "ErrorCodeNoError"];
|
|
GENERIC = 1 [(gogoproto.enumvalue_customname) = "ErrorCodeGeneric"];
|
|
NO_SUCH_FILE = 2 [(gogoproto.enumvalue_customname) = "ErrorCodeNoSuchFile"];
|
|
INVALID_FILE = 3 [(gogoproto.enumvalue_customname) = "ErrorCodeInvalidFile"];
|
|
}
|
|
|
|
// DownloadProgress
|
|
|
|
message DownloadProgress {
|
|
string folder = 1;
|
|
repeated FileDownloadProgressUpdate updates = 2 [(gogoproto.nullable) = false];
|
|
}
|
|
|
|
message FileDownloadProgressUpdate {
|
|
FileDownloadProgressUpdateType update_type = 1;
|
|
string name = 2;
|
|
Vector version = 3 [(gogoproto.nullable) = false];
|
|
repeated int32 block_indexes = 4 [packed=false];
|
|
}
|
|
|
|
enum FileDownloadProgressUpdateType {
|
|
APPEND = 0 [(gogoproto.enumvalue_customname) = "UpdateTypeAppend"];
|
|
FORGET = 1 [(gogoproto.enumvalue_customname) = "UpdateTypeForget"];
|
|
}
|
|
|
|
// Ping
|
|
|
|
message Ping {
|
|
}
|
|
|
|
// Close
|
|
|
|
message Close {
|
|
string reason = 1;
|
|
}
|
|
|