fuse/
reply.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use super::protocol::*;
5use std::io;
6use std::io::Write;
7use zerocopy::FromZeros;
8use zerocopy::Immutable;
9use zerocopy::IntoBytes;
10use zerocopy::KnownLayout;
11
12/// Trait used by objects that send FUSE replies to the kernel.
13pub trait ReplySender {
14    /// Send the specified buffers to the kernel.
15    fn send(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<()>;
16
17    /// Send an empty reply.
18    fn send_empty(&mut self, unique: u64) -> io::Result<()> {
19        tracing::trace!(unique, "Reply");
20        self.send_error(unique, 0)
21    }
22
23    /// Send a reply with a struct as an argument.
24    fn send_arg<T: std::fmt::Debug + IntoBytes + Immutable + KnownLayout>(
25        &mut self,
26        unique: u64,
27        arg: T,
28    ) -> io::Result<()> {
29        tracing::trace!(unique, ?arg, "reply");
30        let header = make_header(unique, size_of_val(&arg), 0);
31        let header = header.as_bytes();
32        let arg = arg.as_bytes();
33        self.send(&[io::IoSlice::new(header), io::IoSlice::new(arg)])
34    }
35
36    /// Send an error reply.
37    fn send_error(&mut self, unique: u64, error: i32) -> io::Result<()> {
38        if error != 0 {
39            tracing::debug!(unique, error, "reply");
40        }
41
42        let header = make_header(unique, 0, -error);
43        let header = header.as_bytes();
44        self.send(&[io::IoSlice::new(header)])
45    }
46
47    /// Send a reply with arbitrary data.
48    fn send_data(&mut self, unique: u64, data: &[u8]) -> io::Result<()> {
49        tracing::trace!(unique, len = data.len(), "reply");
50        let header = make_header(unique, data.len(), 0);
51        let header = header.as_bytes();
52        self.send(&[io::IoSlice::new(header), io::IoSlice::new(data)])
53    }
54
55    /// Send a reply with a struct argument and arbitrary data.
56    fn send_arg_data<T: std::fmt::Debug + IntoBytes + Immutable + KnownLayout>(
57        &mut self,
58        unique: u64,
59        arg: T,
60        data: &[u8],
61    ) -> io::Result<()> {
62        tracing::trace!(unique, ?arg, len = data.len(), "reply");
63
64        let header = make_header(unique, size_of_val(&arg), 0);
65        let header = header.as_bytes();
66        let arg = arg.as_bytes();
67        self.send(&[
68            io::IoSlice::new(header),
69            io::IoSlice::new(arg),
70            io::IoSlice::new(data),
71        ])
72    }
73
74    /// Send a string reply.
75    fn send_string(&mut self, unique: u64, value: lx::LxString) -> io::Result<()> {
76        tracing::trace!(unique, len = value.len(), "reply");
77        let header = make_header(unique, value.len() + 1, 0);
78        let header = header.as_bytes();
79        self.send(&[
80            io::IoSlice::new(header),
81            io::IoSlice::new(value.as_bytes()),
82            io::IoSlice::new(&[0]),
83        ])
84    }
85}
86
87fn make_header(unique: u64, extra_len: usize, error: i32) -> fuse_out_header {
88    fuse_out_header {
89        len: (size_of::<fuse_out_header>() + extra_len)
90            .try_into()
91            .unwrap(),
92        error,
93        unique,
94    }
95}
96
97/// Helpers for writing a reply for `read_dir` or `read_dir_plus`.
98///
99/// This trait is implemented on `Cursor<&mut Vec<u8>>`. To write directory entries, create an
100/// appropriately-sized vector with `Vec::with_capacity`, wrap it in a `Cursor`, and write entries
101/// using the methods of this trait.
102///
103/// The methods of this trait ensure that the entries are correctly aligned in the buffer.
104pub trait DirEntryWriter {
105    /// Write a directory entry to the buffer.
106    ///
107    /// Returns `true` if the entry fit in the buffer; otherwise, false.
108    fn dir_entry(
109        &mut self,
110        name: impl AsRef<lx::LxStr>,
111        inode_nr: u64,
112        offset: u64,
113        file_type: u32,
114    ) -> bool;
115
116    /// Write a directory entry for `read_dir_plus` to the buffer.
117    ///
118    /// Returns `true` if the entry fit in the buffer; otherwise, false.
119    fn dir_entry_plus(
120        &mut self,
121        name: impl AsRef<lx::LxStr>,
122        offset: u64,
123        entry: fuse_entry_out,
124    ) -> bool;
125
126    /// Checks whether an entry for `read_dir_plus` will fit in the buffer without writing it.
127    ///
128    /// Returns `true` if the entry fit in the buffer; otherwise, false.
129    fn check_dir_entry_plus(&self, name: impl AsRef<lx::LxStr>) -> bool;
130}
131
132impl DirEntryWriter for Vec<u8> {
133    fn dir_entry(
134        &mut self,
135        name: impl AsRef<lx::LxStr>,
136        inode_nr: u64,
137        offset: u64,
138        file_type: u32,
139    ) -> bool {
140        // Determine if it fits in the remaining capacity of the vector.
141        let name = name.as_ref();
142        let size = size_of::<fuse_dirent>() + name.len();
143        let aligned_size = fuse_dirent_align(size);
144        if self.capacity() - self.len() < aligned_size {
145            return false;
146        }
147
148        // Write the entry.
149        let dentry = fuse_dirent {
150            ino: inode_nr,
151            off: offset,
152            namelen: name.len().try_into().unwrap(),
153            file_type,
154        };
155
156        self.write_all(dentry.as_bytes()).unwrap();
157        self.write_all(name.as_bytes()).unwrap();
158
159        // Write padding for the next entry.
160        write_padding(self, aligned_size - size);
161        true
162    }
163
164    fn dir_entry_plus(
165        &mut self,
166        name: impl AsRef<lx::LxStr>,
167        offset: u64,
168        entry: fuse_entry_out,
169    ) -> bool {
170        // Determine if it fits in the remaining capacity of the vector.
171        let name = name.as_ref();
172        let size = size_of::<fuse_direntplus>() + name.len();
173        let aligned_size = fuse_dirent_align(size);
174        if self.capacity() - self.len() < aligned_size {
175            return false;
176        }
177
178        // Write the entry.
179        let mut dentry = fuse_direntplus::new_zeroed();
180        dentry.dirent.ino = entry.attr.ino;
181        dentry.dirent.off = offset;
182        dentry.dirent.namelen = name.len().try_into().unwrap();
183        dentry.dirent.file_type = (entry.attr.mode & lx::S_IFMT) >> 12;
184        dentry.entry_out = entry;
185        self.write_all(dentry.as_bytes()).unwrap();
186        self.write_all(name.as_bytes()).unwrap();
187
188        // Write padding for the next entry.
189        write_padding(self, aligned_size - size);
190        true
191    }
192
193    fn check_dir_entry_plus(&self, name: impl AsRef<lx::LxStr>) -> bool {
194        let size = size_of::<fuse_direntplus>() + name.as_ref().len();
195        let aligned_size = fuse_dirent_align(size);
196        self.capacity() - self.len() >= aligned_size
197    }
198}
199
200/// Write up to 8 zero bytes as padding.
201fn write_padding(writer: &mut impl Write, count: usize) {
202    const PADDING: [u8; 8] = [0; 8];
203    writer.write_all(&PADDING[..count]).unwrap();
204}