perf(protocol): pack metadata into one frame (PROTOCOL 2.20.0)
This commit is contained in:
+18
-2
@@ -642,8 +642,24 @@ typedef struct Config {
|
||||
* anything else) is what keeps a 2.19 client and a 2.18 server from ever
|
||||
* reaching that state. SECURITY: a 2.19 store holds a salted PBKDF2 verifier
|
||||
* and cannot verify (and refuses to load) a legacy unsalted-SHA-256 store line,
|
||||
* so an old bearer digest can never be replayed against a 2.19 daemon. */
|
||||
#define PROTOCOL_VERSION "2.19.0"
|
||||
* so an old bearer digest can never be replayed against a 2.19 daemon.
|
||||
*
|
||||
* Packed Metadata Wave: 2.19.0 -> 2.20.0.
|
||||
*
|
||||
* WHY the bump, grounded in the wire: metadata_send()/metadata_receive() no
|
||||
* longer emit/consume the metadata as up to 12 separate per-field framed
|
||||
* writes. A file's metadata now crosses the wire as ONE packed frame: a
|
||||
* single int32 present flag (0 = absent, 1 = present) followed, when present,
|
||||
* by the fixed FILE_METADATA_WIRE_SIZE-byte (68-byte) field record produced by
|
||||
* metadata_to_buf(). A 2.19 peer would desynchronize on the removed frames
|
||||
* (it would read the packed record's bytes as a stream of separate field
|
||||
* frames), so the strict same-version handshake (config_receive rejects a
|
||||
* mismatched version before parsing anything else) is what keeps a 2.20 client
|
||||
* and a 2.19 server from ever reaching that state. The encoded field layout
|
||||
* itself is unchanged (only its framing collapses), so the chunk codec, which
|
||||
* already used the packed metadata_to_buf()/metadata_from_buf() codec, is
|
||||
* byte-identical to before. */
|
||||
#define PROTOCOL_VERSION "2.20.0"
|
||||
#define DEFAULT_CHUNK_SIZE (10 * 1024 * 1024)
|
||||
/* Upper bound on total basis-dir entries (rsync caps --link-dest at 20). */
|
||||
#define MAX_BASIS_DIRS 64
|
||||
|
||||
+21
-124
@@ -157,33 +157,17 @@ FileMetadata* metadata_from_buf(char** buf) {
|
||||
|
||||
bool metadata_send(int file_descriptor, const FileMetadata* m) {
|
||||
if (m == NULL) {
|
||||
int32_t zero = 0;
|
||||
return send_n_data(file_descriptor, &zero, sizeof(zero));
|
||||
int32_t absent = 0;
|
||||
return send_n_data(file_descriptor, &absent, sizeof(absent));
|
||||
}
|
||||
int32_t present = 1;
|
||||
int32_t mode = (int32_t)m->mode;
|
||||
int32_t uid = (int32_t)m->uid;
|
||||
int32_t gid = (int32_t)m->gid;
|
||||
int64_t mtime_sec = (int64_t)m->mtime_sec;
|
||||
int64_t mtime_nsec = (int64_t)m->mtime_nsec;
|
||||
int32_t atime_valid = m->atime_valid ? 1 : 0;
|
||||
int64_t atime_sec = (int64_t)m->atime_sec;
|
||||
int64_t atime_nsec = (int64_t)m->atime_nsec;
|
||||
int32_t crtime_valid = m->crtime_valid ? 1 : 0;
|
||||
int64_t crtime_sec = (int64_t)m->crtime_sec;
|
||||
int64_t crtime_nsec = (int64_t)m->crtime_nsec;
|
||||
return send_n_data(file_descriptor, &present, sizeof(present)) &&
|
||||
send_n_data(file_descriptor, &mode, sizeof(mode)) &&
|
||||
send_n_data(file_descriptor, &uid, sizeof(uid)) &&
|
||||
send_n_data(file_descriptor, &gid, sizeof(gid)) &&
|
||||
send_n_data(file_descriptor, &mtime_sec, sizeof(mtime_sec)) &&
|
||||
send_n_data(file_descriptor, &mtime_nsec, sizeof(mtime_nsec)) &&
|
||||
send_n_data(file_descriptor, &atime_valid, sizeof(atime_valid)) &&
|
||||
send_n_data(file_descriptor, &atime_sec, sizeof(atime_sec)) &&
|
||||
send_n_data(file_descriptor, &atime_nsec, sizeof(atime_nsec)) &&
|
||||
send_n_data(file_descriptor, &crtime_valid, sizeof(crtime_valid)) &&
|
||||
send_n_data(file_descriptor, &crtime_sec, sizeof(crtime_sec)) &&
|
||||
send_n_data(file_descriptor, &crtime_nsec, sizeof(crtime_nsec));
|
||||
/* One packed frame (protocol 2.20.0): the int32 present flag followed by the
|
||||
fixed FILE_METADATA_WIRE_SIZE-byte field record. metadata_to_buf() emits
|
||||
exactly that layout (present + fields), so build it once and write the
|
||||
whole record in a single call instead of one frame per field. */
|
||||
char packed[sizeof(int32_t) + FILE_METADATA_WIRE_SIZE];
|
||||
char* cursor = packed;
|
||||
metadata_to_buf(&cursor, m);
|
||||
return send_n_data(file_descriptor, packed, sizeof(packed));
|
||||
}
|
||||
|
||||
FileMetadata* metadata_receive(int file_descriptor, int* ok) {
|
||||
@@ -203,109 +187,22 @@ FileMetadata* metadata_receive(int file_descriptor, int* ok) {
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
FileMetadata* m = protocol_alloc(sizeof(FileMetadata));
|
||||
/* Rebuild the packed record metadata_from_buf() expects: the present flag we
|
||||
just read, followed by exactly FILE_METADATA_WIRE_SIZE field bytes. */
|
||||
char packed[sizeof(int32_t) + FILE_METADATA_WIRE_SIZE];
|
||||
memcpy(packed, &present, sizeof(present));
|
||||
if (!receive_n_data(file_descriptor, packed + sizeof(present), FILE_METADATA_WIRE_SIZE)) {
|
||||
if (ok)
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
char* cursor = packed;
|
||||
FileMetadata* m = metadata_from_buf(&cursor);
|
||||
if (m == NULL) {
|
||||
if (ok)
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
int32_t mode;
|
||||
if (!receive_n_data(file_descriptor, &mode, sizeof(mode))) {
|
||||
free(m);
|
||||
if (ok)
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
m->mode = (mode_t)mode;
|
||||
int32_t uid;
|
||||
if (!receive_n_data(file_descriptor, &uid, sizeof(uid))) {
|
||||
free(m);
|
||||
if (ok)
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
m->uid = (uid_t)uid;
|
||||
int32_t gid;
|
||||
if (!receive_n_data(file_descriptor, &gid, sizeof(gid))) {
|
||||
free(m);
|
||||
if (ok)
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
m->gid = (gid_t)gid;
|
||||
int64_t mtime_sec;
|
||||
if (!receive_n_data(file_descriptor, &mtime_sec, sizeof(mtime_sec))) {
|
||||
free(m);
|
||||
if (ok)
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
m->mtime_sec = (time_t)mtime_sec;
|
||||
int64_t mtime_nsec;
|
||||
if (!receive_n_data(file_descriptor, &mtime_nsec, sizeof(mtime_nsec))) {
|
||||
free(m);
|
||||
if (ok)
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
m->mtime_nsec = (long)mtime_nsec;
|
||||
int32_t atime_valid;
|
||||
if (!receive_n_data(file_descriptor, &atime_valid, sizeof(atime_valid))) {
|
||||
free(m);
|
||||
if (ok)
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
int64_t atime_sec;
|
||||
if (!receive_n_data(file_descriptor, &atime_sec, sizeof(atime_sec))) {
|
||||
free(m);
|
||||
if (ok)
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
int64_t atime_nsec;
|
||||
if (!receive_n_data(file_descriptor, &atime_nsec, sizeof(atime_nsec))) {
|
||||
free(m);
|
||||
if (ok)
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
int32_t crtime_valid;
|
||||
if (!receive_n_data(file_descriptor, &crtime_valid, sizeof(crtime_valid))) {
|
||||
free(m);
|
||||
if (ok)
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
int64_t crtime_sec;
|
||||
if (!receive_n_data(file_descriptor, &crtime_sec, sizeof(crtime_sec))) {
|
||||
free(m);
|
||||
if (ok)
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
int64_t crtime_nsec;
|
||||
if (!receive_n_data(file_descriptor, &crtime_nsec, sizeof(crtime_nsec))) {
|
||||
free(m);
|
||||
if (ok)
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
m->atime_valid = atime_valid != 0;
|
||||
m->atime_sec = (time_t)atime_sec;
|
||||
m->atime_nsec = (long)atime_nsec;
|
||||
m->crtime_valid = crtime_valid != 0;
|
||||
m->crtime_sec = (time_t)crtime_sec;
|
||||
m->crtime_nsec = (long)crtime_nsec;
|
||||
if (mtime_nsec < 0 || mtime_nsec >= 1000000000LL || mode < 0 || uid < 0 || gid < 0 ||
|
||||
atime_valid < 0 || atime_valid > 1 || crtime_valid < 0 || crtime_valid > 1 ||
|
||||
(atime_valid && (atime_nsec < 0 || atime_nsec >= 1000000000LL)) ||
|
||||
(crtime_valid && (crtime_nsec < 0 || crtime_nsec >= 1000000000LL))) {
|
||||
free(m);
|
||||
if (ok)
|
||||
*ok = 0;
|
||||
return NULL;
|
||||
}
|
||||
if (ok)
|
||||
*ok = 1;
|
||||
return m;
|
||||
|
||||
@@ -29,7 +29,13 @@
|
||||
|
||||
/* Size of metadata fields on wire, excluding the int32_t `present` field that
|
||||
* is always sent first. The total wire size for present metadata is
|
||||
* sizeof(int32_t) + FILE_METADATA_WIRE_SIZE (68 bytes on most platforms). */
|
||||
* sizeof(int32_t) + FILE_METADATA_WIRE_SIZE (68 bytes on most platforms).
|
||||
*
|
||||
* metadata_send()/metadata_receive() (protocol 2.20.0) frame the metadata as a
|
||||
* single packed record: one int32 present flag (0 = absent) followed, when
|
||||
* present, by exactly FILE_METADATA_WIRE_SIZE bytes of field data. This is the
|
||||
* same present+fields byte layout metadata_to_buf()/metadata_from_buf() use, so
|
||||
* the wire metadata is now one frame instead of one frame per field. */
|
||||
#define FILE_METADATA_WIRE_SIZE (sizeof(int32_t) * 5 + sizeof(int64_t) * 6)
|
||||
|
||||
void metadata_to_buf(char** buf, const FileMetadata* m);
|
||||
|
||||
Reference in New Issue
Block a user