microsoft / microsoft/WSL

9p filesystem does not honor turning off O_APPEND if a file handle is opened with O_APPEND

Open
#40,649 5 comments 0 reactions 0 assignees View on GitHub
file system wsl2
Dominant language
C++
Stars
33.7k
Forks
1.8k
Avg merge
3d 17h
Merged PRs (30d)
116

Description

### Windows Version

Microsoft Windows [Version 10.0.26200.8457]

### WSL Version

2.6.3.0

### Are you using WSL 1 or WSL 2?

- [x] WSL 2
- [ ] WSL 1

### Kernel Version

6.6.87.2-1

### Distro Version

Ubuntu 24.04.3 LTS on Windows 10 x86_64

### Other Software

_No response_

### Repro Steps

This is a test case written in high-level Rust. Most of the calls here can be mapped to simple raw syscalls. It uses `crates.io` dependencies `nix`, `tokio` and `tempdir` with any necessary features.

The file handle must be created in append mode, and then the append mode is switched off.

```rs
#[cfg(test)]
mod tests {
use std::{io::SeekFrom, os::fd::AsFd};

use nix::fcntl::{FcntlArg, OFlag};
use tempdir::TempDir;
use tokio::{
fs::OpenOptions,
io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt},
};

#[tokio::test]
async fn test_appends_on_off() {
// /mnt/d is a 9p location
let tempdir = TempDir::new_in("/mnt/d/path/to/dir", "test_appends_on_off").unwrap();
let mut file = OpenOptions::new()
.create(true)
.append(true)
.read(true)
.write(true)
.open(tempdir.path().join("test.txt"))
.await
.unwrap();
file.write_all(b"Hello, world!").await.unwrap();

// Disable append mode
let flags =
OFlag::from_bits_retain(nix::fcntl::fcntl(file.as_fd(), FcntlArg::F_GETFL).unwrap());
nix::fcntl::fcntl(
file.as_fd(),
FcntlArg::F_SETFL(flags & !nix::fcntl::OFlag::O_APPEND),
)
.unwrap();

// In this error example, the underlying filesystem does not honor removing O_APPEND. So it appends to the end of the file.
file.seek(SeekFrom::Start(7)).await.unwrap();
file.write_all(b"earth!").await.unwrap();

// The cursor should have been at position 19 due to the previous bug, but it is misreported as position 13. Therefore, the caller could not know that a bug occurred and proceeds as normal.
let current_seek = file.seek(SeekFrom::Current(0)).await.unwrap();
println!("current_seek: {}", current_seek);
assert_eq!(current_seek, 13);

file.seek(SeekFrom::Start(0)).await.unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).await.unwrap();
println!("buffer: {:?}", String::from_utf8_lossy(&buffer));
assert_eq!(buffer, b"Hello, earth!");
}
}
```

### Expected Behavior

The test case above should pass.

### Actual Behavior

The test case above failed at the last line because the last write was treated as an append.

### Diagnostic Logs

_No response_

Contributor guide

Open the contributing guide

Research direction

Start by running the provided Rust test against a file under /mnt/d, focusing on fcntl F_SETFL and the 9p filesystem path. Trace how clearing O_APPEND is handled after the handle is opened in append mode. Done means the seek/write operation updates the file as expected and the test produces "Hello, earth!" with the expected offset.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, linux, rust
Domain
operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.