// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include #include #include #include #include #include #include #include #include #include #include #include namespace purefs::fs { namespace { constexpr std::pair part_types_to_vfs[] = { {0x0b, "vfat"}, {0x9e, "littlefs"}, {0x83, "ext4"}}; auto compare_mount_points(std::string_view path1, std::string_view path2) { std::string spath1{path1}; std::string spath2{path2}; if (spath1.back() == '/') { spath1.pop_back(); } if (spath2.back() == '/') { spath2.pop_back(); } return spath1 == spath2; } } filesystem::filesystem(std::shared_ptr diskmm) : m_diskmm(diskmm), m_lock(std::make_unique()), m_notifier(std::make_unique()) {} filesystem::~filesystem() {} auto filesystem::register_filesystem(std::string_view fsname, std::shared_ptr fops) -> int { if (fops == nullptr) { LOG_ERROR("Filesystem operations doesn't exists"); return -EINVAL; } cpp_freertos::LockGuard _lck(*m_lock); const auto it = m_fstypes.find(std::string(fsname)); if (it != std::end(m_fstypes)) { LOG_ERROR("Filesystem %s already registered", std::string(fsname).c_str()); return -EEXIST; } else { const auto ret = fops->finalize_registration(m_diskmm); if (ret) { LOG_ERROR("Disc: Unable to register filesystem finalize error %i", ret); return ret; } m_fstypes.emplace(std::make_pair(fsname, fops)); return {}; } } auto filesystem::unregister_filesystem(std::string_view fsname) -> int { cpp_freertos::LockGuard _lck(*m_lock); const auto it = m_fstypes.find(std::string(fsname)); if (it == std::end(m_fstypes)) { LOG_ERROR("VFS: filesystem %s doesn't exists in manager.", std::string(fsname).c_str()); return -ENOENT; } if (it->second->mount_count() > 0) { LOG_ERROR("VFS: fileystem %s is already used", std::string(fsname).c_str()); return -EBUSY; } m_fstypes.erase(it); return {}; } auto filesystem::mount(std::string_view dev_or_part, std::string_view target, std::string_view fs_type, unsigned flags, const void *data) -> int { // Sanity check input data if (target.size() <= 1 || target[0] != '/') { LOG_ERROR("VFS: Invalid target mountpoint path"); return -EINVAL; } if (flags & ~(mount_flags::remount | mount_flags::read_only)) { LOG_ERROR("VFS: passed mount flags is not supported"); return -ENOTSUP; } { cpp_freertos::LockGuard _lock(*m_lock); const auto mpi = m_mounts.find(std::string(target)); if (mpi != std::end(m_mounts)) { if (flags & mount_flags::remount) { mpi->second->modify_flags(flags & ~mount_flags::remount); return {}; } else { LOG_ERROR("VFS: given mount point already exists"); return -EBUSY; } } const auto mpp = m_partitions.find(std::string(dev_or_part)); if (mpp != std::end(m_partitions)) { LOG_ERROR("VFS: given partition already used"); return -EBUSY; } std::string filesystem_type; if (fs_type.compare("auto") == 0) { filesystem_type = autodetect_filesystem_type(std::string(dev_or_part)); if (filesystem_type.empty()) { LOG_ERROR("Unable to auto detect filesystem"); return -ENODEV; } } else { filesystem_type = fs_type; } const auto vsi = m_fstypes.find(filesystem_type); if (vsi == std::end(m_fstypes)) { LOG_ERROR("VFS: requested filesystem %s not registered", std::string(fs_type).c_str()); return -ENODEV; } // Trying to open disk or part by manager blkdev::disk_fd diskh; { auto disk_mgr = m_diskmm.lock(); if (disk_mgr) { diskh = disk_mgr->device_handle(dev_or_part); } else { LOG_ERROR("VFS: Unable to lock device manager"); return -EIO; } } if (diskh) { const auto mnt_point = vsi->second->mount_prealloc(diskh, target, flags); const auto ret_mnt = vsi->second->mount(mnt_point, data); if (!ret_mnt) { m_mounts.emplace(std::make_pair(target, mnt_point)); m_partitions.emplace(dev_or_part); } else { return ret_mnt; } } else { LOG_ERROR("Device or partition with given name doesn't exists"); return -ENXIO; } } return {}; } auto filesystem::umount(std::string_view mount_point) -> int { cpp_freertos::LockGuard _lck(*m_lock); auto mnti = m_mounts.find(std::string(mount_point)); if (mnti == std::end(m_mounts)) { return -ENOENT; } auto fsops = mnti->second->fs_ops(); if (!fsops) { LOG_ERROR("Unable to lock filesystem operation"); return -EIO; } cleanup_opened_files(mount_point); const auto diskh = mnti->second->disk(); const auto umnt_ret = fsops->umount(mnti->second); if (umnt_ret) { return umnt_ret; } if (diskh) { m_partitions.erase(std::string(diskh->name())); } m_mounts.erase(mnti); return {}; } auto filesystem::read_mountpoints(std::list &mountpoints) const -> int { cpp_freertos::LockGuard _lck(*m_lock); for (const auto &mntp : m_mounts) { mountpoints.push_back(mntp.first); } return {}; } auto filesystem::find_mount_point(std::string_view path) const noexcept -> std::tuple, size_t> { size_t longest_match{}; std::shared_ptr mount_pnt; cpp_freertos::LockGuard _lck(*m_lock); for (const auto &mntp : m_mounts) { const auto slen = mntp.first.size(); if ((slen < longest_match) || (slen > path.size())) { continue; } if ((slen > 1) && (path[slen] != '/') && (path[slen] != '\0')) { continue; } if (path.compare(0, slen, mntp.first) == 0) { mount_pnt = mntp.second; longest_match = slen; } } return std::make_tuple(mount_pnt, longest_match); } auto filesystem::absolute_path(std::string_view path) noexcept -> std::string { std::string ret{}; if (!path.empty() && path[0] == path_separator) { ret.append(path); } else { ret.append(internal::get_thread_local_cwd_path()).append("/").append(path); } return normalize_path(ret); } auto filesystem::normalize_path(std::string_view path) noexcept -> std::string { std::string ret; auto paths = utils::split(path, "/"); for (auto it = std::begin(paths); it != std::end(paths);) { if (!it->compare(".")) { it = paths.erase(it); } else if (!it->compare("..")) { auto pit = it; --pit; if (pit != std::end(paths)) { it = paths.erase(pit); if (it != std::end(paths)) it = paths.erase(it); } else { it = paths.erase(it); } } else { ++it; } } for (auto ep : paths) { if (!ep.empty()) { ret.append("/").append(ep); } } if (ret.empty()) ret.append("/"); return ret; } auto filesystem::add_filehandle(fsfile file) noexcept -> int { cpp_freertos::LockGuard _lck(*m_lock); return m_fds.insert(file) + first_file_descriptor; } auto filesystem::remove_filehandle(int fds) noexcept -> fsfile { if (fds < first_file_descriptor) { return nullptr; } fds -= first_file_descriptor; cpp_freertos::LockGuard _lck(*m_lock); fsfile ret{}; if (m_fds.exists(fds)) { ret = m_fds[fds]; m_fds.remove(fds); } return ret; } auto filesystem::find_filehandle(int fds) const noexcept -> fsfile { fsfile ret{}; if (fds < first_file_descriptor) { return ret; } fds -= first_file_descriptor; cpp_freertos::LockGuard _lck(*m_lock); if (m_fds.exists(fds)) { ret = m_fds[fds]; } return ret; } auto filesystem::autodetect_filesystem_type(std::string_view dev_or_part) const noexcept -> std::string { auto disk_mgr = m_diskmm.lock(); if (disk_mgr) { auto part = disk_mgr->partition_info(dev_or_part); if (part) { for (auto &ptype : part_types_to_vfs) { if (ptype.first == part.value().type) { const auto ret = std::string(ptype.second); LOG_INFO("Autodetected filesystem type %s", ret.c_str()); return ret; } } LOG_ERROR("Unable to detect filesystem type"); return {}; } else { LOG_ERROR("No partition on device"); return {}; } } else { LOG_ERROR("VFS: Unable to lock device manager"); return {}; } } auto filesystem::inotify_create(std::shared_ptr svc) -> std::shared_ptr { return std::make_shared(svc, m_notifier); } auto filesystem::cleanup_opened_files(std::string_view mount_point) -> void { LOG_INFO("Closing opened files on mntpoint: %s before umount.", std::string(mount_point).c_str()); for (auto fd = 0U; fd < m_fds.size(); ++fd) { const auto filp = m_fds[fd]; if (!filp) continue; const auto mp = filp->mntpoint(); if (!mp) continue; if (compare_mount_points(mp->mount_path(), mount_point)) { const auto err = close(fd + first_file_descriptor); LOG_WARN("Emergency closing: %s result: %i.", filp->open_path().c_str(), err); } } } } // namespace purefs::fs