Skip to content

Commit ddbba05

Browse files
committed
refactor(fs): refactor path traversal handling
1 parent b7cb4d3 commit ddbba05

File tree

4 files changed

+377
-460
lines changed

4 files changed

+377
-460
lines changed

src/fs/fuse.rs

Lines changed: 98 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ use crate::fs::{
2929
self, AccessPermission, DirectoryEntry, FileAttr, NodeKind, ObjectInterface, OpenOption,
3030
SeekWhence, VfsNode,
3131
};
32+
use crate::io;
3233
use crate::mm::device_alloc::DeviceAlloc;
3334
use crate::syscalls::Dirent64;
3435
use crate::time::{time_t, timespec};
35-
use crate::{arch, io};
3636

3737
// response out layout eg @ https://github.com/zargony/fuse-rs/blob/bf6d1cf03f3277e35b580f3c7b9999255d72ecf3/src/ll/request.rs#L44
3838
// op in/out sizes/layout: https://github.com/hanwen/go-fuse/blob/204b45dba899dfa147235c255908236d5fde2d32/fuse/opcode.go#L439
@@ -1093,61 +1093,78 @@ impl ObjectInterface for FuseDirectoryHandle {
10931093
}
10941094
}
10951095

1096-
#[derive(Debug)]
1096+
#[derive(Clone, Debug)]
1097+
// The `original_prefix` is the one the directory was originally initialized with,
1098+
// `prefix` is the current prefix (normally, `prefix` has `original_prefix` as its prefix).
1099+
// This distinction is used for symlink resolution and directory traversal.
10971100
pub(crate) struct FuseDirectory {
1098-
prefix: Option<String>,
1099-
attr: FileAttr,
1101+
original_prefix: Arc<str>,
1102+
prefix: String,
11001103
}
11011104

