plan9/
fid.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use super::protocol::*;
5use lxutil::LxCreateOptions;
6use lxutil::LxFile;
7use lxutil::LxVolume;
8use lxutil::PathBufExt;
9use parking_lot::RwLock;
10use std::path::PathBuf;
11use std::sync::Arc;
12
13// Common trait for fids (files and xattrs)
14// Default implementation is provided for most functions since not all are needed by all fid types.
15pub trait Fid: Send + Sync {
16    fn get_attr(&self) -> lx::Result<(Qid, lx::Stat)> {
17        Err(lx::Error::EINVAL)
18    }
19
20    fn walk(&self, _name: &lx::LxStr) -> lx::Result<Qid> {
21        Err(lx::Error::EINVAL)
22    }
23
24    fn open(&self, _flags: u32) -> lx::Result<Qid> {
25        Err(lx::Error::EINVAL)
26    }
27
28    fn create(&self, _name: &lx::LxStr, _flags: u32, _mode: u32, _gid: u32) -> lx::Result<Qid> {
29        Err(lx::Error::EINVAL)
30    }
31
32    fn read(&self, _offset: u64, _buffer: &mut [u8]) -> lx::Result<u32> {
33        Err(lx::Error::EINVAL)
34    }
35
36    fn write(&self, _offset: u64, _buffer: &[u8]) -> lx::Result<u32> {
37        Err(lx::Error::EINVAL)
38    }
39
40    fn read_dir(&self, _offset: u64, _buffer: &mut [u8]) -> lx::Result<u32> {
41        Err(lx::Error::EINVAL)
42    }
43
44    fn mkdir(&self, _name: &lx::LxStr, _mode: u32, _gid: u32) -> lx::Result<Qid> {
45        Err(lx::Error::EINVAL)
46    }
47
48    fn unlink_at(&self, _name: &lx::LxStr, _flags: u32) -> lx::Result<()> {
49        Err(lx::Error::EINVAL)
50    }
51
52    fn fid_clone(&self) -> Arc<dyn Fid>;
53
54    fn clunk(&self) -> lx::Result<()> {
55        Ok(())
56    }
57}
58
59// Contains the mutable state of a File.
60pub struct FileState {
61    root: Arc<LxVolume>,
62    path: PathBuf,
63    qid: Qid,
64    file: Option<LxFile>,
65}
66
67impl FileState {
68    // Initializes a file and make sure it exists.
69    fn new(root: Arc<LxVolume>) -> lx::Result<FileState> {
70        let mut state = FileState {
71            root,
72            path: PathBuf::new(),
73            qid: Default::default(),
74            file: None,
75        };
76
77        state.validate_exists()?;
78        Ok(state)
79    }
80
81    // Not a full clone; doesn't copy file open state.
82    fn fid_clone(&self) -> FileState {
83        FileState {
84            root: Arc::clone(&self.root),
85            path: self.path.clone(),
86            file: None,
87            ..*self
88        }
89    }
90
91    // Checks if the file exists and sets the qid if it does.
92    fn validate_exists(&mut self) -> lx::Result<()> {
93        let (qid, _) = self.get_attributes()?;
94        self.qid = qid;
95        Ok(())
96    }
97
98    // Open a file.
99    fn open(&mut self, flags: u32) -> lx::Result<Qid> {
100        self.check_not_open()?;
101
102        let flags = Self::open_flags_to_o_flags(flags) | lx::O_NOFOLLOW;
103        let file = self.root.open(&self.path, flags, None)?;
104        self.file = Some(file);
105        Ok(self.qid)
106    }
107
108    // Create a file.
109    fn create(
110        &mut self,
111        name: &lx::LxStr,
112        flags: u32,
113        options: LxCreateOptions,
114    ) -> lx::Result<Qid> {
115        self.check_not_open()?;
116
117        let flags = Self::open_flags_to_o_flags(flags) | lx::O_CREAT | lx::O_NOFOLLOW;
118        let child_path = self.child_path(name)?;
119
120        let file = self.root.open(&child_path, flags, Some(options))?;
121        let (qid, _) = Self::get_file_attributes(&file)?;
122        self.path = child_path;
123        self.file = Some(file);
124        self.qid = qid;
125        Ok(qid)
126    }
127
128    // Enumerate a directory.
129    fn read_dir(&mut self, offset: u64, buffer: &mut [u8]) -> lx::Result<u32> {
130        let mut writer = SliceWriter::new_raw(buffer);
131        let file = self.file.as_mut().ok_or(lx::Error::EBADF)?;
132
133        let mut has_entry = false;
134        let self_qid = self.qid;
135        file.read_dir(offset as lx::off_t, |entry| {
136            has_entry = true;
137
138            // Windows doesn't report the inode number for . and .., so just use the current file's
139            // qid for that.
140            let qid = if entry.inode_nr == 0 {
141                self_qid
142            } else {
143                Qid {
144                    path: entry.inode_nr,
145                    version: 0,
146                    qid_type: Self::get_qid_type_from_mode((entry.file_type as u32) << 12),
147                }
148            };
149
150            Ok(writer.dir_entry(&entry.name, &qid, entry.offset as u64, entry.file_type))
151        })?;
152
153        // If the buffer was too small for even one entry, return an error.
154        if has_entry && writer.size() == 0 {
155            return Err(lx::Error::EINVAL);
156        }
157
158        Ok(writer.size() as u32)
159    }
160
161    pub fn read(&self, offset: u64, buffer: &mut [u8]) -> lx::Result<u32> {
162        assert!(buffer.len() < u32::MAX as usize);
163
164        let file = self.file.as_ref().ok_or(lx::Error::EBADF)?;
165        let size = file.pread(buffer, offset as i64)?;
166        Ok(size as u32)
167    }
168
169    pub fn write(&self, offset: u64, buffer: &[u8], request_uid: lx::uid_t) -> lx::Result<u32> {
170        assert!(buffer.len() < u32::MAX as usize);
171
172        let file = self.file.as_ref().ok_or(lx::Error::EBADF)?;
173        let size = file.pwrite(buffer, offset as i64, request_uid)?;
174        Ok(size as u32)
175    }
176
177    fn child_path(&self, name: &lx::LxStr) -> lx::Result<PathBuf> {
178        let mut path = self.path.clone();
179        path.push_lx(name)?;
180        Ok(path)
181    }
182
183    // Convert a Linux mode_t into a qid type.
184    fn get_qid_type_from_mode(mode: u32) -> u8 {
185        match mode & lx::S_IFMT {
186            lx::S_IFLNK => QID_TYPE_SYMLINK,
187            lx::S_IFDIR => QID_TYPE_DIRECTORY,
188            _ => QID_TYPE_FILE,
189        }
190    }
191
192    fn stat_to_qid(stat: &lx::Stat) -> Qid {
193        Qid {
194            path: stat.inode_nr,
195            version: 0,
196            qid_type: Self::get_qid_type_from_mode(stat.mode),
197        }
198    }
199
200    // Convert the Tlopen flags to Linux open flags.
201    fn open_flags_to_o_flags(flags: u32) -> i32 {
202        let mut result = (flags & !OPEN_FLAG_DIRECTORY) as i32;
203
204        // O_DIRECTORY may not match OPEN_FLAG_DIRECTORY depending on the architecture.
205        if flags & OPEN_FLAG_DIRECTORY != 0 {
206            result |= lx::O_DIRECTORY;
207        }
208
209        result
210    }
211
212    // Return an error if the file is already open.
213    fn check_not_open(&self) -> lx::Result<()> {
214        if self.file.is_some() {
215            return Err(lx::Error::EINVAL);
216        }
217
218        Ok(())
219    }
220
221    // Determine file attributes based on the stored path.
222    fn get_attributes(&self) -> lx::Result<(Qid, lx::Stat)> {
223        let stat = if let Some(file) = self.file.as_ref() {
224            file.fstat()?
225        } else {
226            self.root.lstat(&self.path)?
227        };
228
229        Ok((Self::stat_to_qid(&stat), stat))
230    }
231
232    fn get_file_attributes(file: &LxFile) -> lx::Result<(Qid, lx::Stat)> {
233        let stat = file.fstat()?;
234        Ok((Self::stat_to_qid(&stat), stat))
235    }
236}
237
238// Fid that represents a file on the server.
239pub struct File {
240    uid: u32,
241    state: RwLock<FileState>,
242}
243
244impl File {
245    // Creates a file and makes sure it exists.
246    pub fn new(root: Arc<LxVolume>, uid: u32) -> lx::Result<(File, Qid)> {
247        let state = FileState::new(root)?;
248        let qid = state.qid;
249        let file = File {
250            uid,
251            state: RwLock::new(state),
252        };
253
254        Ok((file, qid))
255    }
256}
257
258impl Fid for File {
259    // Get the file's attributes.
260    fn get_attr(&self) -> lx::Result<(Qid, lx::Stat)> {
261        self.state.read().get_attributes()
262    }
263
264    // Walk to a child, and make sure it exists.
265    fn walk(&self, name: &lx::LxStr) -> lx::Result<Qid> {
266        let mut state = self.state.write();
267        state.path.push_lx(name)?;
268        state.validate_exists()?;
269        Ok(state.qid)
270    }
271
272    // Create a clone of everything except the open file state.
273    fn fid_clone(&self) -> Arc<dyn Fid> {
274        let state = self.state.read().fid_clone();
275        let clone = File {
276            state: RwLock::new(state),
277            ..*self
278        };
279
280        Arc::new(clone)
281    }
282
283    // Open the file.
284    fn open(&self, flags: u32) -> lx::Result<Qid> {
285        self.state.write().open(flags)
286    }
287
288    // Create a new file.
289    fn create(&self, name: &lx::LxStr, flags: u32, mode: u32, gid: u32) -> lx::Result<Qid> {
290        // On Unix, the specified gid, as well as the uid from Tattach, are currently ignored. All
291        // operations are done as the user that's running hvlite.
292        self.state
293            .write()
294            .create(name, flags, LxCreateOptions::new(mode, self.uid, gid))
295    }
296
297    // Read from the file.
298    fn read(&self, offset: u64, buffer: &mut [u8]) -> lx::Result<u32> {
299        let state = self.state.read();
300        state.read(offset, buffer)
301    }
302
303    // Write to the file.
304    fn write(&self, offset: u64, buffer: &[u8]) -> lx::Result<u32> {
305        let state = self.state.read();
306        state.write(offset, buffer, self.uid)
307    }
308
309    // Read directory contents.
310    fn read_dir(&self, offset: u64, buffer: &mut [u8]) -> lx::Result<u32> {
311        let mut state = self.state.write();
312        state.read_dir(offset, buffer)
313    }
314
315    // Create a directory.
316    fn mkdir(&self, name: &lx::LxStr, mode: u32, gid: u32) -> lx::Result<Qid> {
317        // On Unix, the specified gid, as well as the uid from Tattach, are currently ignored. All
318        // operations are done as the user that's running hvlite.
319        let state = self.state.read();
320        let child_path = state.child_path(name)?;
321        let stat = state
322            .root
323            .mkdir_stat(child_path, LxCreateOptions::new(mode, self.uid, gid))?;
324
325        Ok(FileState::stat_to_qid(&stat))
326    }
327
328    // Remove a file or directory.
329    fn unlink_at(&self, name: &lx::LxStr, flags: u32) -> lx::Result<()> {
330        let state = self.state.read();
331        let child_path = state.child_path(name)?;
332        state.root.unlink(child_path, flags as i32)
333    }
334}