Skip to content

BufferedStream<File> doesn't maintain logical position in file with mixed reads/writes #17136

Closed
@ben0x539

Description

@ben0x539

Here's an example:

use std::io::{println, File, Open, Truncate, Write, ReadWrite, BufferedStream};

fn main() {
    let path = Path::new("test");

    let mut file = File::open_mode(&path, Truncate, Write).unwrap();
    file.write_str("123456789").unwrap();
    drop(file);

    let file = File::open_mode(&path, Open, ReadWrite).unwrap();
    let mut bs = BufferedStream::new(file);
    println(bs.read_exact(2).unwrap().into_ascii().into_string().as_slice());
    bs.write_str("xyz").unwrap();
    bs.flush().unwrap();
    println(bs.read_to_string().unwrap().as_slice());
}

This prints

12
3456789

and afterwards the file contains the string 123456789xyz even though logically the write happens between the two reads and should have moved the file position over the 345 without them being returned from read calls instead of appending to the end (or overwriting the wrong bytes if a tiny buffer size is chosen). This makes the buffering leak through the abstraction where a mostly equivalent C program maintains the illusion:

#include <stdio.h>

int main(void) {
  char buf[10] = { 0 };
  FILE* f;

  f = fopen("test", "w");
  fwrite("123456789", 1, 9, f);
  fclose(f);

  f = fopen("test", "r+");
  fread(buf, 1, 2, f);
  puts(buf);
  fwrite("xyz", 1, 3, f);
  fread(buf, 1, 9, f);
  puts(buf);
  fclose(f);

  return 0;
}

printing

12
6789

and leaving the file to contain 12xyz6789. strace suggests glibc just seems to seek backwards (lseek(3, -7, SEEK_CUR)) before the write presumably for the length of the discarded read buffer or otherwise manually keeping track of the logical file position.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions