// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved.
//
// StratoVirt is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan
// PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//         http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

/// The map length used to extend the file/inode map.
const MAP_EXTEND_LENGTH: usize = 256;
const F_RDLCK: u32 = 0;
const F_WDLCK: u32 = 1;
const F_UNLCK: u32 = 2;
const RLIMIT_NOFILE_MIN: u64 = 20;
/// The inode 0 is reserved, 1 is root inode.
const ROOT_INODE: usize = 1;

use super::fs_ops::*;
use super::fuse_msg::*;
use crate::cmdline::FsConfig;
use anyhow::{bail, Context, Result};
use std::collections::{BTreeMap, HashMap};
use std::ffi::CString;
use std::fs::{read_to_string, File};
use std::mem;
use std::os::unix::io::{AsRawFd, RawFd};
use util::byte_code::ByteCode;
use util::num_ops::round_up;

/// Used as the key of the inode.
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
struct StatKey {
    /// Inode number.
    ino: libc::ino64_t,
    /// ID of device containing file.
    dev: libc::dev_t,
}

/// The entry of the inode/file.
struct Entry<T> {
    /// The value stored in the Entry.
    value: Option<T>,
    /// If the entry is used or not.
    used: bool,
    /// The next free entry.
    free_next: usize,
}

/// The map used to store inodes/files.
struct Map<T> {
    /// The vector used to store Entry.
    list: Vec<Entry<T>>,
    /// The first free entry in list.
    free_head: usize,
}

impl<T> Map<T> {
    fn new() -> Self {
        Map {
            list: Vec::new(),
            free_head: ROOT_INODE,
        }
    }

    fn destroy_map(&mut self) {
        self.list = Vec::new();
        self.free_head = ROOT_INODE;
    }

    /// Add MAP_EXTEND_LENGTH elems to the map.
    fn extend_map(&mut self) {
        let mut next = self.list.len();

        for _ in 0..MAP_EXTEND_LENGTH {
            next += 1;
            self.list.push(Entry {
                value: None,
                used: false,
                free_next: next,
            });
        }
    }

    /// Add entry to the map.
    fn get_map(&mut self, value: T) -> usize {
        let id = self.free_head;
        if id == ROOT_INODE || id == self.list.len() {
            self.extend_map();
        }

        match self.list.get_mut(id) {
            Some(e) => {
                e.value = Some(value);
                e.used = true;
                self.free_head = e.free_next;

                id
            }
            None => 0,
        }
    }

    /// Delete entry from the map.
    fn put_map(&mut self, id: usize) {
        if id >= self.list.len() {
            return;
        }

        if let Some(e) = self.list.get_mut(id) {
            if !e.used {
                return;
            }

            e.value = None;
            e.used = false;
            e.free_next = self.free_head;
            self.free_head = id
        }
    }

    fn get_value(&self, id: usize) -> Option<&T> {
        if let Some(e) = self.list.get(id) {
            e.value.as_ref()
        } else {
            None
        }
    }

    fn get_value_mut(&mut self, id: usize) -> Option<&mut T> {
        if let Some(e) = self.list.get_mut(id) {
            e.value.as_mut()
        } else {
            None
        }
    }
}

/// Used to lock file.
struct FileLock {
    /// The owner of the lock
    lock_owner: u64,
    /// The file which is locked
    file: File,
}

impl FileLock {
    fn new(file: File, lock_owner: u64) -> Self {
        FileLock { lock_owner, file }
    }
}

impl Clone for FileLock {
    fn clone(&self) -> Self {
        FileLock {
            lock_owner: self.lock_owner,
            file: self.file.try_clone().unwrap(),
        }
    }
}

/// The inode info.
struct Inode {
    /// The inode file.
    file: File,
    /// The refcount of the inode.
    nlookup: u64,
    /// Store the map index of the inode.
    node_id: usize,
    /// the file type.
    file_type: u32,
    /// The key of the inode.
    key: StatKey,
    /// The locks on the file of the Inode.
    locks: HashMap<u64, FileLock>,
}

impl Inode {
    fn new(file: File, nlookup: u64, node_id: usize, file_type: u32, key: StatKey) -> Self {
        Inode {
            file,
            nlookup,
            node_id,
            file_type,
            key,
            locks: HashMap::new(),
        }
    }

    fn as_raw_fd(&self) -> RawFd {
        self.file.as_raw_fd()
    }
}

impl Clone for Inode {
    fn clone(&self) -> Self {
        Inode {
            file: self.file.try_clone().unwrap(),
            nlookup: self.nlookup,
            node_id: self.node_id,
            file_type: self.file_type,
            key: self.key,
            locks: self.locks.clone(),
        }
    }
}

fn array_to_cstring(
    #[cfg(target_arch = "x86_64")] array: &[i8],
    #[cfg(target_arch = "aarch64")] array: &[u8],
) -> Result<(usize, CString)> {
    let mut vec = Vec::new();
    for item in array {
        if *item == 0 {
            break;
        }
        vec.push(*item as u8);
    }

    let len = vec.len();
    if len == 0 {
        bail!("convert array to CString failed")
    }

    let cstring = match CString::new(vec) {
        Ok(c) => c,
        Err(_) => bail!("convert array to CString failed"),
    };

    Ok((len, cstring))
}

fn path_is_dot(path: &CString) -> bool {
    let bytes = path.as_bytes();
    if bytes.len() == 1 && bytes[0] == b'.' {
        return true;
    }

    false
}

fn path_is_dotdot(path: &CString) -> bool {
    let bytes = path.as_bytes();
    if bytes.len() == 2 && bytes[0] == b'.' && bytes[1] == b'.' {
        return true;
    }

    false
}

/// Set file resources limits for the process. The limit value
/// must be more than or equal to 20 to ensure that the process
/// can start normally. The limit value must be less than or equal
/// to the value of "/proc/sys/fs/file-max" and "/proc/sys/fs/nr_open".
///
/// # Arguments
///
/// * `limit` - The limit value which needs to be set.
pub fn set_rlimit_nofile(limit: u64) -> Result<()> {
    if limit < RLIMIT_NOFILE_MIN {
        bail!(
            "The limit {} exceeds minimum of files {}",
            limit,
            RLIMIT_NOFILE_MIN
        );
    }

    let max_file_str =
        read_to_string("/proc/sys/fs/file-max").with_context(|| "Failed to read file-max")?;
    let max_file = max_file_str
        .trim()
        .parse::<u64>()
        .with_context(|| "Failed to convert the string of max files")?;
    if limit > max_file {
        bail!("The limit {} exceeds maximum of files {}", limit, max_file);
    }

    let nr_open_str =
        read_to_string("/proc/sys/fs/nr_open").with_context(|| "Failed to read nr_open")?;
    let max_file = nr_open_str
        .trim()
        .parse::<u64>()
        .with_context(|| "Failed to convert the string of nr_open")?;
    if limit > max_file {
        bail!(
            "The limit {} exceeds maximum of nr_open {}",
            limit,
            max_file
        );
    }

    let ret = set_rlimit(limit, limit);
    if ret != FUSE_OK {
        bail!("Failed to set rlimit, err: {}", ret);
    }

    Ok(())
}

