1 // Copyright 2018 Google Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License.  You may obtain a copy
5 // of the License at:
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
12 // License for the specific language governing permissions and limitations
13 // under the License.
14 
15 extern crate fuse;
new(stream: &'a mut St) -> Self16 extern crate time;
17 
18 use {create_as, IdGenerator};
19 use failure::{Fallible, ResultExt};
20 use nix::{errno, fcntl, sys, unistd};
21 use nix::dir as rawdir;
is_terminated(&self) -> bool22 use nodes::{
23     ArcHandle, ArcNode, AttrDelta, Cache, Handle, KernelError, Node, NodeResult, conv, setattr};
24 use std::collections::HashMap;
25 use std::ffi::{OsStr, OsString};
26 use std::os::unix::ffi::OsStrExt;
27 use std::os::unix::fs::{self as unix_fs, DirBuilderExt, OpenOptionsExt};
28 use std::fs;
29 use std::io;
poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>30 use std::path::{Component, Path, PathBuf};
31 use std::sync::{Arc, Mutex};
32 
33 /// Takes the components of a path and returns the first normal component and the rest.
34 ///
35 /// This assumes that the input path is normalized and that the very first component is a normal
36 /// component as defined by `Component::Normal`.
37 fn split_components<'a>(components: &'a [Component<'a>]) -> (&'a OsStr, &'a [Component<'a>]) {
38     debug_assert!(!components.is_empty());
39     let name = match components[0] {
40         Component::Normal(name) => name,
41         _ => panic!("Input list of components is not normalized"),
42     };
43     (name, &components[1..])
44 }
45 
46 /// Contents of a single `fuse::ReplyDirectory` reply; used for pagination.
47 struct ReplyEntry {
48     inode: u64,
49     fs_type: fuse::FileType,
50     name: OsString,
51 }
52 
53 /// Handle for an open directory.
54 struct OpenDir {
55     // These are copies of the fields that also exist in the Dir corresponding to this OpenDir.
56     // Ideally we could just hold an immutable reference to the Dir instance... but this is hard
57     // to do because, when opendir() gets called on an abstract Node, we do not get access to the
58     // ArcNode that corresponds to it (to clone it).  Given that these values are immutable on
59     // the node, holding a copy here is fine.
60     inode: u64,
61     writable: bool,
62     state: Arc<Mutex<MutableDir>>,
63 
64     /// Handle for the open directory file descriptor.  This is `None` if the directory does not
65     /// have an underlying path.
66     handle: Mutex<Option<rawdir::Dir>>,
67 
68     /// Contents of this directory.  This is populated on the first `readdir` request that has an
69     /// offset of zero and reused for all further calls until the contents are consumed.
70     ///
71     /// Doing this means that mutations to the directory won't be visible to `readdir` while a
72     /// "stream" of partial `readdir` calls is in progress.  This is probably a good thing.
73     reply_contents: Mutex<Vec<ReplyEntry>>,
74 }
75 
76 impl OpenDir {
77     /// Reads all directory entries in one go.
78     ///
79     /// `_ids` and `_cache` are the file system-wide bookkeeping objects needed to instantiate new
80     /// nodes, used when readdir discovers an underlying node that was not yet known.
81     fn readdirall(&self, ids: &IdGenerator, cache: &dyn Cache) -> NodeResult<Vec<ReplyEntry>> {
82         let mut reply = vec!();
83 
84         let mut state = self.state.lock().unwrap();
85 
86         reply.push(ReplyEntry {
87             inode: self.inode,
88             fs_type: fuse::FileType::Directory,
89             name: OsString::from(".")
90         });
91         reply.push(ReplyEntry {
92             inode: state.parent,
93             fs_type: fuse::FileType::Directory,
94             name: OsString::from("..")
95         });
96 
97         // First, return the entries that correspond to explicit mappings performed by the user at
98         // either mount time or during a reconfiguration.  Those should clobber any on-disk
99         // contents that we discover later when we issue the readdir on the underlying directory,
100         // if any.
101         for (name, dirent) in &state.children {
102             if dirent.explicit_mapping {
103                 reply.push(ReplyEntry {
104                     inode: dirent.node.inode(),
105                     fs_type: dirent.node.file_type_cached(),
106                     name: name.clone()
107                 });
108             }
109         }
110 
111         let mut handle = self.handle.lock().unwrap();
112 
113         if handle.is_none() {
114             debug_assert!(state.underlying_path.is_none());
115             return Ok(reply);
116         }
117         debug_assert!(state.underlying_path.is_some());
118         let handle = handle.as_mut().unwrap();
119         for entry in handle.iter() {
120             let entry = entry?;
121             let name = entry.file_name();
122 
123             let name = OsStr::from_bytes(name.to_bytes()).to_os_string();
124 
125             if name == "." || name == ".." {
126                 continue;
127             }
128 
129             if let Some(dirent) = state.children.get(&name) {
130                 // Found a previously-known on-disk entry.  Must return it "as is" (even if its
131                 // type might have changed) because, if we called into `cache.get_or_create` below,
132                 // we might recreate the node unintentionally.  Note that mappings were handled
133                 // earlier, so only handle the non-mapping case here.
134                 if !dirent.explicit_mapping {
135                     reply.push(ReplyEntry {
136                         inode: dirent.node.inode(),
137                         fs_type: dirent.node.file_type_cached(),
138                         name: name.clone(),
139                     });
140                 }
141                 continue;
142             }
143 
144             let path = state.underlying_path.as_ref().unwrap().join(&name);
145 
146             // TODO(jmmv): In theory we shouldn't need to issue a stat for every entry during a
147             // readdir.  However, it's much easier to handle things this way because we currently
148             // require a file's metadata in order to instantiate a node.  Note that the Go variant
149             // of this code does the same and an attempt to "fix" this resulted in more complex
150             // code and no visible performance gains.  That said, it'd be worth to investigate this
151             // again.
152             let fs_attr = fs::symlink_metadata(&path)?;
153 
154             let fs_type = conv::filetype_fs_to_fuse(&path, fs_attr.file_type());
155             let child = cache.get_or_create(ids, &path, &fs_attr, self.writable);
156 
157             reply.push(ReplyEntry { inode: child.inode(), fs_type: fs_type, name: name.clone() });
158 
159             // Do the insertion into state.children after calling reply.add() to be able to move
160             // the name into the key without having to copy it again.
161             let dirent = Dirent {
162                 node: child.clone(),
163                 explicit_mapping: false,
164             };
165             // TODO(jmmv): We should remove stale entries at some point (possibly here), but the Go
166             // variant does not do this so any implications of this are not tested.  The reason this
167             // hasn't caused trouble yet is because: on readdir, we don't use any contents from
168             // state.children that correspond to unmapped entries, and any stale entries visited
169             // during lookup will result in an ENOENT.
170             state.children.insert(name, dirent);
171         }
172         // No need to worry about rewinding handle.iter() for future reads on the same OpenDir:
173         // the rawdir::Dir implementation does this for us.
174         Ok(reply)
175     }
176 }
177 
178 impl Handle for OpenDir {
179     fn readdir(&self, ids: &IdGenerator, cache: &dyn Cache, offset: i64,
180         reply: &mut fuse::ReplyDirectory) -> NodeResult<()> {
181         let mut offset: usize = offset as usize;
182 
183         let mut contents = self.reply_contents.lock().unwrap();
184         if offset == 0 {
185             *contents = self.readdirall(ids, cache)?;
186         } else {
187             // When the kernel asks us to return extra entries from a partially-read directory, it
188             // does so by giving us the offset of the last entry we returned -- not the first one
189             // that we ought to return.  Therefore, advance the offset by one to avoid duplicate
190             // return values and to avoid entering an infinite loop.
191             offset += 1;
192         }
193 
194         while offset < contents.len() {
195             let entry = &contents[offset];
196             if reply.add(entry.inode, offset as i64, entry.fs_type, &entry.name) {
197                 break;  // Reply buffer is full.
198             }
199             offset += 1;
200         }
201         Ok(())
202     }
203 }
204 
205 /// Representation of a directory entry.
206 #[derive(Clone)]
207 pub struct Dirent {
208     node: ArcNode,
209     explicit_mapping: bool,
210 }
211 
212 /// Representation of a directory node.
213 pub struct Dir {
214     inode: u64,
215     writable: bool,
216     state: Arc<Mutex<MutableDir>>,
217 }
218 
219 /// Holds the mutable data of a directory node.
220 pub struct MutableDir {
221     parent: u64,
222     underlying_path: Option<PathBuf>,
223     attr: fuse::FileAttr,
224     children: HashMap<OsString, Dirent>,
225 }
226 
227 impl Dir {
228     /// Creates a new scaffold directory to represent an in-memory directory.
229     ///
230     /// The directory's timestamps are set to `now` and the ownership is set to the current user.
231     pub fn new_empty(inode: u64, parent: Option<&dyn Node>, now: time::Timespec) -> ArcNode {
232         let attr = fuse::FileAttr {
233             ino: inode,
234             kind: fuse::FileType::Directory,
235             nlink: 2,  // "." entry plus whichever initial named node points at this.
236             size: 2,  // TODO(jmmv): Reevaluate what directory sizes should be.
237             blocks: 1,  // TODO(jmmv): Reevaluate what directory blocks should be.
238             atime: now,
239             mtime: now,
240             ctime: now,
241             crtime: now,
242             perm: 0o555 as u16,  // Scaffold directories cannot be mutated by the user.
243             uid: unistd::getuid().as_raw(),
244             gid: unistd::getgid().as_raw(),
245             rdev: 0,
246             flags: 0,
247         };
248 
249         let state = MutableDir {
250             parent: parent.map_or(inode, Node::inode),
251             underlying_path: None,
252             attr: attr,
253             children: HashMap::new(),
254         };
255 
256         Arc::new(Dir {
257             inode: inode,
258             writable: false,
259             state: Arc::from(Mutex::from(state)),
260         })
261     }
262 
263     /// Creates a new directory whose contents are backed by another directory.
264     ///
265     /// `inode` is the node number to assign to the created in-memory directory and has no relation
266     /// to the underlying directory.  `underlying_path` indicates the path to the directory outside
267     /// of the sandbox that backs this one.  `fs_attr` contains the stat data for the given path.
268     ///
269     /// `fs_attr` is an input parameter because, by the time we decide to instantiate a directory
270     /// node (e.g. as we discover directory entries during readdir or lookup), we have already
271     /// issued a stat on the underlying file system and we cannot re-do it for efficiency reasons.
272     pub fn new_mapped(inode: u64, underlying_path: &Path, fs_attr: &fs::Metadata, writable: bool)
273         -> ArcNode {
274         if !fs_attr.is_dir() {
275             panic!("Can only construct based on dirs");
276         }
277 
278         // For directories, assume a fixed link count of 2 that does not change throughout the
279         // lifetime of the directory (except for its own removal).
280         //
281         // An alternative would be to inherit the link count from the underlying file system and
282         // trust that it is accurate in the (typical) absence of hard links for directories.
283         // I tried that on 2020-02-10 on macOS Catalina with APFS and discovered that this
284         // assumption does not work because this system seems to count *all* directory entries as
285         // links (not just subdirectories).
286         let nlink = 2;
287 
288         let attr = conv::attr_fs_to_fuse(underlying_path, inode, nlink, &fs_attr);
289 
290         let state = MutableDir {
291             parent: inode,
292             underlying_path: Some(PathBuf::from(underlying_path)),
293             attr: attr,
294             children: HashMap::new(),
295         };
296 
297         Arc::new(Dir { inode, writable, state: Arc::from(Mutex::from(state)) })
298     }
299 
300     /// Creates a new scaffold directory as a child of the current one.
301     ///
302     /// Errors are all logged, not reported.  The rationale is that a scaffold directory for an
303     /// intermediate path component of a mapping has to always be created, as it takes preference
304     /// over any other on-disk contents.
305     ///
306     /// This is purely a helper function for `map`.  As a result, the caller is responsible for
307     /// inserting the new directory into the children of the current directory.
308     fn new_scaffold_child(&self, underlying_path: Option<&PathBuf>, name: &OsStr, ids: &IdGenerator,
309         now: time::Timespec) -> ArcNode {
310         if let Some(path) = underlying_path {
311             let child_path = path.join(name);
312             match fs::symlink_metadata(&child_path) {
313                 Ok(fs_attr) => {
314                     if fs_attr.is_dir() {
315                         return Dir::new_mapped(ids.next(), &child_path, &fs_attr, self.writable);
316                     }
317 
318                     info!("Mapping clobbers non-directory {} with an immutable directory",
319                         child_path.display());
320                 },
321                 Err(e) => {
322                     if e.kind() != io::ErrorKind::NotFound {
323                         warn!("Mapping clobbers {} due to an error: {}", child_path.display(), e);
324                     }
325                 },
326             }
327         }
328         Dir::new_empty(ids.next(), Some(self), now)
329     }
330 
331     /// Same as `getattr` but with the node already locked.
332     fn getattr_locked(inode: u64, state: &mut MutableDir) -> NodeResult<fuse::FileAttr> {
333         if let Some(path) = &state.underlying_path {
334             let fs_attr = fs::symlink_metadata(path)?;
335             if !fs_attr.is_dir() {
336                 warn!("Path {} backing a directory node is no longer a directory; got {:?}",
337                     path.display(), fs_attr.file_type());
338                 return Err(KernelError::from_errno(errno::Errno::EIO));
339             }
340             state.attr = conv::attr_fs_to_fuse(path, inode, state.attr.nlink, &fs_attr);
341         }
342 
343         Ok(state.attr)
344     }
345 
346     /// Gets the underlying path of the entry `name` in this directory.
347     ///
348     /// This also ensures that the entry is writable, which is determined by the directory itself
349     /// being mapped to an underlying path and the entry not being an explicit mapping.
350     fn get_writable_path(state: &mut MutableDir, name: &OsStr) -> NodeResult<PathBuf> {
351         if state.underlying_path.is_none() {
352             return Err(KernelError::from_errno(errno::Errno::EPERM));
353         }
354         let path = state.underlying_path.as_ref().unwrap().join(name);
355 
356         if let Some(node) = state.children.get(name) {
357             if node.explicit_mapping {
358                 return Err(KernelError::from_errno(errno::Errno::EPERM));
359             }
360         };
361 
362         Ok(path)
363     }
364 
365     // Same as `lookup` but with the node already locked.
366     fn lookup_locked(writable: bool, state: &mut MutableDir, name: &OsStr, ids: &IdGenerator,
367         cache: &dyn Cache) -> NodeResult<(ArcNode, fuse::FileAttr)> {
368         if let Some(dirent) = state.children.get(name) {
369             let refreshed_attr = dirent.node.getattr()?;
370             return Ok((dirent.node.clone(), refreshed_attr))
371         }
372 
373         let (child, attr) = {
374             let path = match &state.underlying_path {
375                 Some(underlying_path) => underlying_path.join(name),
376                 None => return Err(KernelError::from_errno(errno::Errno::ENOENT)),
377             };
378             let fs_attr = fs::symlink_metadata(&path)?;
379             let node = cache.get_or_create(ids, &path, &fs_attr, writable);
380             let attr = conv::attr_fs_to_fuse(
381                 path.as_path(), node.inode(), node.getattr()?.nlink, &fs_attr);
382             (node, attr)
383         };
384         let dirent = Dirent {
385             node: child.clone(),
386             explicit_mapping: false,
387         };
388         state.children.insert(name.to_os_string(), dirent);
389         Ok((child, attr))
390     }
391 
392     /// Obtains the node and attributes of an underlying file immediately after its creation.
393     ///
394     /// `writable` and `state` are the properties of the node, passed in as arguments because we
395     /// have to hold the node locked already.
396     ///
397     /// `path` and `name` are the path to the underlying file and the basename to lookup in the
398     /// directory, respectively.  It is expected that the basename of `path` matches `name`.
399     ///
400     /// `exp_type` is the type of the node we expect to find for the just-created file.  If the
401     /// node doesn't match this type, it means we encountered a race on the underlying file system
402     /// and we fail the lookup.  (This is an artifact of how we currently implement this function
403     /// as this condition should just be impossible.)
404     fn post_create_lookup(writable: bool, state: &mut MutableDir, path: &Path, name: &OsStr,
405         exp_type: fuse::FileType, ids: &IdGenerator, cache: &dyn Cache)
406         -> NodeResult<(ArcNode, fuse::FileAttr)> {
407         debug_assert_eq!(path.file_name().unwrap(), name);
408 
409         // TODO(https://github.com/bazelbuild/sandboxfs/issues/43): We abuse lookup here to handle
410         // the node creation and the child insertion into the directory, but we shouldn't do this
411         // because lookup performs an extra stat that we should not be issuing.  But to resolve this
412         // we need to be able to synthesize the returned attr, which means we need to track ctimes
413         // internally.
414         match Dir::lookup_locked(writable, state, name, ids, cache) {
415             Ok((node, attr)) => {
416                 if node.file_type_cached() != exp_type {
417                     warn!("Newly-created file {} was replaced or deleted before create finished",
418                         path.display());
419                     return Err(KernelError::from_errno(errno::Errno::EIO));
420                 }
421                 Ok((node, attr))
422             },
423             Err(e) => {
424                 if let Err(e) = fs::remove_file(&path) {
425                     warn!("Failed to clean up newly-created {}: {}", path.display(), e);
426                 }
427                 Err(e)
428             }
429         }
430     }
431 
432     /// Common implementation for the `rmdir` and `unlink` operations.
433     ///
434     /// The behavior of these operations differs only in the syscall we invoke to delete the
435     /// underlying entry, which is passed in as the `remove` parameter.
436     fn remove_any<R>(&self, name: &OsStr, remove: R, cache: &dyn Cache) -> NodeResult<()>
437         where R: Fn(&PathBuf) -> io::Result<()> {
438         let mut state = self.state.lock().unwrap();
439         let path = Dir::get_writable_path(&mut state, name)?;
440 
441         remove(&path)?;
442 
443         // Removing the underlying path from the cache is not racy within the same directory: we
444         // hold the directory node locked while we perform the operations below to remove the child,
445         // which means that no other lookup on the node can complete.
446         //
447         // However... this is racy if the same underlying path is mapped in more than one location
448         // because different lookups on different nodes could still race against the cache state.
449         // We don't bother for now though: the Rust FUSE library serializes all requests so this
450         // situation cannot arise.
451         let entry = state.children.remove(name)
452             .expect("Presence guaranteed by get_writable_path call above");
453         entry.node.delete(cache);
454         Ok(())
455     }
456 }
457 
458 impl Node for Dir {
459     fn inode(&self) -> u64 {
460         self.inode
461     }
462 
463     fn writable(&self) -> bool {
464         self.writable
465     }
466 
467     fn file_type_cached(&self) -> fuse::FileType {
468         fuse::FileType::Directory
469     }
470 
471     fn delete(&self, cache: &dyn Cache) {
472         let mut state = self.state.lock().unwrap();
473         assert!(
474             state.underlying_path.is_some(),
475             "Delete already called or trying to delete an explicit mapping");
476         cache.delete(state.underlying_path.as_ref().unwrap(), state.attr.kind);
477         state.underlying_path = None;
478         // Make the hard link count for the directory be zero.  This is pretty much arbitrary as the
479         // semantics for hard link counts on directories are not well defined, and thus different
480         // OSes and file systems behave inconsistently.  For example, Linux's FUSE forces this to
481         // zero, and macOS's APFS keeps this at 2.
482         debug_assert!(state.attr.nlink >= 2);
483         state.attr.nlink -= 2;
484     }
485 
486     fn set_underlying_path(&self, path: &Path, cache: &dyn Cache) {
487         let mut state = self.state.lock().unwrap();
488         debug_assert!(state.underlying_path.is_some(),
489             "Renames should not have been allowed in scaffold or deleted nodes");
490         cache.rename(
491             state.underlying_path.as_ref().unwrap(), path.to_owned(), state.attr.kind);
492         state.underlying_path = Some(PathBuf::from(path));
493 
494         // This is racy: if other file operations are going on inside this subtree, they will fail
495         // with ENOENT until we have updated their underlying paths after the move.  However, as we
496         // are currently single-threaded (because the Rust FUSE bindings don't support multiple
497         // threads), we are fine.
498         for (name, dirent) in &state.children {
499             dirent.node.set_underlying_path(&path.join(name), cache);
500         }
501     }
502 
503     fn find_subdir(&self, name: &OsStr, ids: &IdGenerator) -> Fallible<ArcNode> {
504         let mut state = self.state.lock().unwrap();
505 
506         match state.children.get(name) {
507             Some(dirent) => {
508                 ensure!(dirent.explicit_mapping, "Not a mapping");
509                 Ok(dirent.node.clone())
510             },
511             None => {
512                 let child = self.new_scaffold_child(None, name, ids, time::get_time());
513                 let dirent = Dirent { node: child.clone(), explicit_mapping: true };
514                 state.children.insert(name.to_os_string(), dirent);
515                 Ok(child)
516             },
517         }
518     }
519 
520     fn map(&self, components: &[Component], underlying_path: &Path, writable: bool,
521         ids: &IdGenerator, cache: &dyn Cache) -> Fallible<ArcNode> {
522         debug_assert!(
523             !components.is_empty(),
524             "Must not be reached because we don't have the containing ArcNode to return it");
525         let (name, remainder) = split_components(components);
526 
527         let mut state = self.state.lock().unwrap();
528 
529         if let Some(dirent) = state.children.get(name) {
530             // TODO(jmmv): We should probably mark this dirent as an explicit mapping if it already
531             // wasn't, but the Go variant of this code doesn't do this -- so investigate later.
532             ensure!(dirent.node.file_type_cached() == fuse::FileType::Directory
533                 && !remainder.is_empty(), "Already mapped");
534             return dirent.node.map(remainder, underlying_path, writable, ids, cache);
535         }
536 
537         let child = if remainder.is_empty() {
538             let fs_attr = fs::symlink_metadata(underlying_path)
539                 .context(format!("Stat failed for {:?}", underlying_path))?;
540             cache.get_or_create(ids, underlying_path, &fs_attr, writable)
541         } else {
542             self.new_scaffold_child(state.underlying_path.as_ref(), name, ids, time::get_time())
543         };
544 
545         let dirent = Dirent { node: child.clone(), explicit_mapping: true };
546         state.children.insert(name.to_os_string(), dirent);
547 
548         if remainder.is_empty() {
549             Ok(child)
550         } else {
551             ensure!(child.file_type_cached() == fuse::FileType::Directory, "Already mapped");
552             child.map(remainder, underlying_path, writable, ids, cache)
553         }
554     }
555 
556     fn unmap(&self, inodes: &mut Vec<u64>) -> Fallible<()> {
557         let mut state = self.state.lock().unwrap();
558         for dirent in state.children.values() {
559             dirent.node.unmap(inodes)?;
560         }
561         state.children.clear();
562 
563         inodes.push(self.inode);
564         Ok(())
565     }
566 
567     fn unmap_subdir(&self, name: &OsStr, inodes: &mut Vec<u64>) -> Fallible<()> {
568         let mut state = self.state.lock().unwrap();
569         match state.children.remove_entry(name) {
570             Some((name, dirent)) => {
571                 if dirent.explicit_mapping {
572                     dirent.node.unmap(inodes)
573                 } else {
574                     let err = format_err!("{:?} is not a mapping", &name);
575                     state.children.insert(name, dirent);
576                     Err(err)
577                 }
578             },
579             None => Err(format_err!("Unknown entry")),
580         }
581     }
582 
583     #[allow(clippy::type_complexity)]
584     fn create(&self, name: &OsStr, uid: unistd::Uid, gid: unistd::Gid, mode: u32, flags: u32,
585         ids: &IdGenerator, cache: &dyn Cache) -> NodeResult<(ArcNode, ArcHandle, fuse::FileAttr)> {
586         let mut state = self.state.lock().unwrap();
587         let path = Dir::get_writable_path(&mut state, name)?;
588 
589         let mut options = conv::flags_to_openoptions(flags, self.writable)?;
590         options.create(true);
591         options.mode(mode);
592 
593         let file = create_as(&path, uid, gid, |p| options.open(&p), |p| fs::remove_file(&p))?;
594         let (node, attr) = Dir::post_create_lookup(self.writable, &mut state, &path, name,
595             fuse::FileType::RegularFile, ids, cache)?;
596         Ok((node.clone(), node.handle_from(file), attr))
597     }
598 
599     fn getattr(&self) -> NodeResult<fuse::FileAttr> {
600         let mut state = self.state.lock().unwrap();
601         Dir::getattr_locked(self.inode, &mut state)
602     }
603 
604     fn getxattr(&self, name: &OsStr) -> NodeResult<Option<Vec<u8>>> {
605         let state = self.state.lock().unwrap();
606         match &state.underlying_path {
607             Some(path) => Ok(xattr::get(path, name)?),
608             None => Ok(None),
609         }
610     }
611 
612     fn listxattr(&self) -> NodeResult<Option<xattr::XAttrs>> {
613         let state = self.state.lock().unwrap();
614         match &state.underlying_path {
615             Some(path) => Ok(Some(xattr::list(path)?)),
616             None => Ok(None),
617         }
618     }
619 
620     fn lookup(&self, name: &OsStr, ids: &IdGenerator, cache: &dyn Cache)
621         -> NodeResult<(ArcNode, fuse::FileAttr)> {
622         let mut state = self.state.lock().unwrap();
623         Dir::lookup_locked(self.writable, &mut state, name, ids, cache)
624     }
625 
626     fn mkdir(&self, name: &OsStr, uid: unistd::Uid, gid: unistd::Gid, mode: u32, ids: &IdGenerator,
627         cache: &dyn Cache) -> NodeResult<(ArcNode, fuse::FileAttr)> {
628         let mut state = self.state.lock().unwrap();
629         let path = Dir::get_writable_path(&mut state, name)?;
630 
631         create_as(
632             &path, uid, gid,
633             |p| fs::DirBuilder::new().mode(mode).create(&p),
634             |p| fs::remove_dir(&p))?;
635         Dir::post_create_lookup(self.writable, &mut state, &path, name,
636             fuse::FileType::Directory, ids, cache)
637     }
638 
639     fn mknod(&self, name: &OsStr, uid: unistd::Uid, gid: unistd::Gid, mode: u32, rdev: u32,
640         ids: &IdGenerator, cache: &dyn Cache) -> NodeResult<(ArcNode, fuse::FileAttr)> {
641         let mut state = self.state.lock().unwrap();
642         let path = Dir::get_writable_path(&mut state, name)?;
643 
644         if mode > u32::from(std::u16::MAX) {
645             warn!("mknod got too-big mode {} (exceeds {})", mode, std::u16::MAX);
646         }
647         let mode = mode as sys::stat::mode_t;
648 
649         // We have to break apart the incoming mode into a separate file type flag and a permissions
650         // set... only to have mknod() combine them later once again.  Doesn't make a lot of sense
651         // but that the API we get from nix, hence ensure we are doing the right thing.
652         let (sflag, perm) = {
653             let sflag = sys::stat::SFlag::from_bits_truncate(mode);
654             let perm = sys::stat::Mode::from_bits_truncate(mode);
655 
656             let truncated_mode = sflag.bits() | perm.bits();
657             if truncated_mode != mode {
658                 warn!("mknod cannot only handle {} from mode {}", truncated_mode, mode);
659             }
660 
661             (sflag, perm)
662         };
663 
664         let exp_filetype = match sflag {
665             sys::stat::SFlag::S_IFBLK => fuse::FileType::BlockDevice,
666             sys::stat::SFlag::S_IFCHR => fuse::FileType::CharDevice,
667             sys::stat::SFlag::S_IFIFO => fuse::FileType::NamedPipe,
668             sys::stat::SFlag::S_IFREG => fuse::FileType::RegularFile,
669             _ => {
670                 warn!("mknod received request to create {} with type {:?}, which is not supported",
671                     path.display(), sflag);
672                 return Err(KernelError::from_errno(errno::Errno::EIO));
673             },
674         };
675 
676         #[allow(clippy::cast_lossless)]
677         create_as(
678             &path, uid, gid,
679             |p| sys::stat::mknod(p, sflag, perm, rdev as sys::stat::dev_t),
680             |p| unistd::unlink(p))?;
681         Dir::post_create_lookup(self.writable, &mut state, &path, name, exp_filetype, ids, cache)
682     }
683 
684     fn open(&self, flags: u32) -> NodeResult<ArcHandle> {
685         let flags = flags as i32;
686         let oflag = fcntl::OFlag::from_bits_truncate(flags);
687 
688         let handle = {
689             let state = self.state.lock().unwrap();
690 
691             match state.underlying_path.as_ref() {
692                 Some(path) => Some(rawdir::Dir::open(path, oflag, sys::stat::Mode::S_IRUSR)?),
693                 None => None,
694             }
695         };
696 
697         Ok(Arc::from(OpenDir {
698             inode: self.inode,
699             writable: self.writable,
700             state: self.state.clone(),
701             handle: Mutex::from(handle),
702             reply_contents: Mutex::from(vec!()),
703         }))
704     }
705 
706     fn removexattr(&self, name: &OsStr) -> NodeResult<()> {
707         let state = self.state.lock().unwrap();
708         match &state.underlying_path {
709             Some(path) => Ok(xattr::remove(path, name)?),
710             None => Err(KernelError::from_errno(errno::Errno::EACCES)),
711         }
712     }
713 
714     fn rename(&self, old_name: &OsStr, new_name: &OsStr, cache: &dyn Cache) -> NodeResult<()> {
715         let mut state = self.state.lock().unwrap();
716 
717         let old_path = Dir::get_writable_path(&mut state, old_name)?;
718         let new_path = Dir::get_writable_path(&mut state, new_name)?;
719 
720         fs::rename(&old_path, &new_path)?;
721 
722         let dirent = state.children.remove(old_name)
723             .expect("get_writable_path call above ensured the child exists");
724         dirent.node.set_underlying_path(&new_path, cache);
725         state.children.insert(new_name.to_owned(), dirent);
726         Ok(())
727     }
728 
729     fn rename_and_move_source(&self, old_name: &OsStr, new_dir: ArcNode, new_name: &OsStr,
730         cache: &dyn Cache) -> NodeResult<()> {
731         debug_assert!(self.inode != new_dir.as_ref().inode(),
732             "Same-directory renames have to be done via `rename`");
733 
734         let mut state = self.state.lock().unwrap();
735 
736         let old_path = Dir::get_writable_path(&mut state, old_name)?;
737 
738         let (old_name, dirent) = state.children.remove_entry(old_name)
739             .expect("get_writable_path call above ensured the child exists");
740         let result = new_dir.rename_and_move_target(&dirent, &old_path, new_name, cache);
741         if result.is_err() {
742             // "Roll back" any changes we did to the current directory because the rename could not
743             // be completed on the target.
744             state.children.insert(old_name, dirent);
745         }
746         result
747     }
748 
749     fn rename_and_move_target(&self, dirent: &Dirent, old_path: &Path, new_name: &OsStr,
750         cache: &dyn Cache) -> NodeResult<()> {
751         // We are locking the target node while the source node is already locked, so this can
752         // deadlock.  The previous Go implementation of this code ordered the locks based on inode
753         // numbers so we should do the same thing here, but then this two-phase move implementation
754         // does not work.
755         //
756         // The benefit of this implementation, however, is that the rename-and-move operation is
757         // oblivious to the type of the target node type.  We just delegate part of the move to the
758         // target, which can choose to operate as necessary.
759         //
760         // TODO(jmmv): Redo this once FUSE operations can be called concurrently, which at the
761         // moment are serialized because the FUSE library we use does not support concurrency.  We
762         // have an integration test to catch this race, which will ensure this doesn't go unnoticed.
763         let mut state = self.state.lock().unwrap();
764 
765         let new_path = Dir::get_writable_path(&mut state, new_name)?;
766 
767         fs::rename(&old_path, &new_path)?;
768 
769         dirent.node.set_underlying_path(&new_path, cache);
770         state.children.insert(new_name.to_owned(), dirent.clone());
771         Ok(())
772     }
773 
774     fn rmdir(&self, name: &OsStr, cache: &dyn Cache) -> NodeResult<()> {
775         // TODO(jmmv): Figure out how to remove the redundant closure.
776         #[allow(clippy::redundant_closure)]
777         self.remove_any(name, |p| fs::remove_dir(p), cache)
778     }
779 
780     fn setattr(&self, delta: &AttrDelta) -> NodeResult<fuse::FileAttr> {
781         let mut state = self.state.lock().unwrap();
782         state.attr = setattr(state.underlying_path.as_ref(), &state.attr, delta)?;
783         Ok(state.attr)
784     }
785 
786     fn setxattr(&self, name: &OsStr, value: &[u8]) -> NodeResult<()> {
787         let state = self.state.lock().unwrap();
788         match &state.underlying_path {
789             Some(path) => Ok(xattr::set(path, name, value)?),
790             None => Err(KernelError::from_errno(errno::Errno::EACCES)),
791         }
792     }
793 
794     fn symlink(&self, name: &OsStr, link: &Path, uid: unistd::Uid, gid: unistd::Gid,
795         ids: &IdGenerator, cache: &dyn Cache) -> NodeResult<(ArcNode, fuse::FileAttr)> {
796         let mut state = self.state.lock().unwrap();
797         let path = Dir::get_writable_path(&mut state, name)?;
798 
799         create_as(&path, uid, gid, |p| unix_fs::symlink(link, &p), |p| fs::remove_file(&p))?;
800         Dir::post_create_lookup(self.writable, &mut state, &path, name,
801             fuse::FileType::Symlink, ids, cache)
802     }
803 
804     fn unlink(&self, name: &OsStr, cache: &dyn Cache) -> NodeResult<()> {
805         // TODO(jmmv): Figure out how to remove the redundant closure.
806         #[allow(clippy::redundant_closure)]
807         self.remove_any(name, |p| fs::remove_file(p), cache)
808     }
809 }
810