Skip to content
Snippets Groups Projects
scheme.rs 8.72 KiB
Newer Older
use resource::{Resource, DirResource, FileResource};
Jeremy Soller's avatar
Jeremy Soller committed

use redoxfs::{FileSystem, Node};
Jeremy Soller's avatar
Jeremy Soller committed
use spin::Mutex;
use std::cell::RefCell;
Jeremy Soller's avatar
Jeremy Soller committed
use std::collections::BTreeMap;
Jeremy Soller's avatar
Jeremy Soller committed
use std::str;
use std::sync::atomic::{AtomicUsize, Ordering};
Jeremy Soller's avatar
Jeremy Soller committed

Jeremy Soller's avatar
Jeremy Soller committed
use syscall::error::{Error, Result, EEXIST, EISDIR, ENOTDIR, EPERM, ENOENT, EBADF};
use syscall::scheme::Scheme;
use syscall::{Stat, O_CREAT, O_TRUNC};
Jeremy Soller's avatar
Jeremy Soller committed

pub struct FileScheme {
Jeremy Soller's avatar
Jeremy Soller committed
    fs: RefCell<FileSystem>,
    next_id: AtomicUsize,
    files: Mutex<BTreeMap<usize, Box<Resource>>>
Jeremy Soller's avatar
Jeremy Soller committed
}