/// The management structure of filesystem that contains the management of inodes
/// and the information of files in host directory which needs to be shared.
pub struct FileSystem {
    root_inode: Inode,
    inodes: BTreeMap<StatKey, Inode>,
    inode_key_map: Map<StatKey>,
    file_map: Map<File>,
    proc_dir: File,
}

impl FileSystem {
    /// Create a filesystem management structure.
    ///
    /// # Arguments
    ///
    /// * `source_dir` - The path of the host directory which needs to be shared.
    pub fn new(fs_config: FsConfig) -> Result<Self> {
        let root_dir = fs_config.root_dir.clone();
        let (root_file_opt, ret) = open(CString::new(root_dir).unwrap(), libc::O_PATH);
        if ret != FUSE_OK {
            bail!("Failed to open root file {}", fs_config.root_dir);
        }
        let root_file = root_file_opt.unwrap();
        let (stat, ret) = fstat_at(
            &root_file,
            CString::new("").unwrap(),
            libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW,
        );
        if ret != FUSE_OK {
            bail!("Failed to get stat of root file {}", fs_config.root_dir);
        }
        let key = StatKey {
            ino: stat.st_ino,
            dev: stat.st_dev,
        };
        let mut inode_key_map = Map::new();
        let root_id = inode_key_map.get_map(key);
        // The default folder nlookup is 2
        let root_inode = Inode::new(root_file, 2, root_id, libc::S_IFDIR, key);
        let mut inodes = BTreeMap::new();
        inodes.insert(key, root_inode.clone());
        Ok(FileSystem {
            root_inode,
            inodes,
            inode_key_map,
            file_map: Map::new(),
            proc_dir: fs_config.proc_dir_opt.unwrap(),
        })
    }

    fn find_inode(&self, node_id: usize) -> Option<&Inode> {
        match self.inode_key_map.get_value(node_id) {
            Some(k) => self.inodes.get(k),
            _ => None,
        }
    }

    fn find_mut_inode(&mut self, node_id: usize) -> Option<&mut Inode> {
        match self.inode_key_map.get_value(node_id) {
            Some(k) => self.inodes.get_mut(k),
            _ => None,
        }
    }

    fn unref_inode(&mut self, inode: &mut Inode, count: u64) {
        if count > inode.nlookup {
            inode.nlookup = 0;
        } else {
            inode.nlookup -= count;
        }

        if inode.nlookup == 0 {
            self.inodes.remove(&inode.key);
            self.inode_key_map.put_map(inode.node_id);
        } else if let Some(inode_) = self.find_mut_inode(inode.node_id) {
            inode_.nlookup = inode.nlookup;
        }
    }

    fn create_file_lock(&mut self, node_id: usize, owner: u64) -> (Option<File>, i32) {
        let proc_file = self.proc_dir.try_clone().unwrap();
        let inode = match self.find_mut_inode(node_id) {
            Some(inode_) => inode_,
            None => return (None, libc::EBADF as i32),
        };

        if let Some(lock) = inode.locks.get_mut(&owner) {
            return (Some(lock.file.try_clone().unwrap()), FUSE_OK);
        }

        if inode.file_type & libc::S_IFDIR == 0 && inode.file_type & libc::S_IFREG == 0 {
            return (None, libc::EBADF as i32);
        }

        let (file_opt, ret) = open_at(
            &proc_file,
            CString::new(format!("{}", inode.as_raw_fd())).unwrap(),
            libc::O_RDWR,
            0,
        );

        if ret != FUSE_OK {
            return (None, ret);
        }

        let file = file_opt.unwrap().try_clone().unwrap();
        let file_lock = FileLock::new(file.try_clone().unwrap(), owner);
        inode.locks.insert(owner, file_lock);

        (Some(file), FUSE_OK)
    }

    fn delete_file_lock(&mut self, node_id: usize, owner: u64) -> i32 {
        let inode = match self.find_mut_inode(node_id) {
            Some(inode_) => inode_,
            None => return libc::EBADF as i32,
        };

        inode.locks.remove(&owner);

        FUSE_OK
    }

    fn internal_lookup(
        &mut self,
        parent_inode: &Inode,
        name: CString,
        node_id: &mut u64,
        fuse_attr: &mut FuseAttr,
    ) -> i32 {
        let mut son_name = name.clone();
        if parent_inode.node_id == self.root_inode.node_id && path_is_dotdot(&name) {
            son_name = CString::new(".").unwrap();
        }

        let (stat, ret) = fstat_at(
            &parent_inode.file,
            son_name.clone(),
            libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW,
        );
        if ret != FUSE_OK {
            return ret;
        }

        let key = StatKey {
            ino: stat.st_ino,
            dev: stat.st_dev,
        };
        if let Some(inode) = self.inodes.get_mut(&key) {
            inode.nlookup += 1;
            *node_id = inode.node_id as u64;
        } else {
            let (file_opt, ret) = open_at(
                &parent_inode.file,
                son_name,
                libc::O_PATH | libc::O_NOFOLLOW,
                0,
            );
            if ret != FUSE_OK {
                return ret;
            }

            let map_id = self.inode_key_map.get_map(key);
            if let Some(file) = file_opt {
                self.inodes.insert(
                    key,
                    Inode::new(file, 1, map_id, stat.st_mode & libc::S_IFMT, key),
                );
            }
            *node_id = map_id as u64;
        };

        *fuse_attr = FuseAttr::from_stat(stat);

        FUSE_OK
    }

    /// Look up the directory or file information by name and reply attributes.
    ///
    /// # Arguments
    ///
    /// * `parent_nodeid` - The parent node id that is the starting directory to look up.
    /// * `name` - The name that needs to be looked up.
    /// * `node_id` - The node id that needs to be looked up by name.
    /// * `fuse_attr` - The attributes that needs to be looked up by name.
    pub fn lookup(
        &mut self,
        parent_nodeid: usize,
        name: CString,
        node_id: &mut u64,
        fuse_attr: &mut FuseAttr,
    ) -> i32 {
        let inode = match self.find_inode(parent_nodeid) {
            Some(i) => i.clone(),
            _ => {
                return libc::EBADF as i32;
            }
        };
        self.internal_lookup(&inode, name, node_id, fuse_attr)
    }