11021105
impl FuseDirectory {
11031106
pub fn new(prefix: Option<String>) -> Self {
1104-
let microseconds = arch::kernel::systemtime::now_micros();
1105-
let t = timespec::from_usec(microseconds as i64);
1106-
11071107
FuseDirectory {
1108-
prefix,
1109-
attr: FileAttr {
1110-
st_mode: AccessPermission::from_bits(0o777).unwrap() | AccessPermission::S_IFDIR,
1111-
st_atim: t,
1112-
st_mtim: t,
1113-
st_ctim: t,
1114-
..Default::default()
1115-
},
1108+
original_prefix: Arc::from(prefix.as_deref().unwrap_or_default()),
1109+
prefix: prefix.unwrap_or_default(),
11161110
}
11171111
}
11181112

1119-
fn traversal_path(&self, components: &[&str]) -> CString {
1120-
let prefix_deref = self.prefix.as_deref();
1121-
let components_with_prefix = prefix_deref.iter().chain(components.iter().rev());
1122-
let path: String = components_with_prefix
1123-
.flat_map(|component| ["/", component])
1124-
.collect();
1125-
if path.is_empty() {
1126-
CString::new("/").unwrap()
1127-
} else {
1128-
CString::new(path).unwrap()
1113+
fn traversal_this(&self) -> CString {
1114+
let mut path = String::new();
1115+
path.push('/');
1116+
path.push_str(&self.prefix);
1117+
CString::new(path).unwrap()
1118+
}
1119+
1120+
fn traversal_path(&self, component: &str) -> CString {
1121+
let mut path = String::new();
1122+
if !self.prefix.is_empty() {
1123+
path.push('/');
1124+
path.push_str(&self.prefix);
11291125
}
1126+
path.push('/');
1127+
path.push_str(component);
1128+
CString::new(path).unwrap()
11301129
}
11311130
}
11321131

1132+
#[async_trait]
11331133
impl VfsNode for FuseDirectory {
11341134
/// Returns the node type
11351135
fn get_kind(&self) -> NodeKind {
11361136
NodeKind::Directory
11371137
}
11381138

1139-
fn get_file_attributes(&self) -> io::Result<FileAttr> {
1140-
Ok(self.attr)
1141-
}
1142-
11431139
fn get_object(&self) -> io::Result<Arc<async_lock::RwLock<dyn ObjectInterface>>> {
11441140
Ok(Arc::new(async_lock::RwLock::new(FuseDirectoryHandle::new(
1145-
self.prefix.clone(),
1141+
if self.prefix.is_empty() {
1142+
None
1143+
} else {
1144+
Some(self.prefix.clone())
1145+
},
11461146
))))
11471147
}
11481148

1149-
fn traverse_readdir(&self, components: &mut Vec<&str>) -> io::Result<Vec<DirectoryEntry>> {
1150-
let path = self.traversal_path(components);
1149+
fn dup(&self) -> Box<dyn VfsNode> {
1150+
Box::new(self.clone())
1151+
}
1152+
1153+
async fn traverse_once(&self, component: &str) -> io::Result<Box<dyn VfsNode>> {
1154+
let mut prefix = self.prefix.clone();
1155+
if !prefix.is_empty() {
1156+
prefix.push('/');
1157+
}
1158+
prefix.push_str(component);
1159+
1160+
Ok(Box::new(Self {
1161+
original_prefix: Arc::clone(&self.original_prefix),
1162+
prefix,
1163+
}))
1164+
}
1165+
1166+
async fn readdir(&self) -> io::Result<Vec<DirectoryEntry>> {
1167+
let path = self.traversal_this();
11511168

11521169
debug!("FUSE opendir: {path:#?}");
11531170

@@ -1226,36 +1243,54 @@ impl VfsNode for FuseDirectory {
12261243
Ok(entries)
12271244
}
12281245

1229-
fn traverse_stat(&self, components: &mut Vec<&str>) -> io::Result<FileAttr> {
1230-
let path = self.traversal_path(components);
1246+
async fn stat(&self) -> io::Result<FileAttr> {
1247+
let mut prefix = alloc::borrow::Cow::Borrowed(&self.prefix);
12311248

1232-
debug!("FUSE stat: {path:#?}");
1249+
loop {
1250+
let mut path = String::new();
1251+
path.push('/');
1252+
path.push_str(&*prefix);
1253+
let path = CString::new(path).unwrap();
12331254

1234-
// Is there a better way to implement this?
1235-
let (cmd, rsp_payload_len) = ops::Lookup::create(path);
1236-
let rsp = get_filesystem_driver()
1237-
.unwrap()
1238-
.lock()
1239-
.send_command(cmd, rsp_payload_len)?;
1255+
debug!("FUSE stat: {path:#?}");
12401256

1241-
if rsp.headers.out_header.error != 0 {
1242-
return Err(Errno::try_from(-rsp.headers.out_header.error).unwrap());
1243-
}
1257+
// Is there a better way to implement this?
1258+
let (cmd, rsp_payload_len) = ops::Lookup::create(path);
1259+
let rsp = get_filesystem_driver()
1260+
.unwrap()
1261+
.lock()
1262+
.send_command(cmd, rsp_payload_len)?;
12441263

1245-
let entry_out = rsp.headers.op_header;
1246-
let attr = entry_out.attr;
1264+
if rsp.headers.out_header.error != 0 {
1265+
break Err(Errno::try_from(-rsp.headers.out_header.error).unwrap());
1266+
}
12471267

1248-
if attr.mode & S_IFMT != S_IFLNK {
1249-
return Ok(FileAttr::from(attr));
1250-
}
1268+
let entry_out = rsp.headers.op_header;
1269+
let attr = entry_out.attr;
1270+
1271+
if attr.mode & S_IFMT != S_IFLNK {
1272+
break Ok(FileAttr::from(attr));
1273+
}
12511274

1252-
let path = readlink(entry_out.nodeid)?;
1253-
let mut components: Vec<&str> = path.split('/').collect();
1254-
self.traverse_stat(&mut components)
1275+
let path = readlink(entry_out.nodeid)?;
1276+
1277+
// rewind and re-traverse
1278+
let mut new_prefix: String = (*self.original_prefix).to_owned();
1279+
if !path.starts_with('/') {
1280+
new_prefix.push('/');
1281+
}
1282+
1283+
// handle infinite recursion
1284+
new_prefix.push_str(&path);
1285+
if &new_prefix == &*prefix {
1286+
break Err(Errno::Loop);
1287+
}
1288+
prefix = alloc::borrow::Cow::Owned(new_prefix);
1289+
}
12551290
}
12561291

1257-
fn traverse_lstat(&self, components: &mut Vec<&str>) -> io::Result<FileAttr> {
1258-
let path = self.traversal_path(components);
1292+
async fn lstat(&self) -> io::Result<FileAttr> {
1293+
let path = self.traversal_this();
12591294

12601295
debug!("FUSE lstat: {path:#?}");
12611296

@@ -1267,13 +1302,13 @@ impl VfsNode for FuseDirectory {
12671302
Ok(FileAttr::from(rsp.headers.op_header.attr))
12681303
}
12691304

1270-
fn traverse_open(
1305+
async fn open(
12711306
&self,
1272-
components: &mut Vec<&str>,
1307+
component: &str,
12731308
opt: OpenOption,
12741309
mode: AccessPermission,
12751310
) -> io::Result<Arc<async_lock::RwLock<dyn ObjectInterface>>> {
1276-
let path = self.traversal_path(components);
1311+
let path = self.traversal_path(component);
12771312

12781313
debug!("FUSE open: {path:#?}, {opt:?} {mode:?}");
12791314

@@ -1345,8 +1380,8 @@ impl VfsNode for FuseDirectory {
13451380
}
13461381
}
13471382

1348-
fn traverse_unlink(&self, components: &mut Vec<&str>) -> io::Result<()> {
1349-
let path = self.traversal_path(components);
1383+
async fn unlink(&self, component: &str) -> io::Result<()> {
1384+
let path = self.traversal_path(component);
13501385

13511386
let (cmd, rsp_payload_len) = ops::Unlink::create(path);
13521387
let rsp = get_filesystem_driver()
@@ -1358,8 +1393,8 @@ impl VfsNode for FuseDirectory {
13581393
Ok(())
13591394
}
13601395

1361-
fn traverse_rmdir(&self, components: &mut Vec<&str>) -> io::Result<()> {
1362-
let path = self.traversal_path(components);
1396+
async fn rmdir(&self, component: &str) -> io::Result<()> {
1397+
let path = self.traversal_path(component);
13631398

13641399
let (cmd, rsp_payload_len) = ops::Rmdir::create(path);
13651400
let rsp = get_filesystem_driver()
@@ -1371,8 +1406,8 @@ impl VfsNode for FuseDirectory {
13711406
Ok(())
13721407
}
13731408

1374-
fn traverse_mkdir(&self, components: &mut Vec<&str>, mode: AccessPermission) -> io::Result<()> {
1375-
let path = self.traversal_path(components);
1409+
async fn mkdir(&self, component: &str, mode: AccessPermission) -> io::Result<()> {
1410+
let path = self.traversal_path(component);
13761411
let (cmd, rsp_payload_len) = ops::Mkdir::create(path, mode.bits());
13771412

13781413
let rsp = get_filesystem_driver()

0 commit comments

Comments
 (0)