cube(2): one frame for every walk — the class mask is in it
CUBE_OP_ENUM and CUBE_OP_RANGE returned `key | value_len | value`, so a caller could walk a store and not learn what any record it walked past *was*. A store could not answer `entries_flagged` from a kernel store at all, and the only way to find out was to open the device and read the format directly — which is the second reader of the format this interface exists to make unnecessary. Both walks now return the flag scan's frame: `key(24) | flags(2) | value_len | value`, with the space ahead of it when the scope is every space. One frame, one packer, one `Batch::offer`; the separate `offer_flagged` and `pack_record` are gone, and so is the reason for them to disagree. The class comes from wherever the value did: an addressed image's index entry (the binary search already read it and used to throw it away), a log entry's mask, or zero for a packed v1/v2 record, which has no field to carry one. The merge in `SpaceWalker` hands it back alongside the key and the value for the same reason — a record the log supplied carries the class its writer stamped, and dropping it there is what left a listing unable to say what it was listing.
This commit is contained in:
@@ -2330,7 +2330,13 @@ impl<'a> SpaceWalker<'a> {
|
||||
}
|
||||
|
||||
/// The next live record, or nothing when the space is walked out.
|
||||
fn next(&mut self) -> Option<(&'a [u8; RAW_KEY_LEN], &'a [u8])> {
|
||||
/// The next live record: `(key, its class, its value)`.
|
||||
///
|
||||
/// The class comes back because a listing has to be able to say what a record *is*. A packed
|
||||
/// image's records carry no mask field, so they read as "no class" — but a record the log
|
||||
/// supplied carries the mask its writer stamped, and dropping it here is what left a
|
||||
/// kernel-backed store unable to answer `entries_flagged`.
|
||||
fn next(&mut self) -> Option<(&'a [u8; RAW_KEY_LEN], u16, &'a [u8])> {
|
||||
loop {
|
||||
// The entries of one key arrive in log order, so the last of a key's group is the
|
||||
// newest word on it and the earlier ones are history.
|
||||
@@ -2346,7 +2352,7 @@ impl<'a> SpaceWalker<'a> {
|
||||
self.edit_at += 1;
|
||||
self.image_next = next_in_space(&mut self.records, &self.wanted);
|
||||
if !e.deleted {
|
||||
return Some((e.key, e.value));
|
||||
return Some((e.key, e.flags, e.value));
|
||||
}
|
||||
}
|
||||
// A record only the log holds, in its key's place.
|
||||
@@ -2354,19 +2360,20 @@ impl<'a> SpaceWalker<'a> {
|
||||
self.edit_at += 1;
|
||||
self.image_next = Some((key, value));
|
||||
if !e.deleted {
|
||||
return Some((e.key, e.value));
|
||||
return Some((e.key, e.flags, e.value));
|
||||
}
|
||||
}
|
||||
// The image's own record, which the log says nothing about.
|
||||
// The image's own record, which the log says nothing about. A packed image has no
|
||||
// mask field, so it answers none.
|
||||
(_, Some((key, value))) => {
|
||||
self.image_next = next_in_space(&mut self.records, &self.wanted);
|
||||
return Some((key, value));
|
||||
return Some((key, 0, value));
|
||||
}
|
||||
// The image is walked out; the rest of the log still has records to report.
|
||||
(Some(e), None) => {
|
||||
self.edit_at += 1;
|
||||
if !e.deleted {
|
||||
return Some((e.key, e.value));
|
||||
return Some((e.key, e.flags, e.value));
|
||||
}
|
||||
}
|
||||
(None, None) => return None,
|
||||
@@ -2493,43 +2500,22 @@ struct Batch<'a> {
|
||||
with_space: bool,
|
||||
}
|
||||
|
||||
/// The fixed part of a flag-scan frame, before the value.
|
||||
/// The fixed part of a walk's frame, before the value.
|
||||
fn flagged_frame(with_space: bool) -> usize {
|
||||
RAW_KEY_LEN + FLAGS_LEN + 4 + if with_space { SPACE_ID_LEN } else { 0 }
|
||||
}
|
||||
|
||||
impl Batch<'_> {
|
||||
/// Offer one record. `false` means nothing more will fit in this batch.
|
||||
fn offer(&mut self, key: &[u8], value: &[u8]) -> bool {
|
||||
self.seen += 1;
|
||||
if self.seen <= self.cursor {
|
||||
return true;
|
||||
}
|
||||
match pack_record(key, value, self.out, self.written) {
|
||||
Some(n) => {
|
||||
self.written += n;
|
||||
self.returned += 1;
|
||||
true
|
||||
}
|
||||
None => {
|
||||
self.too_big = RAW_KEY_LEN + 4 + value.len();
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Offer one *flagged* record. The counting rule is `offer`'s, and it is what makes the cursor
|
||||
/// mean "matches already returned" rather than an index position: only a record that passed the
|
||||
/// mask is counted, so a filtered walk resumes where it left off.
|
||||
/// Offer one record, class and all. `false` means nothing more will fit in this batch.
|
||||
///
|
||||
/// One frame for every walk. It used to be two — `key | len | value` for the plain walks and
|
||||
/// `key | flags | len | value` for the scan — which meant a caller could not ask a listing what
|
||||
/// class its records were, and a *store* could not answer `entries_flagged` from a kernel store
|
||||
/// at all. A walk that cannot say what a record is forces a second reader of the format to find
|
||||
/// out, and that is the thing this whole arrangement exists to avoid.
|
||||
///
|
||||
/// `space` is carried only when `with_space` is set — see [`Batch::with_space`].
|
||||
fn offer_flagged(
|
||||
&mut self,
|
||||
space: &[u8; SPACE_ID_LEN],
|
||||
key: &[u8],
|
||||
flags: u16,
|
||||
value: &[u8],
|
||||
) -> bool {
|
||||
fn offer(&mut self, space: &[u8; SPACE_ID_LEN], key: &[u8], flags: u16, value: &[u8]) -> bool {
|
||||
self.seen += 1;
|
||||
if self.seen <= self.cursor {
|
||||
return true;
|
||||
@@ -3020,8 +3006,8 @@ unsafe fn v3_enum(
|
||||
let mut entry = KVVec::<u8>::new();
|
||||
let mut at = first + cursor;
|
||||
while at < first + records {
|
||||
let (value_off, value_len, _) = match image.index_entry(at, &mut entry) {
|
||||
Ok(pair) => pair,
|
||||
let (value_off, value_len, flags) = match image.index_entry(at, &mut entry) {
|
||||
Ok(triple) => triple,
|
||||
Err(e) => return -(e.to_errno() as i32),
|
||||
};
|
||||
let value = match image.value_at(value_off, value_len) {
|
||||
@@ -3032,7 +3018,7 @@ unsafe fn v3_enum(
|
||||
Ok(k) => k,
|
||||
Err(_) => return -22,
|
||||
};
|
||||
if !batch.offer(key, value.as_slice()) {
|
||||
if !batch.offer(&wanted, key, flags, value.as_slice()) {
|
||||
break;
|
||||
}
|
||||
at += 1;
|
||||
@@ -3061,38 +3047,32 @@ unsafe fn v3_enum(
|
||||
None => None,
|
||||
};
|
||||
match (edit, image_entry, image_key) {
|
||||
(Some(e), Some((_, value_len, _)), Some(key)) if e.key == key => {
|
||||
let _ = value_len;
|
||||
(Some(e), Some((_, _, _)), Some(key)) if e.key == key => {
|
||||
edit_at += 1;
|
||||
at += 1;
|
||||
if !e.deleted && !batch.offer(e.key, e.value) {
|
||||
if !e.deleted && !batch.offer(&wanted, e.key, e.flags, e.value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
(Some(e), Some((value_off, value_len, _)), Some(key)) if e.key < key => {
|
||||
(Some(e), Some((_, _, _)), Some(key)) if e.key < key => {
|
||||
edit_at += 1;
|
||||
if !e.deleted && !batch.offer(e.key, e.value) {
|
||||
if !e.deleted && !batch.offer(&wanted, e.key, e.flags, e.value) {
|
||||
break;
|
||||
}
|
||||
let _ = (value_off, value_len);
|
||||
}
|
||||
(_, Some((value_off, value_len, _)), Some(_)) => {
|
||||
(_, Some((value_off, value_len, flags)), Some(key)) => {
|
||||
let value = match image.value_at(value_off, value_len) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return -(e.to_errno() as i32),
|
||||
};
|
||||
let key = match entry.as_slice()[..RAW_KEY_LEN].try_into() {
|
||||
Ok(k) => k,
|
||||
Err(_) => return -22,
|
||||
};
|
||||
at += 1;
|
||||
if !batch.offer(key, value.as_slice()) {
|
||||
if !batch.offer(&wanted, key, flags, value.as_slice()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
(Some(e), None, _) => {
|
||||
edit_at += 1;
|
||||
if !e.deleted && !batch.offer(e.key, e.value) {
|
||||
if !e.deleted && !batch.offer(&wanted, e.key, e.flags, e.value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3235,31 +3215,40 @@ unsafe fn v3_range(
|
||||
(Some(e), Some(_), Some(key)) if e.key == key => {
|
||||
edit_at += 1;
|
||||
at += 1;
|
||||
if !e.deleted && region.holds(e.key) && !batch.offer(e.key, e.value) {
|
||||
if !e.deleted
|
||||
&& region.holds(e.key)
|
||||
&& !batch.offer(&wanted, e.key, e.flags, e.value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// A record only the log holds, in its key's place.
|
||||
(Some(e), Some(_), Some(key)) if e.key < key => {
|
||||
edit_at += 1;
|
||||
if !e.deleted && region.holds(e.key) && !batch.offer(e.key, e.value) {
|
||||
if !e.deleted
|
||||
&& region.holds(e.key)
|
||||
&& !batch.offer(&wanted, e.key, e.flags, e.value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// The image's own record, which the log says nothing about.
|
||||
(_, Some((value_off, value_len, _)), Some(key)) => {
|
||||
(_, Some((value_off, value_len, flags)), Some(key)) => {
|
||||
let value = match image.value_at(value_off, value_len) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return -(e.to_errno() as i32),
|
||||
};
|
||||
at += 1;
|
||||
if region.holds(key) && !batch.offer(key, value.as_slice()) {
|
||||
if region.holds(key) && !batch.offer(&wanted, key, flags, value.as_slice()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
(Some(e), None, _) => {
|
||||
edit_at += 1;
|
||||
if !e.deleted && region.holds(e.key) && !batch.offer(e.key, e.value) {
|
||||
if !e.deleted
|
||||
&& region.holds(e.key)
|
||||
&& !batch.offer(&wanted, e.key, e.flags, e.value)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3390,7 +3379,7 @@ fn flag_scan_space(
|
||||
at += 1;
|
||||
if !e.deleted
|
||||
&& flag_matches(e.flags, mask, mode)
|
||||
&& !batch.offer_flagged(wanted, e.key, e.flags, e.value)
|
||||
&& !batch.offer(wanted, e.key, e.flags, e.value)
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -3400,7 +3389,7 @@ fn flag_scan_space(
|
||||
edit_at += 1;
|
||||
if !e.deleted
|
||||
&& flag_matches(e.flags, mask, mode)
|
||||
&& !batch.offer_flagged(wanted, e.key, e.flags, e.value)
|
||||
&& !batch.offer(wanted, e.key, e.flags, e.value)
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -3410,7 +3399,7 @@ fn flag_scan_space(
|
||||
let value = image.value_at(value_off, value_len)?;
|
||||
at += 1;
|
||||
if flag_matches(flags, mask, mode)
|
||||
&& !batch.offer_flagged(wanted, key, flags, value.as_slice())
|
||||
&& !batch.offer(wanted, key, flags, value.as_slice())
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -3419,7 +3408,7 @@ fn flag_scan_space(
|
||||
edit_at += 1;
|
||||
if !e.deleted
|
||||
&& flag_matches(e.flags, mask, mode)
|
||||
&& !batch.offer_flagged(wanted, e.key, e.flags, e.value)
|
||||
&& !batch.offer(wanted, e.key, e.flags, e.value)
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -3459,7 +3448,7 @@ fn flag_scan_log_space(
|
||||
edit_at += 1;
|
||||
if !e.deleted
|
||||
&& flag_matches(e.flags, mask, mode)
|
||||
&& !batch.offer_flagged(space, e.key, e.flags, e.value)
|
||||
&& !batch.offer(space, e.key, e.flags, e.value)
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -3805,8 +3794,8 @@ pub unsafe extern "C" fn cubelinux_kernel_enum(
|
||||
too_big: 0,
|
||||
with_space: false,
|
||||
};
|
||||
while let Some((key, value)) = walker.next() {
|
||||
if !batch.offer(key, value) {
|
||||
while let Some((key, flags, value)) = walker.next() {
|
||||
if !batch.offer(&wanted, key, flags, value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3908,7 +3897,7 @@ pub unsafe extern "C" fn cubelinux_kernel_range(
|
||||
too_big: 0,
|
||||
with_space: false,
|
||||
};
|
||||
while let Some((key, value)) = walker.next() {
|
||||
while let Some((key, flags, value)) = walker.next() {
|
||||
match region.place(key) {
|
||||
// Below the span: not in the box, and skipping it is the seek's substitute on a layout
|
||||
// that has no index to search.
|
||||
@@ -3917,7 +3906,7 @@ pub unsafe extern "C" fn cubelinux_kernel_range(
|
||||
Span::Above => break,
|
||||
Span::Inside => {}
|
||||
}
|
||||
if region.holds(key) && !batch.offer(key, value) {
|
||||
if region.holds(key) && !batch.offer(&wanted, key, flags, value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user