    /// When the nlookup of inode is reduced to 0, delete the inode from the management structure.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to find the inode.
    /// * `nlookup` - The number of nlookup for inode needs to be reduced.
    pub fn forget(&mut self, node_id: usize, nlookup: u64) -> i32 {
        let mut inode = match self.find_inode(node_id) {
            Some(i) => i.clone(),
            _ => {
                return libc::EBADF as i32;
            }
        };
        self.unref_inode(&mut inode, nlookup);
        FUSE_OK
    }

    /// Get the attributes of a file or directory.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to find the inode.
    /// * `fuse_attr` - The attributes will be returned by the found inode.
    pub fn getattr(&mut self, node_id: usize, fuse_attr: &mut FuseAttr) -> i32 {
        let inode = match self.find_inode(node_id) {
            Some(i) => i.clone(),
            _ => {
                return libc::EBADF as i32;
            }
        };
        let (stat, ret) = fstat_at(
            &inode.file,
            CString::new("").unwrap(),
            libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW,
        );
        if ret != FUSE_OK {
            return ret;
        }
        *fuse_attr = FuseAttr::from_stat(stat);
        FUSE_OK
    }

    /// Set the attributes of a file or directory.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to find the inode.
    /// * `attr` - The attributes will be set to the found inode.
    /// * `fuse_attr` - The attributes will be returned by the found inode.
    pub fn setattr(
        &mut self,
        node_id: usize,
        attr: &FuseSetattrIn,
        fuse_attr: &mut FuseAttr,
    ) -> i32 {
        if attr.valid & FUSE_SET_ATTR_MODE != 0 {
            if attr.valid & FATTR_FH != 0 {
                match self.file_map.get_value(attr.fh as usize) {
                    Some(file) => {
                        let ret = fchmod(file, attr.mode);
                        if ret != FUSE_OK {
                            return ret;
                        }
                    }
                    _ => {
                        return libc::EBADF as i32;
                    }
                };
            } else {
                match self.find_inode(node_id) {
                    Some(i) => {
                        let ret = fchmod_at(
                            &self.proc_dir,
                            CString::new(format!("{}", &i.file.as_raw_fd())).unwrap(),
                            attr.mode,
                        );
                        if ret != FUSE_OK {
                            return ret;
                        }
                    }
                    _ => {
                        return libc::EBADF as i32;
                    }
                };
            }
        }

        if attr.valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID) != 0 {
            let uid = if attr.valid & FUSE_SET_ATTR_UID != 0 {
                attr.uid
            } else {
                u32::MAX
            };

            let gid = if attr.valid & FUSE_SET_ATTR_GID != 0 {
                attr.gid
            } else {
                u32::MAX
            };

            match self.find_inode(node_id) {
                Some(i) => {
                    let ret = fchown_at(
                        &i.file,
                        CString::new("").unwrap(),
                        uid,
                        gid,
                        libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW,
                    );
                    if ret != FUSE_OK {
                        return ret;
                    }
                }
                _ => {
                    return libc::EBADF as i32;
                }
            };
        }

        if attr.valid & FUSE_SET_ATTR_SIZE != 0 {
            if attr.valid & FATTR_FH != 0 {
                match self.file_map.get_value(attr.fh as usize) {
                    Some(file) => {
                        let ret = ftruncate(file, attr.size);
                        if ret != FUSE_OK {
                            return ret;
                        }
                    }
                    _ => {
                        return libc::EBADF as i32;
                    }
                };
            } else {
                match self.find_inode(node_id) {
                    Some(i) => {
                        if i.file_type & libc::S_IFREG == 0 && i.file_type & libc::S_IFDIR == 0 {
                            return libc::EBADF as i32;
                        }

                        let (file_opt, ret) = open_at(
                            &self.proc_dir,
                            CString::new(format!("{}", &i.file.as_raw_fd())).unwrap(),
                            libc::O_RDWR,
                            0,
                        );
                        if ret != FUSE_OK {
                            return ret;
                        }

                        if let Some(file) = file_opt {
                            let ret = ftruncate(&file, attr.size);
                            if ret != FUSE_OK {
                                return ret;
                            }
                        }
                    }
                    _ => {
                        return libc::EBADF as i32;
                    }
                };
            }
        }