impl FileScheme {
    pub fn new(fs: FileSystem) -> FileScheme {
        FileScheme {
Jeremy Soller's avatar
Jeremy Soller committed
            fs: RefCell::new(fs),
            next_id: AtomicUsize::new(1),
            files: Mutex::new(BTreeMap::new())
Jeremy Soller's avatar
Jeremy Soller committed
    fn open_inner(&self, url: &[u8], flags: usize) -> Result<Box<Resource>> {
        let path = str::from_utf8(url).unwrap_or("").trim_matches('/');
Jeremy Soller's avatar
Jeremy Soller committed

        // println!("Open '{}' {:X}", path, flags);

Jeremy Soller's avatar
Jeremy Soller committed
        let mut fs = self.fs.borrow_mut();

Jeremy Soller's avatar
Jeremy Soller committed
        let mut nodes = Vec::new();
Jeremy Soller's avatar
Jeremy Soller committed
        let node_result = fs.path_nodes(path, &mut nodes);
Jeremy Soller's avatar
Jeremy Soller committed

        match node_result {
            Ok(node) => if node.1.is_dir() {
Jeremy Soller's avatar
Jeremy Soller committed
                let mut children = Vec::new();
Jeremy Soller's avatar
Jeremy Soller committed
                try!(fs.child_nodes(&mut children, node.0));
Jeremy Soller's avatar
Jeremy Soller committed
                for child in children.iter() {
                    if let Ok(name) = child.1.name() {
                        if ! data.is_empty() {
Jeremy Soller's avatar
Jeremy Soller committed
                        }
                        data.extend_from_slice(&name.as_bytes());
                        if child.1.is_dir() {
                            data.push(b'/');
                        }
                    }
                }
                return Ok(Box::new(DirResource::new(url, data)));
Jeremy Soller's avatar
Jeremy Soller committed
            } else {
Jeremy Soller's avatar
Jeremy Soller committed
                if flags & O_TRUNC == O_TRUNC {
                    // println!("Truncate {}", path);
Jeremy Soller's avatar
Jeremy Soller committed
                    try!(fs.node_set_len(node.0, 0));
Jeremy Soller's avatar
Jeremy Soller committed
                let size = try!(fs.node_len(node.0));
                return Ok(Box::new(FileResource::new(url, node.0, size)));
Jeremy Soller's avatar
Jeremy Soller committed
            },
            Err(err) => if err.errno == ENOENT && flags & O_CREAT == O_CREAT {
                let mut last_part = String::new();
                for part in path.split('/') {
                    if ! part.is_empty() {
                        last_part = part.to_string();
                    }
                }
                if ! last_part.is_empty() {
                    if let Some(parent) = nodes.last() {
Jeremy Soller's avatar
Jeremy Soller committed
                        let node = try!(fs.create_node(Node::MODE_FILE, &last_part, parent.0));
                        return Ok(Box::new(FileResource::new(url, node.0, 0)));
Jeremy Soller's avatar
Jeremy Soller committed
                    } else {
                        return Err(Error::new(EPERM));
                    }
                } else {
                    return Err(Error::new(EPERM));
                }
            } else {
                return Err(err);
            }
        }
Jeremy Soller's avatar
Jeremy Soller committed
    fn open(&self, url: &[u8], flags: usize) -> Result<usize> {
        let resource = try!(self.open_inner(url, flags));
Jeremy Soller's avatar
Jeremy Soller committed

Jeremy Soller's avatar
Jeremy Soller committed
        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
        self.files.lock().insert(id, resource);
Jeremy Soller's avatar
Jeremy Soller committed
        Ok(id)
    }

Jeremy Soller's avatar
Jeremy Soller committed
    fn mkdir(&self, url: &[u8], _mode: usize) -> Result<usize> {
        let path = str::from_utf8(url).unwrap_or("").trim_matches('/');
Jeremy Soller's avatar
Jeremy Soller committed
        // println!("Mkdir '{}'", path);
Jeremy Soller's avatar
Jeremy Soller committed
        let mut fs = self.fs.borrow_mut();

        let mut nodes = Vec::new();
Jeremy Soller's avatar
Jeremy Soller committed
        match fs.path_nodes(path, &mut nodes) {
            Ok(_node) => Err(Error::new(EEXIST)),
            Err(err) => if err.errno == ENOENT {
                let mut last_part = String::new();
                for part in path.split('/') {
                    if ! part.is_empty() {
                        last_part = part.to_owned();
                    }
                }
                if ! last_part.is_empty() {
                    if let Some(parent) = nodes.last() {
Jeremy Soller's avatar
Jeremy Soller committed
                        fs.create_node(Node::MODE_DIR, &last_part, parent.0).and(Ok(0))
                    } else {
                        Err(Error::new(EPERM))
                    }
                } else {
                    Err(Error::new(EPERM))
                }
            } else {
                Err(err)
            }
        }
Jeremy Soller's avatar
Jeremy Soller committed
    fn rmdir(&self, url: &[u8]) -> Result<usize> {
        let path = str::from_utf8(url).unwrap_or("").trim_matches('/');
Jeremy Soller's avatar
Jeremy Soller committed
        // println!("Rmdir '{}'", path);
Jeremy Soller's avatar
Jeremy Soller committed
        let mut fs = self.fs.borrow_mut();

        let mut nodes = Vec::new();
Jeremy Soller's avatar
Jeremy Soller committed
        let child = try!(fs.path_nodes(path, &mut nodes));
        if let Some(parent) = nodes.last() {
            if child.1.is_dir() {
                if let Ok(child_name) = child.1.name() {
Jeremy Soller's avatar
Jeremy Soller committed
                    fs.remove_node(Node::MODE_DIR, child_name, parent.0).and(Ok(0))
                } else {
                    Err(Error::new(ENOENT))
                }
            } else {
                Err(Error::new(ENOTDIR))
            }
        } else {
            Err(Error::new(EPERM))
        }
Jeremy Soller's avatar
Jeremy Soller committed
    fn unlink(&self, url: &[u8]) -> Result<usize> {
        let path = str::from_utf8(url).unwrap_or("").trim_matches('/');
Jeremy Soller's avatar
Jeremy Soller committed
        // println!("Unlink '{}'", path);
Jeremy Soller's avatar
Jeremy Soller committed
        let mut fs = self.fs.borrow_mut();

        let mut nodes = Vec::new();
Jeremy Soller's avatar
Jeremy Soller committed
        let child = try!(fs.path_nodes(path, &mut nodes));
        if let Some(parent) = nodes.last() {
            if ! child.1.is_dir() {
                if let Ok(child_name) = child.1.name() {
Jeremy Soller's avatar
Jeremy Soller committed
                    fs.remove_node(Node::MODE_FILE, child_name, parent.0).and(Ok(0))
                } else {
                    Err(Error::new(ENOENT))
                }
            } else {
                Err(Error::new(EISDIR))
            }
        } else {
            Err(Error::new(EPERM))
        }
Jeremy Soller's avatar
Jeremy Soller committed
    }

    /* Resource operations */
Jeremy Soller's avatar
Jeremy Soller committed
    #[allow(unused_variables)]
Jeremy Soller's avatar
Jeremy Soller committed
    fn dup(&self, old_id: usize) -> Result<usize> {
Jeremy Soller's avatar
Jeremy Soller committed
        // println!("Dup {}", old_id);
Jeremy Soller's avatar
Jeremy Soller committed

Jeremy Soller's avatar
Jeremy Soller committed
        let mut files = self.files.lock();
        let resource = try!(try!(files.get(&old_id).ok_or(Error::new(EBADF))).dup());
Jeremy Soller's avatar
Jeremy Soller committed

Jeremy Soller's avatar
Jeremy Soller committed
        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
        files.insert(id, resource);
Jeremy Soller's avatar
Jeremy Soller committed

        Ok(id)
    }

Jeremy Soller's avatar
Jeremy Soller committed
    #[allow(unused_variables)]
Jeremy Soller's avatar
Jeremy Soller committed
    fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
Jeremy Soller's avatar
Jeremy Soller committed
        // println!("Read {}, {:X} {}", id, buf.as_ptr() as usize, buf.len());
Jeremy Soller's avatar
Jeremy Soller committed
        let mut files = self.files.lock();
        if let Some(mut file) = files.get_mut(&id) {
            file.read(buf, &mut self.fs.borrow_mut())
Jeremy Soller's avatar
Jeremy Soller committed
        } else {
            Err(Error::new(EBADF))
        }
    }

Jeremy Soller's avatar
Jeremy Soller committed
    fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
Jeremy Soller's avatar
Jeremy Soller committed
        // println!("Write {}, {:X} {}", id, buf.as_ptr() as usize, buf.len());
Jeremy Soller's avatar
Jeremy Soller committed
        let mut files = self.files.lock();
        if let Some(mut file) = files.get_mut(&id) {
            file.write(buf, &mut self.fs.borrow_mut())
Jeremy Soller's avatar
Jeremy Soller committed
        } else {
            Err(Error::new(EBADF))
        }
    }

Jeremy Soller's avatar
Jeremy Soller committed
    fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> {
Jeremy Soller's avatar
Jeremy Soller committed
        // println!("Seek {}, {} {}", id, pos, whence);
Jeremy Soller's avatar
Jeremy Soller committed
        let mut files = self.files.lock();
        if let Some(mut file) = files.get_mut(&id) {
Jeremy Soller's avatar
Jeremy Soller committed
            file.seek(pos, whence)
        } else {
            Err(Error::new(EBADF))
        }
    }

    fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
Jeremy Soller's avatar
Jeremy Soller committed
        // println!("Fpath {}, {:X} {}", id, buf.as_ptr() as usize, buf.len());
Jeremy Soller's avatar
Jeremy Soller committed
        let files = self.files.lock();
        if let Some(file) = files.get(&id) {
Jeremy Soller's avatar
Jeremy Soller committed
            file.path(buf)
        } else {
            Err(Error::new(EBADF))
        }
    }

    fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
Jeremy Soller's avatar
Jeremy Soller committed
        // println!("Fstat {}, {:X}", id, stat as *mut Stat as usize);
Jeremy Soller's avatar
Jeremy Soller committed
        let files = self.files.lock();
        if let Some(file) = files.get(&id) {
Jeremy Soller's avatar
Jeremy Soller committed
            file.stat(stat)
        } else {
            Err(Error::new(EBADF))
        }
    }

Jeremy Soller's avatar
Jeremy Soller committed
    fn fsync(&self, id: usize) -> Result<usize> {
Jeremy Soller's avatar
Jeremy Soller committed
        // println!("Fsync {}", id);
Jeremy Soller's avatar
Jeremy Soller committed
        let mut files = self.files.lock();
        if let Some(mut file) = files.get_mut(&id) {
Jeremy Soller's avatar
Jeremy Soller committed
            file.sync()
        } else {
            Err(Error::new(EBADF))
        }
    }

Jeremy Soller's avatar
Jeremy Soller committed
    fn ftruncate(&self, id: usize, len: usize) -> Result<usize> {
Jeremy Soller's avatar
Jeremy Soller committed
        // println!("Ftruncate {}, {}", id, len);
Jeremy Soller's avatar
Jeremy Soller committed
        let mut files = self.files.lock();
        if let Some(mut file) = files.get_mut(&id) {
            file.truncate(len, &mut self.fs.borrow_mut())
Jeremy Soller's avatar
Jeremy Soller committed
        } else {
            Err(Error::new(EBADF))
        }
    }

Jeremy Soller's avatar
Jeremy Soller committed
    fn close(&self, id: usize) -> Result<usize> {
Jeremy Soller's avatar
Jeremy Soller committed
        // println!("Close {}", id);
Jeremy Soller's avatar
Jeremy Soller committed
        let mut files = self.files.lock();
        if files.remove(&id).is_some() {
Jeremy Soller's avatar
Jeremy Soller committed
            Ok(0)
        } else {
            Err(Error::new(EBADF))
        }
    }
}