-
Notifications
You must be signed in to change notification settings - Fork 42
/
symlink.rs
117 lines (100 loc) · 4.02 KB
/
symlink.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
extern crate fuse;
extern crate time;
use nix::errno;
use nodes::{AttrDelta, KernelError, Node, NodeResult, conv, setattr};
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
/// Representation of a symlink node.
pub struct Symlink {
inode: u64,
writable: bool,
state: Mutex<MutableSymlink>,
}
/// Holds the mutable data of a symlink node.
struct MutableSymlink {
underlying_path: Option<PathBuf>,
attr: fuse::FileAttr,
}
impl Symlink {
/// Creates a new symlink backed by a symlink on an underlying file system.
///
/// `inode` is the node number to assign to the created in-memory symlink and has no relation
/// to the underlying symlink. `underlying_path` indicates the path to the symlink outside
/// of the sandbox that backs this one. `fs_attr` contains the stat data for the given path.
///
/// `fs_attr` is an input parameter because, by the time we decide to instantiate a symlink
/// node (e.g. as we discover directory entries during readdir or lookup), we have already
/// issued a stat on the underlying file system and we cannot re-do it for efficiency reasons.
pub fn new_mapped(inode: u64, underlying_path: &Path, fs_attr: &fs::Metadata, writable: bool)
-> Arc<Node> {
if !fs_attr.file_type().is_symlink() {
panic!("Can only construct based on symlinks");
}
let attr = conv::attr_fs_to_fuse(underlying_path, inode, &fs_attr);
let state = MutableSymlink {
underlying_path: Some(PathBuf::from(underlying_path)),
attr: attr,
};
Arc::new(Symlink { inode, writable, state: Mutex::from(state) })
}
/// Same as `getattr` but with the node already locked.
fn getattr_locked(inode: u64, state: &mut MutableSymlink) -> NodeResult<fuse::FileAttr> {
if let Some(path) = &state.underlying_path {
let fs_attr = fs::symlink_metadata(path)?;
if !fs_attr.file_type().is_symlink() {
warn!("Path {} backing a symlink node is no longer a symlink; got {:?}",
path.display(), fs_attr.file_type());
return Err(KernelError::from_errno(errno::Errno::EIO));
}
state.attr = conv::attr_fs_to_fuse(path, inode, &fs_attr);
}
Ok(state.attr)
}
}
impl Node for Symlink {
fn inode(&self) -> u64 {
self.inode
}
fn writable(&self) -> bool {
self.writable
}
fn file_type_cached(&self) -> fuse::FileType {
fuse::FileType::Symlink
}
fn delete(&self) {
let mut state = self.state.lock().unwrap();
assert!(
state.underlying_path.is_some(),
"Delete already called or trying to delete an explicit mapping");
state.underlying_path = None;
}
fn getattr(&self) -> NodeResult<fuse::FileAttr> {
let mut state = self.state.lock().unwrap();
Symlink::getattr_locked(self.inode, &mut state)
}
fn readlink(&self) -> NodeResult<PathBuf> {
let state = self.state.lock().unwrap();
let path = state.underlying_path.as_ref().expect(
"There is no known API to get the target of a deleted symlink");
Ok(fs::read_link(path)?)
}
fn setattr(&self, delta: &AttrDelta) -> NodeResult<fuse::FileAttr> {
let mut state = self.state.lock().unwrap();
state.attr = setattr(state.underlying_path.as_ref(), &state.attr, delta)?;
Ok(state.attr)
}
}