        if attr.valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME) != 0 {
            let (a_sec, a_nsec) = if attr.valid & FUSE_SET_ATTR_ATIME_NOW != 0 {
                (0, libc::UTIME_NOW)
            } else if attr.valid & FUSE_SET_ATTR_ATIME != 0 {
                (attr.atime, attr.atimensec as i64)
            } else {
                (0, libc::UTIME_OMIT)
            };

            let (m_sec, m_nsec) = if attr.valid & FUSE_SET_ATTR_MTIME_NOW != 0 {
                (0, libc::UTIME_NOW)
            } else if attr.valid & FUSE_SET_ATTR_MTIME != 0 {
                (attr.mtime, attr.mtimensec as i64)
            } else {
                (0, libc::UTIME_OMIT)
            };

            if attr.valid & FATTR_FH != 0 {
                match self.file_map.get_value(attr.fh as usize) {
                    Some(file) => {
                        let ret = futimens(file, a_sec, a_nsec, m_sec, m_nsec);
                        if ret != FUSE_OK {
                            return ret;
                        }
                    }
                    _ => {
                        return libc::EBADF as i32;
                    }
                };
            } else {
                match self.find_inode(node_id) {
                    Some(i) => {
                        let ret = utimensat(
                            &self.proc_dir,
                            CString::new(format!("{}", &i.file.as_raw_fd())).unwrap(),
                            a_sec,
                            a_nsec,
                            m_sec,
                            m_nsec,
                            0,
                        );
                        if ret != FUSE_OK {
                            return ret;
                        }
                    }
                    _ => {
                        return libc::EBADF as i32;
                    }
                };
            }
        }

        self.getattr(node_id, fuse_attr)
    }

    /// Get the contexts of the symbolic link into the buffer.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to find the inode.
    /// * `buff` - The buffer is saved by the contexts of the symbolic link.
    pub fn readlink(&self, node_id: usize, buff: &mut Vec<u8>) -> i32 {
        let inode = match self.find_inode(node_id) {
            Some(i) => i.clone(),
            None => {
                return libc::EBADF as i32;
            }
        };

        let (buf_opt, ret) = readlinkat(&inode.file, CString::new("").unwrap());
        if ret != FUSE_OK {
            return ret;
        }

        if let Some(mut buf) = buf_opt {
            buff.append(&mut buf);
        } else {
            return libc::EBADF as i32;
        }

        FUSE_OK
    }

    /// Get the contexts of the symbolic link into the buffer.
    ///
    /// # Arguments
    ///
    /// * `in_header` - The in_header of fuse message used to get uid and gid.
    /// * `name` - The target link name used to create a symbolic link.
    /// * `link_name` - The link name that will be created a symbolic link.
    /// * `node_id` - The node id that is found by name.
    /// * `fuse_attr` - The attributes will be returned by the found inode.
    pub fn symlink(
        &mut self,
        in_header: &FuseInHeader,
        name: CString,
        link_name: CString,
        node_id: &mut u64,
        fuse_attr: &mut FuseAttr,
    ) -> i32 {
        let parent_inode = match self.find_inode(in_header.nodeid as usize) {
            Some(i) => i.clone(),
            _ => {
                return libc::EBADF as i32;
            }
        };

        let mut old_uid = 0_u32;
        let mut old_gid = 0_u32;
        let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid);
        if ret != FUSE_OK {
            return ret;
        }

        let ret = symlinkat(&parent_inode.file, name.clone(), link_name);

        recover_uid_gid(old_uid, old_gid);

        if ret != FUSE_OK {
            return ret;
        }

        self.internal_lookup(&parent_inode, name, node_id, fuse_attr)
    }

    /// Create a file system node(file, device special file or named pipe) by the path name
    /// with the mode and dev in the mknod information.
    ///
    /// # Arguments
    ///
    /// * `in_header` - The in_header of fuse message used to get uid and gid.
    /// * `mknod_in` - The information of mknod to get the permissions and dev.
    /// * `name` - The path name used to create a file system node.
    /// * `node_id` - The node id that is found by name.
    /// * `fuse_attr` - The attributes will be returned by the found inode.
    pub fn mknod(
        &mut self,
        in_header: &FuseInHeader,
        mknod_in: &FuseMknodIn,
        name: CString,
        node_id: &mut u64,
        fuse_attr: &mut FuseAttr,
    ) -> i32 {
        let parent_inode = match self.find_inode(in_header.nodeid as usize) {
            Some(i) => i.clone(),
            _ => {
                return libc::EBADF as i32;
            }
        };

        let mut old_uid = 0_u32;
        let mut old_gid = 0_u32;
        let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid);
        if ret != FUSE_OK {
            return ret;
        }

        let ret = mknodat(
            &parent_inode.file,
            name.clone(),
            mknod_in.mode & !mknod_in.umask,
            mknod_in.rdev,
        );

        recover_uid_gid(old_uid, old_gid);

        if ret != FUSE_OK {
            return ret;
        }

        self.internal_lookup(&parent_inode, name, node_id, fuse_attr)
    }

    /// Create a directory by the name with the permissions in the mkdir information.
    ///
    /// # Arguments
    ///
    /// * `in_header` - The in_header of fuse message used to get uid and gid.
    /// * `mkdir_in` - The information of mkdir used to get permissions.
    /// * `name` - The path name that will be created a directory.
    /// * `node_id` - The node id that is found by the path name.
    /// * `fuse_attr` - The attributes will be returned by the found inode.
    pub fn mkdir(
        &mut self,
        in_header: &FuseInHeader,
        mkdir_in: &FuseMkdirIn,
        name: CString,
        node_id: &mut u64,
        fuse_attr: &mut FuseAttr,
    ) -> i32 {
        let parent_dir = match self.find_inode(in_header.nodeid as usize) {
            Some(i) => i.clone(),
            _ => {
                return libc::EBADF as i32;
            }
        };

        let mut old_uid = 0_u32;
        let mut old_gid = 0_u32;
        let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid);
        if ret != FUSE_OK {
            return ret;
        }

        let ret = mkdir_at(
            &parent_dir.file,
            name.clone(),
            mkdir_in.mode & !mkdir_in.umask,
        );

        recover_uid_gid(old_uid, old_gid);

        if ret != FUSE_OK {
            return ret;
        }

        self.internal_lookup(&parent_dir, name, node_id, fuse_attr)
    }

    /// Delete a name from the host filesystem.
    ///
    /// # Arguments
    ///
    /// * `parent_nodeid` - The parent node id that is the starting directory to look up
    /// in the management of filesystem.
    /// * `name` - The name will be deleted.
    pub fn unlink(&mut self, parent_nodeid: usize, name: CString) -> i32 {
        let parent_inode = match self.find_inode(parent_nodeid) {
            Some(i) => i.clone(),
            None => return libc::EBADF as i32,
        };

        let (stat, ret) = fstat_at(
            &parent_inode.file,
            name.clone(),
            libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW,
        );
        if ret != FUSE_OK {
            return ret;
        }

        let key = StatKey {
            ino: stat.st_ino,
            dev: stat.st_dev,
        };

        match self.inodes.get(&key) {
            Some(i) => i.clone(),
            None => return libc::EIO as i32,
        };

        let ret = unlinkat(&parent_inode.file, name, 0);
        if ret != FUSE_OK {
            return ret;
        }

        FUSE_OK
    }

    /// Delete a directory from the host filesystem by the path name.
    ///
    /// # Arguments
    ///
    /// * `parent_nodeid` - The parent node id that is the starting directory to look up
    /// in the management of filesystem.
    /// * `name` - The path name of the directory will be deleted.
    pub fn rmdir(&mut self, parent_nodeid: usize, name: CString) -> i32 {
        let parent_inode = match self.find_inode(parent_nodeid) {
            Some(i) => i.clone(),
            None => return libc::EBADF as i32,
        };

        let (stat, ret) = fstat_at(
            &parent_inode.file,
            name.clone(),
            libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW,
        );
        if ret != FUSE_OK {
            return ret;
        }

        let key = StatKey {
            ino: stat.st_ino,
            dev: stat.st_dev,
        };

        match self.inodes.get(&key) {
            Some(i) => i.clone(),
            None => return libc::EIO as i32,
        };

        let ret = unlinkat(&parent_inode.file, name, libc::AT_REMOVEDIR);
        if ret != FUSE_OK {
            return ret;
        }

        FUSE_OK
    }

    /// Rename the old path name to the new path name in the host filesystem..
    ///
    /// # Arguments
    ///
    /// * `parent_nodeid` - The parent node id that is the starting directory to look up
    /// for old path name in the management of filesystem.
    /// * `oldname` - The old path name that is relative to the directory of parent node.
    /// * `newparent_nodeid` - The new parent node id that is the starting directory to
    /// look up for new path name in the management of filesystem.
    /// * `newname` - The new path name that is relative to the directory of new parent node.
    pub fn rename(
        &self,
        parent_nodeid: usize,
        oldname: CString,
        newparent_nodeid: usize,
        newname: CString,
    ) -> i32 {
        let parent_inode = match self.find_inode(parent_nodeid) {
            Some(i) => i.clone(),
            None => {
                return libc::EBADF as i32;
            }
        };

        let newparent_inode = match self.find_inode(newparent_nodeid) {
            Some(i) => i.clone(),
            None => {
                return libc::EBADF as i32;
            }
        };

        let (stat, ret) = fstat_at(
            &parent_inode.file,
            oldname.clone(),
            libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW,
        );
        if ret != FUSE_OK {
            return ret;
        }

        let key = StatKey {
            ino: stat.st_ino,
            dev: stat.st_dev,
        };

        match self.inodes.get(&key) {
            Some(_) => {}
            None => return libc::EIO as i32,
        };

        rename(&parent_inode.file, oldname, &newparent_inode.file, newname)
    }

    /// Create a new link to an existing file for the host filesystem.
    ///
    /// # Arguments
    ///
    /// * `parent_nodeid` - The parent node id that is the starting directory to look up.
    /// * `old_nodeid` - The old node id in the management of filesystem.
    /// * `name` - The path name that is relative to the directory of parent node.
    /// * `node_id` - The node id that is found by the path name in the management of filesystem.
    /// * `fuse_attr` - The attributes will be returned by the found inode.
    pub fn link(
        &mut self,
        parent_nodeid: usize,
        old_nodeid: usize,
        name: CString,
        node_id: &mut u64,
        fuse_attr: &mut FuseAttr,
    ) -> i32 {
        let proc_file = self.proc_dir.try_clone().unwrap();
        let parent_inode = match self.find_inode(parent_nodeid) {
            Some(i) => i.clone(),
            None => return libc::EBADF as i32,
        };

        let inode = match self.find_mut_inode(old_nodeid) {
            Some(inode_) => inode_,
            None => return libc::EBADF as i32,
        };

        let ret = linkat(
            &proc_file,
            CString::new(format!("{}", inode.as_raw_fd())).unwrap(),
            &parent_inode.file,
            name,
            libc::AT_SYMLINK_FOLLOW,
        );
        if ret != FUSE_OK {
            return ret;
        }

        let (stat, ret) = fstat_at(
            &inode.file,
            CString::new("").unwrap(),
            libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW,
        );

        if ret != FUSE_OK {
            return ret;
        }

        *fuse_attr = FuseAttr::from_stat(stat);
        *node_id = inode.node_id as u64;
        inode.nlookup += 1;

        FUSE_OK
    }

    /// Open the file with the node id in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to look up the inode.
    /// * `flags` - The flags used to open the file.
    /// * `fh` - The file handler is returned in the management of filesystem.
    pub fn open(&mut self, node_id: usize, flags: u32, fh: &mut u64) -> i32 {
        // File creation should be done with create and mknod fuse messages.
        if (flags & (libc::O_CREAT as u32 | libc::O_TMPFILE as u32)) != 0 {
            return libc::EINVAL;
        }

        let (inode_fd, file_type) = match self.find_inode(node_id) {
            Some(i) => (i.as_raw_fd(), i.file_type),
            None => {
                return libc::EBADF as i32;
            }
        };

        if file_type & libc::S_IFREG == 0 && file_type & libc::S_IFDIR == 0 {
            return libc::EBADF as i32;
        }

        let (file_opt, ret) = open_at(
            &self.proc_dir,
            CString::new(format!("{}", inode_fd)).unwrap(),
            (flags as i32) & !libc::O_NOFOLLOW,
            0,
        );
        if ret != FUSE_OK {
            return ret;
        }

        if let Some(file) = file_opt {
            *fh = self.file_map.get_map(file.try_clone().unwrap()) as u64;
        }

        FUSE_OK
    }

    /// Read the file descriptor by file hander in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `fh` - The file handler in the management of filesystem.
    /// * `fd` - The file descriptor in the host filesystem.
    pub fn read(&mut self, fh: usize, fd: &mut RawFd) -> i32 {
        match self.file_map.get_value(fh) {
            Some(file) => {
                *fd = file.as_raw_fd();
            }
            _ => {
                return libc::EBADF as i32;
            }
        }

        FUSE_OK
    }

    /// write the file descriptor by file hander in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `fh` - The file handler in the management of filesystem.
    /// * `fd` - The file descriptor in the host filesystem.
    pub fn write(&mut self, fh: usize, fd: &mut RawFd) -> i32 {
        match self.file_map.get_value(fh) {
            Some(file) => {
                *fd = file.as_raw_fd();
            }
            _ => {
                return libc::EBADF as i32;
            }
        }

        FUSE_OK
    }

    /// Get the information about a mounted filesystem.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to look up the inode.
    /// * `fuse_statfs` - The information about the mounted filesystem is
    /// returned by the found inode.
    pub fn statfs(&mut self, node_id: usize, fuse_statfs: &mut FuseStatfsOut) -> i32 {
        let inode = match self.find_inode(node_id) {
            Some(i) => i.clone(),
            None => {
                return libc::EBADF as i32;
            }
        };

        let (stat, ret) = fstat_vfs(&inode.file);
        if ret != FUSE_OK {
            return ret;
        }

        *fuse_statfs = FuseStatfsOut::from_stat(stat);

        FUSE_OK
    }

    /// Release the file with file handler in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `fh` - The file handler in the management of filesystem.
    pub fn release(&mut self, fh: usize) -> i32 {
        self.file_map.put_map(fh);

        FUSE_OK
    }

    /// Transfer the file data to the storage device with file handler in
    /// the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `fh` - The file handler in the management of filesystem.
    /// * `datasync` - The datasync indicates whether to use the fdatasync
    /// or fsync interface.
    pub fn fsyncfile(&self, fh: usize, datasync: bool) -> i32 {
        let mut ret = FUSE_OK;

        if fh == u64::max_value() as usize {
            let (inode_fd, file_type) = match self.find_inode(fh) {
                Some(i) => (i.as_raw_fd(), i.file_type),
                None => {
                    return libc::EBADF as i32;
                }
            };

            if file_type & libc::S_IFREG == 0 && file_type & libc::S_IFDIR == 0 {
                return libc::EBADF as i32;
            }

            let (file_opt, ret_) = open_at(
                &self.proc_dir,
                CString::new(format!("{}", inode_fd)).unwrap(),
                libc::O_RDWR,
                0,
            );
            if ret_ != FUSE_OK {
                return ret;
            }

            if let Some(file) = file_opt {
                ret = fsync(&file, datasync);
            } else {
                return libc::EBADF as i32;
            }
        } else {
            match self.file_map.get_value(fh) {
                Some(file) => {
                    ret = fsync(file, datasync);
                }
                _ => {
                    return libc::EBADF as i32;
                }
            }
        }

        ret
    }

    /// Set an extended attribute identified by name and associated with the node id
    /// in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to look up the inode.
    /// * `name` - The name associated with inode.
    /// * `value` - The value of the extended attribute.
    /// * `size` - The size of the value string.
    /// * `flags` - The flags used to set an extended attribute.
    pub fn setxattr(
        &self,
        node_id: usize,
        name: CString,
        value: CString,
        size: u32,
        flags: u32,
    ) -> i32 {
        let inode = match self.find_inode(node_id) {
            Some(i) => i.clone(),
            None => {
                return libc::EBADF as i32;
            }
        };

        if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 {
            let (file_opt, ret_) = open_at(
                &self.proc_dir,
                CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(),
                libc::O_RDONLY,
                0,
            );
            if ret_ != FUSE_OK {
                return ret_;
            }

            if let Some(file) = file_opt {
                fset_xattr(&file, name, value, size, flags)
            } else {
                libc::EBADF as i32
            }
        } else {
            if fchdir(&self.proc_dir) != FUSE_OK {
                panic!("setxattr: failed to change process directoy");
            }

            let ret_ = set_xattr(
                CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(),
                name,
                value,
                size,
                flags,
            );

            if fchdir(&self.root_inode.file) != FUSE_OK {
                panic!("setxattr: failed to change directoy of root inode");
            }

            ret_
        }
    }

    /// Get an extended attribute identified by name and associated with the node id
    /// in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to look up the inode.
    /// * `name` - The name associated with inode.
    /// * `size` - The size of the buffer.
    /// * `buff` - The buffer of the extended attribute.
    pub fn getxattr(&self, node_id: usize, name: CString, size: u32, buff: &mut Vec<u8>) -> i32 {
        let inode = match self.find_inode(node_id) {
            Some(i) => i.clone(),
            None => {
                return libc::EBADF as i32;
            }
        };

        if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 {
            let (file_opt, ret) = open_at(
                &self.proc_dir,
                CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(),
                libc::O_RDONLY,
                0,
            );
            if ret != FUSE_OK {
                return ret;
            }

            if let Some(file) = file_opt {
                let (buf_opt, ret) = fget_xattr(&file, name, size as usize);
                if ret != FUSE_OK {
                    return ret;
                }
                if let Some(mut buf) = buf_opt {
                    buff.append(&mut buf);
                }
            } else {
                return libc::EBADF as i32;
            }
        } else {
            if fchdir(&self.proc_dir) != FUSE_OK {
                panic!("getxattr: failed to change process directoy");
            }

            let (buf_opt, ret) = get_xattr(
                CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(),
                name,
                size as usize,
            );

            if fchdir(&self.root_inode.file) != FUSE_OK {
                panic!("getxattr: failed to change directoy of root inode");
            }
            if ret != FUSE_OK {
                return ret;
            }
            if let Some(mut buf) = buf_opt {
                buff.append(&mut buf);
            }
        }

        FUSE_OK
    }

    /// List extended attribute names associated with the node id
    /// in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to look up the inode.
    /// * `size` - The size of the buffer.
    /// * `buff` - The buffer of the extended attribute.
    pub fn listxattr(&self, node_id: usize, size: u32, buff: &mut Vec<u8>) -> i32 {
        let inode = match self.find_inode(node_id) {
            Some(i) => i.clone(),
            None => {
                return libc::EBADF as i32;
            }
        };

        if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 {
            let (file_opt, ret) = open_at(
                &self.proc_dir,
                CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(),
                libc::O_RDONLY,
                0,
            );
            if ret != FUSE_OK {
                return ret;
            }

            if let Some(file) = file_opt {
                let (buf_opt, ret) = flist_xattr(&file, size as usize);
                if ret != FUSE_OK {
                    return ret;
                }
                if let Some(mut buf) = buf_opt {
                    buff.append(&mut buf);
                }
            } else {
                return libc::EBADF as i32;
            }
        } else {
            if fchdir(&self.proc_dir) != FUSE_OK {
                panic!("listxattr: failed to change process directoy");
            }

            let (buf_opt, ret) = list_xattr(
                CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(),
                size as usize,
            );

            if fchdir(&self.root_inode.file) != FUSE_OK {
                panic!("listxattr: failed to change directoy of root inode");
            }
            if ret != FUSE_OK {
                return ret;
            }
            if let Some(mut buf) = buf_opt {
                buff.append(&mut buf);
            }
        }

        FUSE_OK
    }

    /// Remove an extended attribute identified by name and associated with the node id
    /// in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to look up the inode in the management of filesystem.
    /// * `name` - The name associated with inode.
    pub fn removexattr(&self, node_id: usize, name: CString) -> i32 {
        let inode = match self.find_inode(node_id) {
            Some(i) => i.clone(),
            None => {
                return libc::EBADF as i32;
            }
        };

        if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 {
            let (file_opt, ret) = open_at(
                &self.proc_dir,
                CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(),
                libc::O_RDONLY,
                0,
            );
            if ret != FUSE_OK {
                return ret;
            }

            if let Some(file) = file_opt {
                let ret = fremove_xattr(&file, name);
                if ret != FUSE_OK {
                    return ret;
                }
            } else {
                return libc::EBADF as i32;
            }
        } else {
            if fchdir(&self.proc_dir) != FUSE_OK {
                panic!("removexattr: failed to change process directoy");
            }

            let ret = remove_xattr(
                CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(),
                name,
            );

            if fchdir(&self.root_inode.file) != FUSE_OK {
                panic!("removexattr: failed to change directoy of root inode");
            }
            if ret != FUSE_OK {
                return ret;
            }
        }

        FUSE_OK
    }

    /// Delete the file lock by the node id in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to look up the inode in the management of filesystem.
    /// * `owner` - The name associated with inode.
    pub fn flush(&mut self, node_id: usize, owner: u64) -> i32 {
        self.delete_file_lock(node_id, owner)
    }

    /// Initialize fuse message for getting supported features in the process.
    ///
    /// # Arguments
    ///
    /// * `flags` - The supported features in StratoVirt.
    /// * `support_flags` - The supported features in the process.
    pub fn init(&self, flags: u32, support_flags: &mut u32) {
        if flags & FUSE_MAX_PAGES != 0 {
            *support_flags |= FUSE_MAX_PAGES;
        }
        if flags & FUSE_CAP_ASYNC_READ != 0 {
            *support_flags |= FUSE_ASYNC_READ;
        }
        if flags & FUSE_CAP_PARALLEL_DIROPS != 0 {
            *support_flags |= FUSE_PARALLEL_DIROPS;
        }
        if flags & FUSE_CAP_POSIX_LOCKS != 0 {
            *support_flags |= FUSE_POSIX_LOCKS;
        }
        if flags & FUSE_CAP_ATOMIC_O_TRUNC != 0 {
            *support_flags |= FUSE_ATOMIC_O_TRUNC;
        }
        if flags & FUSE_CAP_EXPORT_SUPPORT != 0 {
            *support_flags |= FUSE_EXPORT_SUPPORT;
        }
        if flags & FUSE_CAP_DONT_MASK != 0 {
            *support_flags |= FUSE_DONT_MASK;
        }
        if flags & FUSE_CAP_FLOCK_LOCKS != 0 {
            *support_flags |= FUSE_FLOCK_LOCKS;
        }
        if flags & FUSE_CAP_AUTO_INVAL_DATA != 0 {
            *support_flags |= FUSE_AUTO_INVAL_DATA;
        }
        if flags & FUSE_CAP_READDIRPLUS != 0 {
            *support_flags |= FUSE_DO_READDIRPLUS;
        }
        if flags & FUSE_CAP_READDIRPLUS_AUTO != 0 {
            *support_flags |= FUSE_READDIRPLUS_AUTO;
        }
        if flags & FUSE_CAP_ASYNC_DIO != 0 {
            *support_flags |= FUSE_ASYNC_DIO;
        }

        if flags & FUSE_CAP_POSIX_ACL != 0 {
            *support_flags |= FUSE_POSIX_ACL;
        }

        umask(0o000);
    }

    /// Open a directory with the node id in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to look up the inode in the management of filesystem.
    /// * `dir_fh` - The directory handler is returned in the management of filesystem.
    pub fn opendir(&mut self, node_id: usize, dir_fh: &mut u64) -> i32 {
        let inode = match self.find_inode(node_id) {
            Some(i) => i.clone(),
            None => {
                return libc::EBADF as i32;
            }
        };

        let (file_opt, ret) = open_at(&inode.file, CString::new(".").unwrap(), libc::O_RDONLY, 0);
        if ret != FUSE_OK {
            return ret;
        }

        if let Some(file) = file_opt {
            *dir_fh = self.file_map.get_map(file) as u64;
            return FUSE_OK;
        }

        libc::EBADF as i32
    }

    /// read a directory stream with the directory handler in the host filesystem.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to look up the inode in the management of filesystem.
    /// * `dh` - The directory handler in the management of filesystem.
    /// * `size` - The size of the buffer.
    /// * `offset` - The offset indicates it opens a directory stream with the offset.
    /// * `plus` - The plus indicates it uses FuseDirentplus struct to the buffer.
    /// * `buff` - The buffer of all FuseDirent structs or FuseDirentplus structs.
    pub fn readdir(
        &mut self,
        node_id: usize,
        dh: usize,
        size: u32,
        offset: u64,
        plus: bool,
        buff: &mut Vec<u8>,
    ) -> i32 {
        let dir_inode = match self.find_inode(node_id) {
            Some(i) => i.clone(),
            None => {
                return libc::EBADF as i32;
            }
        };

        let mut dirp = match self.file_map.get_value_mut(dh) {
            Some(file) => {
                let (dirp_opt, ret) = fdopen_dir(file.as_raw_fd());
                if ret != FUSE_OK {
                    return libc::EBADF as i32;
                }
                dirp_opt.unwrap()
            }
            _ => {
                return libc::EBADF as i32;
            }
        };

        seek_dir(&mut dirp, offset);

        let mut remain = size;
        let mut son_nodeid = 0_u64;
        loop {
            let (dirent_opt, ret) = read_dir(&mut dirp);
            if ret != FUSE_OK {
                return ret;
            }
            let direntp = dirent_opt.unwrap();
            if direntp.is_null() {
                break;
            }

            // The above code has checked the validity of direntp, so it is safe for *direntp.
            let dirent = unsafe { *direntp };

            let (name_len, son_name) = match array_to_cstring(&dirent.d_name[..]) {
                Ok(v) => v,
                Err(_) => {
                    continue;
                }
            };

            let only_entry_size = if plus {
                mem::size_of::<FuseDirentplus>()
            } else {
                mem::size_of::<FuseDirent>()
            };

            let (entry_size, gap) = match round_up((only_entry_size + name_len) as u64, 8) {
                Some(v) => (v as u32, v as usize - (only_entry_size + name_len)),
                _ => {
                    return libc::EINVAL as i32;
                }
            };
            if entry_size > remain {
                if son_nodeid != 0 {
                    self.forget(son_nodeid as usize, 1);
                }
                break;
            }

            let mut fuse_dirent = FuseDirent {
                ino: dirent.d_ino,
                off: dirent.d_off as u64,
                namelen: name_len as u32,
                type_: dirent.d_type as u32 & (libc::S_IFMT >> 12),
                name: [0u8; 0],
            };

            if dir_inode.node_id == self.root_inode.node_id && path_is_dotdot(&son_name) {
                fuse_dirent.ino = self.root_inode.key.ino;
                fuse_dirent.type_ = libc::DT_DIR as u32 & (libc::S_IFMT >> 12);
            }

            if plus {
                son_nodeid = 0;
                let mut son_attr = FuseAttr::default();
                if !path_is_dot(&son_name) && !path_is_dotdot(&son_name) {
                    let ret = self.internal_lookup(
                        &dir_inode,
                        son_name.clone(),
                        &mut son_nodeid,
                        &mut son_attr,
                    );
                    if ret != FUSE_OK {
                        return ret;
                    }
                }

                buff.extend_from_slice(
                    FuseDirentplus {
                        entry_out: FuseEntryOut {
                            nodeid: son_nodeid as u64,
                            generation: 0,
                            entry_valid: 0,
                            entry_valid_nsec: 0,
                            attr_valid: 0,
                            attr_valid_nsec: 0,
                            attr: son_attr,
                        },
                        dirent: fuse_dirent,
                    }
                    .as_bytes(),
                );
            } else {
                buff.extend_from_slice(fuse_dirent.as_bytes());
            };

            buff.extend_from_slice(son_name.as_bytes());
            if gap > 0 {
                buff.append(&mut vec![0u8; gap]);
            }

            remain -= entry_size;
        }

        FUSE_OK
    }

    /// Release a directory in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `dir_fh` - The directory handler in the management of filesystem.
    pub fn releasedir(&mut self, dir_fh: usize) -> i32 {
        self.file_map.put_map(dir_fh);

        FUSE_OK
    }

    /// Transfer the directory data to the storage device with directory handler in
    /// the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `dir_fh` - The directory handler in the management of filesystem.
    /// * `datasync` - The datasync indicates whether to use the fdatasync
    /// or fsync interface.
    pub fn fsyncdir(&self, dir_fh: usize, datasync: bool) -> i32 {
        if let Some(file) = self.file_map.get_value(dir_fh) {
            let ret = fsync(file, datasync);
            if ret != FUSE_OK {
                return ret;
            }
        } else {
            return libc::EBADF as i32;
        }

        FUSE_OK
    }

    /// Create the POSIX file lock with the node id in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to look up the inode in the management of filesystem.
    /// * `owner` - The unique index for file lock in the inode.
    /// * `file_lock_in` - The information of file lock will be set.
    /// * `file_lock_out` - The information of file lock will be returned.
    pub fn getlk(
        &mut self,
        node_id: usize,
        owner: u64,
        file_lock_in: &FuseFileLock,
        file_lock_out: &mut FuseFileLock,
    ) -> i32 {
        let (file_opt, ret) = self.create_file_lock(node_id, owner);
        if ret != FUSE_OK {
            return ret;
        }

        let ret = fcntl_flock(
            &file_opt.unwrap(),
            libc::F_GETLK,
            file_lock_in,
            file_lock_out,
        );
        if ret != FUSE_OK {
            return ret;
        }

        FUSE_OK
    }

    /// Lock the file or unlock the file by POSIX lock with the node id in
    /// the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `node_id` - The node id used to look up the inode in the management of filesystem.
    /// * `owner` - The unique index for file lock in the inode.
    /// * `is_blocking` - The is_blocking indicates whether to use a blocking lock.
    /// * `file_lock_in` - The information of file lock will be set.
    pub fn setlk(
        &mut self,
        node_id: usize,
        owner: u64,
        is_blocking: bool,
        file_lock_in: &FuseFileLock,
    ) -> i32 {
        if is_blocking {
            return libc::EOPNOTSUPP as i32;
        }

        let (file_opt, ret) = self.create_file_lock(node_id, owner);
        if ret != FUSE_OK {
            return ret;
        }

        let mut file_lock_out = FuseFileLock::default();
        let ret = fcntl_flock(
            &file_opt.unwrap(),
            libc::F_SETLK,
            file_lock_in,
            &mut file_lock_out,
        );
        if ret != FUSE_OK {
            return ret;
        }

        FUSE_OK
    }

    /// Lock the file or unlock the file by BSD lock with file handler in
    /// the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `fh` - The file handler in the management of filesystem.
    /// * `lock_type` - The lock type contains the type of read lock, write lock and unlocking.
    /// * `is_blocking` - The is_blocking indicates whether to use a blocking lock.
    pub fn flock(&self, fh: usize, lock_type: u32, is_blocking: bool) -> i32 {
        let mut operation: i32 = 0;

        if lock_type == F_RDLCK {
            operation = libc::LOCK_SH;
        } else if lock_type == F_WDLCK {
            operation = libc::LOCK_EX;
        } else if lock_type == F_UNLCK {
            operation = libc::LOCK_UN;
        }

        if !is_blocking {
            operation |= libc::LOCK_NB;
        }

        if let Some(file) = self.file_map.get_value(fh) {
            let ret = flock(file, operation);
            if ret != FUSE_OK {
                return ret;
            }
        } else {
            return libc::EBADF as i32;
        }

        FUSE_OK
    }

    /// Create a file with name in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `in_header` - The in_header of fuse message used to get uid and gid.
    /// * `create_in` - The information of creating a file contains the flags, mode and umask.
    /// * `name` - The string of name used to create a file.
    /// * `fh` - The file handler is returned in the management of filesystem.
    /// * `node_id` - The node id that is found by the name in the management of filesystem.
    /// * `fuse_attr` - The attributes will be returned by the found inode.
    pub fn create(
        &mut self,
        in_header: &FuseInHeader,
        create_in: &FuseCreateIn,
        name: CString,
        fh: &mut u64,
        node_id: &mut u64,
        fuse_attr: &mut FuseAttr,
    ) -> i32 {
        let parent_dir = match self.find_inode(in_header.nodeid as usize) {
            Some(i) => i.clone(),
            _ => {
                return libc::EBADF as i32;
            }
        };

        let mut old_uid = 0_u32;
        let mut old_gid = 0_u32;
        let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid);
        if ret != FUSE_OK {
            return ret;
        }

        let (file_opt, ret) = open_at(
            &parent_dir.file,
            name.clone(),
            (create_in.flags as i32 | libc::O_CREAT) & !libc::O_NOFOLLOW,
            create_in.mode & !(create_in.umask & 0o777),
        );

        recover_uid_gid(old_uid, old_gid);

        if ret != FUSE_OK {
            return ret;
        }

        if let Some(file) = file_opt {
            *fh = self.file_map.get_map(file.try_clone().unwrap()) as u64;
        }

        self.internal_lookup(&parent_dir, name, node_id, fuse_attr)
    }

    /// Destroy the management of filesystem, except for the root inode.
    pub fn destroy(&mut self) -> i32 {
        let root_key = self.root_inode.key;

        self.inode_key_map.destroy_map();
        self.file_map.destroy_map();
        self.inodes = BTreeMap::new();

        // Need to add root_inode back to the inode table and inode_key_map table for
        // the filesystem function
        let root_id = self.inode_key_map.get_map(root_key);
        self.root_inode.node_id = root_id;
        self.inodes.insert(root_key, self.root_inode.clone());

        FUSE_OK
    }

    /// Allocate the disk space with file handler in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `fh` - The file handler in the management of filesystem.
    /// * `mode` - The mode determines the operation to be performed on the given range.
    /// * `offset` - The offset in the file.
    /// * `length` - The length that needs to be allocated.
    pub fn fallocate(&self, fh: usize, mode: u32, offset: u64, length: u64) -> i32 {
        if let Some(file) = self.file_map.get_value(fh) {
            let ret = fallocate(file, mode, offset, length);
            if ret != FUSE_OK {
                return ret;
            }
        } else {
            return libc::EBADF as i32;
        }

        FUSE_OK
    }

    /// Reposition the file offset of the open file with file handler
    /// in the management of filesystem.
    ///
    /// # Arguments
    ///
    /// * `fh` - The file handler in the management of filesystem.
    /// * `offset` - The offset in the file used together with the whence.
    /// * `whence` - The whence determines the operation to be performed in the file.
    /// * `outoffset` - The offset from the beginning of the file is returned.
    pub fn lseek(&self, fh: usize, offset: u64, whence: u32, outoffset: &mut u64) -> i32 {
        if let Some(file) = self.file_map.get_value(fh) {
            let (offset_tmp, ret) = lseek(file, offset, whence);
            if ret != FUSE_OK {
                return ret;
            }
            *outoffset = offset_tmp;
        } else {
            return libc::EBADF as i32;
        }

        FUSE_OK
    }
}