From b304bc013ef8022131ef0a146f20c6bad7ac45d5 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Wed, 16 Jun 2021 10:34:07 +0100 Subject: libcamera/base: Move File to base library The File abstraction is a base helper and not part of the libcamera API. Move it to to allow usage by users of the base library. Reviewed-by: Hirokazu Honda Reviewed-by: Paul Elder Reviewed-by: Laurent Pinchart Signed-off-by: Kieran Bingham --- src/libcamera/base/file.cpp | 465 +++++++++++++++++++++++++++++++++++++++++ src/libcamera/base/meson.build | 1 + src/libcamera/file.cpp | 465 ----------------------------------------- src/libcamera/ipa_manager.cpp | 2 +- src/libcamera/ipa_module.cpp | 2 +- src/libcamera/meson.build | 1 - src/libcamera/sysfs.cpp | 3 +- 7 files changed, 469 insertions(+), 470 deletions(-) create mode 100644 src/libcamera/base/file.cpp delete mode 100644 src/libcamera/file.cpp (limited to 'src/libcamera') diff --git a/src/libcamera/base/file.cpp b/src/libcamera/base/file.cpp new file mode 100644 index 00000000..073666fa --- /dev/null +++ b/src/libcamera/base/file.cpp @@ -0,0 +1,465 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * file.cpp - File I/O operations + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +/** + * \file base/file.h + * \brief File I/O operations + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(File) + +/** + * \class File + * \brief Interface for I/O operations on files + * + * The File class provides an interface to perform I/O operations on files. It + * wraps opening, closing and mapping files in memory, and handles the cleaning + * of allocated resources. + * + * File instances are usually constructed with a file name, but the name can be + * set later through the setFileName() function. Instances are not automatically + * opened when constructed, and shall be opened explictly with open(). + * + * Files can be mapped to the process memory with map(). Mapped regions can be + * unmapped manually with munmap(), and are automatically unmapped when the File + * is destroyed or when it is used to reference another file with setFileName(). + */ + +/** + * \enum File::MapFlag + * \brief Flags for the File::map() function + * \var File::MapNoOption + * \brief No option (used as default value) + * \var File::MapPrivate + * \brief The memory region is mapped as private, changes are not reflected in + * the file constents + */ + +/** + * \enum File::OpenMode + * \brief Mode in which a file is opened + * \var File::NotOpen + * \brief The file is not open + * \var File::ReadOnly + * \brief The file is open for reading + * \var File::WriteOnly + * \brief The file is open for writing + * \var File::ReadWrite + * \brief The file is open for reading and writing + */ + +/** + * \brief Construct a File to represent the file \a name + * \param[in] name The file name + * + * Upon construction the File object is closed and shall be opened with open() + * before performing I/O operations. + */ +File::File(const std::string &name) + : name_(name), fd_(-1), mode_(NotOpen), error_(0) +{ +} + +/** + * \brief Construct a File without an associated name + * + * Before being used for any purpose, the file name shall be set with + * setFileName(). + */ +File::File() + : fd_(-1), mode_(NotOpen), error_(0) +{ +} + +/** + * \brief Destroy a File instance + * + * Any memory mapping associated with the File is unmapped, and the File is + * closed if it is open. + */ +File::~File() +{ + unmapAll(); + close(); +} + +/** + * \fn const std::string &File::fileName() const + * \brief Retrieve the file name + * \return The file name + */ + +/** + * \brief Set the name of the file + * \param[in] name The name of the file + * + * The \a name can contain an absolute path, a relative path or no path at all. + * Calling this function on an open file results in undefined behaviour. + * + * Any memory mapping associated with the File is unmapped. + */ +void File::setFileName(const std::string &name) +{ + if (isOpen()) { + LOG(File, Error) + << "Can't set file name on already open file " << name_; + return; + } + + unmapAll(); + + name_ = name; +} + +/** + * \brief Check if the file specified by fileName() exists + * + * This function checks if the file specified by fileName() exists. The File + * instance doesn't need to be open to check for file existence, and this + * function may return false even if the file is open, if it was deleted from + * the file system. + * + * \return True if the the file exists, false otherwise + */ +bool File::exists() const +{ + return exists(name_); +} + +/** + * \brief Open the file in the given mode + * \param[in] mode The open mode + * + * This function opens the file specified by fileName() in \a mode. If the file + * doesn't exist and the mode is WriteOnly or ReadWrite, this function will + * attempt to create the file with initial permissions set to 0666 (modified by + * the process' umask). + * + * The error() status is updated. + * + * \return True on success, false otherwise + */ +bool File::open(File::OpenMode mode) +{ + if (isOpen()) { + LOG(File, Error) << "File " << name_ << " is already open"; + return false; + } + + int flags = (mode & ReadWrite) - 1; + if (mode & WriteOnly) + flags |= O_CREAT; + + fd_ = ::open(name_.c_str(), flags, 0666); + if (fd_ < 0) { + error_ = -errno; + return false; + } + + mode_ = mode; + error_ = 0; + return true; +} + +/** + * \fn bool File::isOpen() const + * \brief Check if the file is open + * \return True if the file is open, false otherwise + */ + +/** + * \fn OpenMode File::openMode() const + * \brief Retrieve the file open mode + * \return The file open mode + */ + +/** + * \brief Close the file + * + * This function closes the File. If the File is not open, it performs no + * operation. Memory mappings created with map() are not destroyed when the + * file is closed. + */ +void File::close() +{ + if (fd_ == -1) + return; + + ::close(fd_); + fd_ = -1; + mode_ = NotOpen; +} + +/** + * \fn int File::error() const + * \brief Retrieve the file error status + * + * This function retrieves the error status from the last file open or I/O + * operation. The error status is a negative number as defined by errno.h. If + * no error occurred, this function returns 0. + * + * \return The file error status + */ + +/** + * \brief Retrieve the file size + * + * This function retrieves the size of the file on the filesystem. The File + * instance shall be open to retrieve its size. The error() status is not + * modified, error codes are returned directly on failure. + * + * \return The file size in bytes on success, or a negative error code otherwise + */ +ssize_t File::size() const +{ + if (!isOpen()) + return -EINVAL; + + struct stat st; + int ret = fstat(fd_, &st); + if (ret < 0) + return -errno; + + return st.st_size; +} + +/** + * \brief Return current read or write position + * + * If the file is closed, this function returns 0. + * + * \return The current read or write position + */ +off_t File::pos() const +{ + if (!isOpen()) + return 0; + + return lseek(fd_, 0, SEEK_CUR); +} + +/** + * \brief Set the read or write position + * \param[in] pos The desired position + * \return The resulting offset from the beginning of the file on success, or a + * negative error code otherwise + */ +off_t File::seek(off_t pos) +{ + if (!isOpen()) + return -EINVAL; + + off_t ret = lseek(fd_, pos, SEEK_SET); + if (ret < 0) + return -errno; + + return ret; +} + +/** + * \brief Read data from the file + * \param[in] data Memory to read data into + * + * Read at most \a data.size() bytes from the file into \a data.data(), and + * return the number of bytes read. If less data than requested is available, + * the returned byte count may be smaller than the requested size. If no more + * data is available, 0 is returned. + * + * The position of the file as returned by pos() is advanced by the number of + * bytes read. If an error occurs, the position isn't modified. + * + * \return The number of bytes read on success, or a negative error code + * otherwise + */ +ssize_t File::read(const Span &data) +{ + if (!isOpen()) + return -EINVAL; + + size_t readBytes = 0; + ssize_t ret = 0; + + /* Retry in case of interrupted system calls. */ + while (readBytes < data.size()) { + ret = ::read(fd_, data.data() + readBytes, + data.size() - readBytes); + if (ret <= 0) + break; + + readBytes += ret; + } + + if (ret < 0 && !readBytes) + return -errno; + + return readBytes; +} + +/** + * \brief Write data to the file + * \param[in] data Memory containing data to be written + * + * Write at most \a data.size() bytes from \a data.data() to the file, and + * return the number of bytes written. If the file system doesn't have enough + * space for the data, the returned byte count may be less than requested. + * + * The position of the file as returned by pos() is advanced by the number of + * bytes written. If an error occurs, the position isn't modified. + * + * \return The number of bytes written on success, or a negative error code + * otherwise + */ +ssize_t File::write(const Span &data) +{ + if (!isOpen()) + return -EINVAL; + + size_t writtenBytes = 0; + + /* Retry in case of interrupted system calls. */ + while (writtenBytes < data.size()) { + ssize_t ret = ::write(fd_, data.data() + writtenBytes, + data.size() - writtenBytes); + if (ret <= 0) + break; + + writtenBytes += ret; + } + + if (data.size() && !writtenBytes) + return -errno; + + return writtenBytes; +} + +/** + * \brief Map a region of the file in the process memory + * \param[in] offset The region offset within the file + * \param[in] size The region sise + * \param[in] flags The mapping flags + * + * This function maps a region of \a size bytes of the file starting at \a + * offset into the process memory. The File instance shall be open, but may be + * closed after mapping the region. Mappings stay valid when the File is + * closed, and are destroyed automatically when the File is deleted. + * + * If \a size is a negative value, this function maps the region starting at \a + * offset until the end of the file. + * + * The mapping memory protection is controlled by the file open mode, unless \a + * flags contains MapPrivate in which case the region is mapped in read/write + * mode. + * + * The error() status is updated. + * + * \return The mapped memory on success, or an empty span otherwise + */ +Span File::map(off_t offset, ssize_t size, enum File::MapFlag flags) +{ + if (!isOpen()) { + error_ = -EBADF; + return {}; + } + + if (size < 0) { + size = File::size(); + if (size < 0) { + error_ = size; + return {}; + } + + size -= offset; + } + + int mmapFlags = flags & MapPrivate ? MAP_PRIVATE : MAP_SHARED; + + int prot = 0; + if (mode_ & ReadOnly) + prot |= PROT_READ; + if (mode_ & WriteOnly) + prot |= PROT_WRITE; + if (flags & MapPrivate) + prot |= PROT_WRITE; + + void *map = mmap(NULL, size, prot, mmapFlags, fd_, offset); + if (map == MAP_FAILED) { + error_ = -errno; + return {}; + } + + maps_.emplace(map, size); + + error_ = 0; + return { static_cast(map), static_cast(size) }; +} + +/** + * \brief Unmap a region mapped with map() + * \param[in] addr The region address + * + * The error() status is updated. + * + * \return True on success, or false if an error occurs + */ +bool File::unmap(uint8_t *addr) +{ + auto iter = maps_.find(static_cast(addr)); + if (iter == maps_.end()) { + error_ = -ENOENT; + return false; + } + + int ret = munmap(addr, iter->second); + if (ret < 0) { + error_ = -errno; + return false; + } + + maps_.erase(iter); + + error_ = 0; + return true; +} + +void File::unmapAll() +{ + for (const auto &map : maps_) + munmap(map.first, map.second); + + maps_.clear(); +} + +/** + * \brief Check if the file specified by \a name exists + * \param[in] name The file name + * \return True if the file exists, false otherwise + */ +bool File::exists(const std::string &name) +{ + struct stat st; + int ret = stat(name.c_str(), &st); + if (ret < 0) + return false; + + /* Directories can not be handled here, even if they exist. */ + return !S_ISDIR(st.st_mode); +} + +} /* namespace libcamera */ diff --git a/src/libcamera/base/meson.build b/src/libcamera/base/meson.build index 7a19c67c..fb8ed79a 100644 --- a/src/libcamera/base/meson.build +++ b/src/libcamera/base/meson.build @@ -5,6 +5,7 @@ libcamera_base_sources = files([ 'bound_method.cpp', 'event_dispatcher.cpp', 'event_dispatcher_poll.cpp', + 'file.cpp', 'log.cpp', 'message.cpp', 'object.cpp', diff --git a/src/libcamera/file.cpp b/src/libcamera/file.cpp deleted file mode 100644 index def0f60d..00000000 --- a/src/libcamera/file.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2020, Google Inc. - * - * file.cpp - File I/O operations - */ - -#include "libcamera/internal/file.h" - -#include -#include -#include -#include -#include -#include - -#include - -/** - * \file file.h - * \brief File I/O operations - */ - -namespace libcamera { - -LOG_DEFINE_CATEGORY(File) - -/** - * \class File - * \brief Interface for I/O operations on files - * - * The File class provides an interface to perform I/O operations on files. It - * wraps opening, closing and mapping files in memory, and handles the cleaning - * of allocated resources. - * - * File instances are usually constructed with a file name, but the name can be - * set later through the setFileName() function. Instances are not automatically - * opened when constructed, and shall be opened explictly with open(). - * - * Files can be mapped to the process memory with map(). Mapped regions can be - * unmapped manually with munmap(), and are automatically unmapped when the File - * is destroyed or when it is used to reference another file with setFileName(). - */ - -/** - * \enum File::MapFlag - * \brief Flags for the File::map() function - * \var File::MapNoOption - * \brief No option (used as default value) - * \var File::MapPrivate - * \brief The memory region is mapped as private, changes are not reflected in - * the file constents - */ - -/** - * \enum File::OpenMode - * \brief Mode in which a file is opened - * \var File::NotOpen - * \brief The file is not open - * \var File::ReadOnly - * \brief The file is open for reading - * \var File::WriteOnly - * \brief The file is open for writing - * \var File::ReadWrite - * \brief The file is open for reading and writing - */ - -/** - * \brief Construct a File to represent the file \a name - * \param[in] name The file name - * - * Upon construction the File object is closed and shall be opened with open() - * before performing I/O operations. - */ -File::File(const std::string &name) - : name_(name), fd_(-1), mode_(NotOpen), error_(0) -{ -} - -/** - * \brief Construct a File without an associated name - * - * Before being used for any purpose, the file name shall be set with - * setFileName(). - */ -File::File() - : fd_(-1), mode_(NotOpen), error_(0) -{ -} - -/** - * \brief Destroy a File instance - * - * Any memory mapping associated with the File is unmapped, and the File is - * closed if it is open. - */ -File::~File() -{ - unmapAll(); - close(); -} - -/** - * \fn const std::string &File::fileName() const - * \brief Retrieve the file name - * \return The file name - */ - -/** - * \brief Set the name of the file - * \param[in] name The name of the file - * - * The \a name can contain an absolute path, a relative path or no path at all. - * Calling this function on an open file results in undefined behaviour. - * - * Any memory mapping associated with the File is unmapped. - */ -void File::setFileName(const std::string &name) -{ - if (isOpen()) { - LOG(File, Error) - << "Can't set file name on already open file " << name_; - return; - } - - unmapAll(); - - name_ = name; -} - -/** - * \brief Check if the file specified by fileName() exists - * - * This function checks if the file specified by fileName() exists. The File - * instance doesn't need to be open to check for file existence, and this - * function may return false even if the file is open, if it was deleted from - * the file system. - * - * \return True if the the file exists, false otherwise - */ -bool File::exists() const -{ - return exists(name_); -} - -/** - * \brief Open the file in the given mode - * \param[in] mode The open mode - * - * This function opens the file specified by fileName() in \a mode. If the file - * doesn't exist and the mode is WriteOnly or ReadWrite, this function will - * attempt to create the file with initial permissions set to 0666 (modified by - * the process' umask). - * - * The error() status is updated. - * - * \return True on success, false otherwise - */ -bool File::open(File::OpenMode mode) -{ - if (isOpen()) { - LOG(File, Error) << "File " << name_ << " is already open"; - return false; - } - - int flags = (mode & ReadWrite) - 1; - if (mode & WriteOnly) - flags |= O_CREAT; - - fd_ = ::open(name_.c_str(), flags, 0666); - if (fd_ < 0) { - error_ = -errno; - return false; - } - - mode_ = mode; - error_ = 0; - return true; -} - -/** - * \fn bool File::isOpen() const - * \brief Check if the file is open - * \return True if the file is open, false otherwise - */ - -/** - * \fn OpenMode File::openMode() const - * \brief Retrieve the file open mode - * \return The file open mode - */ - -/** - * \brief Close the file - * - * This function closes the File. If the File is not open, it performs no - * operation. Memory mappings created with map() are not destroyed when the - * file is closed. - */ -void File::close() -{ - if (fd_ == -1) - return; - - ::close(fd_); - fd_ = -1; - mode_ = NotOpen; -} - -/** - * \fn int File::error() const - * \brief Retrieve the file error status - * - * This function retrieves the error status from the last file open or I/O - * operation. The error status is a negative number as defined by errno.h. If - * no error occurred, this function returns 0. - * - * \return The file error status - */ - -/** - * \brief Retrieve the file size - * - * This function retrieves the size of the file on the filesystem. The File - * instance shall be open to retrieve its size. The error() status is not - * modified, error codes are returned directly on failure. - * - * \return The file size in bytes on success, or a negative error code otherwise - */ -ssize_t File::size() const -{ - if (!isOpen()) - return -EINVAL; - - struct stat st; - int ret = fstat(fd_, &st); - if (ret < 0) - return -errno; - - return st.st_size; -} - -/** - * \brief Return current read or write position - * - * If the file is closed, this function returns 0. - * - * \return The current read or write position - */ -off_t File::pos() const -{ - if (!isOpen()) - return 0; - - return lseek(fd_, 0, SEEK_CUR); -} - -/** - * \brief Set the read or write position - * \param[in] pos The desired position - * \return The resulting offset from the beginning of the file on success, or a - * negative error code otherwise - */ -off_t File::seek(off_t pos) -{ - if (!isOpen()) - return -EINVAL; - - off_t ret = lseek(fd_, pos, SEEK_SET); - if (ret < 0) - return -errno; - - return ret; -} - -/** - * \brief Read data from the file - * \param[in] data Memory to read data into - * - * Read at most \a data.size() bytes from the file into \a data.data(), and - * return the number of bytes read. If less data than requested is available, - * the returned byte count may be smaller than the requested size. If no more - * data is available, 0 is returned. - * - * The position of the file as returned by pos() is advanced by the number of - * bytes read. If an error occurs, the position isn't modified. - * - * \return The number of bytes read on success, or a negative error code - * otherwise - */ -ssize_t File::read(const Span &data) -{ - if (!isOpen()) - return -EINVAL; - - size_t readBytes = 0; - ssize_t ret = 0; - - /* Retry in case of interrupted system calls. */ - while (readBytes < data.size()) { - ret = ::read(fd_, data.data() + readBytes, - data.size() - readBytes); - if (ret <= 0) - break; - - readBytes += ret; - } - - if (ret < 0 && !readBytes) - return -errno; - - return readBytes; -} - -/** - * \brief Write data to the file - * \param[in] data Memory containing data to be written - * - * Write at most \a data.size() bytes from \a data.data() to the file, and - * return the number of bytes written. If the file system doesn't have enough - * space for the data, the returned byte count may be less than requested. - * - * The position of the file as returned by pos() is advanced by the number of - * bytes written. If an error occurs, the position isn't modified. - * - * \return The number of bytes written on success, or a negative error code - * otherwise - */ -ssize_t File::write(const Span &data) -{ - if (!isOpen()) - return -EINVAL; - - size_t writtenBytes = 0; - - /* Retry in case of interrupted system calls. */ - while (writtenBytes < data.size()) { - ssize_t ret = ::write(fd_, data.data() + writtenBytes, - data.size() - writtenBytes); - if (ret <= 0) - break; - - writtenBytes += ret; - } - - if (data.size() && !writtenBytes) - return -errno; - - return writtenBytes; -} - -/** - * \brief Map a region of the file in the process memory - * \param[in] offset The region offset within the file - * \param[in] size The region sise - * \param[in] flags The mapping flags - * - * This function maps a region of \a size bytes of the file starting at \a - * offset into the process memory. The File instance shall be open, but may be - * closed after mapping the region. Mappings stay valid when the File is - * closed, and are destroyed automatically when the File is deleted. - * - * If \a size is a negative value, this function maps the region starting at \a - * offset until the end of the file. - * - * The mapping memory protection is controlled by the file open mode, unless \a - * flags contains MapPrivate in which case the region is mapped in read/write - * mode. - * - * The error() status is updated. - * - * \return The mapped memory on success, or an empty span otherwise - */ -Span File::map(off_t offset, ssize_t size, enum File::MapFlag flags) -{ - if (!isOpen()) { - error_ = -EBADF; - return {}; - } - - if (size < 0) { - size = File::size(); - if (size < 0) { - error_ = size; - return {}; - } - - size -= offset; - } - - int mmapFlags = flags & MapPrivate ? MAP_PRIVATE : MAP_SHARED; - - int prot = 0; - if (mode_ & ReadOnly) - prot |= PROT_READ; - if (mode_ & WriteOnly) - prot |= PROT_WRITE; - if (flags & MapPrivate) - prot |= PROT_WRITE; - - void *map = mmap(NULL, size, prot, mmapFlags, fd_, offset); - if (map == MAP_FAILED) { - error_ = -errno; - return {}; - } - - maps_.emplace(map, size); - - error_ = 0; - return { static_cast(map), static_cast(size) }; -} - -/** - * \brief Unmap a region mapped with map() - * \param[in] addr The region address - * - * The error() status is updated. - * - * \return True on success, or false if an error occurs - */ -bool File::unmap(uint8_t *addr) -{ - auto iter = maps_.find(static_cast(addr)); - if (iter == maps_.end()) { - error_ = -ENOENT; - return false; - } - - int ret = munmap(addr, iter->second); - if (ret < 0) { - error_ = -errno; - return false; - } - - maps_.erase(iter); - - error_ = 0; - return true; -} - -void File::unmapAll() -{ - for (const auto &map : maps_) - munmap(map.first, map.second); - - maps_.clear(); -} - -/** - * \brief Check if the file specified by \a name exists - * \param[in] name The file name - * \return True if the file exists, false otherwise - */ -bool File::exists(const std::string &name) -{ - struct stat st; - int ret = stat(name.c_str(), &st); - if (ret < 0) - return false; - - /* Directories can not be handled here, even if they exist. */ - return !S_ISDIR(st.st_mode); -} - -} /* namespace libcamera */ diff --git a/src/libcamera/ipa_manager.cpp b/src/libcamera/ipa_manager.cpp index 35c72598..b4606c61 100644 --- a/src/libcamera/ipa_manager.cpp +++ b/src/libcamera/ipa_manager.cpp @@ -12,10 +12,10 @@ #include #include +#include #include #include -#include "libcamera/internal/file.h" #include "libcamera/internal/ipa_module.h" #include "libcamera/internal/ipa_proxy.h" #include "libcamera/internal/pipeline_handler.h" diff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp index 7ab55579..984c1fed 100644 --- a/src/libcamera/ipa_module.cpp +++ b/src/libcamera/ipa_module.cpp @@ -23,10 +23,10 @@ #include +#include #include #include -#include "libcamera/internal/file.h" #include "libcamera/internal/pipeline_handler.h" /** diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build index e915266a..36f6ddd7 100644 --- a/src/libcamera/meson.build +++ b/src/libcamera/meson.build @@ -16,7 +16,6 @@ libcamera_sources = files([ 'device_enumerator.cpp', 'device_enumerator_sysfs.cpp', 'event_notifier.cpp', - 'file.cpp', 'file_descriptor.cpp', 'formats.cpp', 'framebuffer_allocator.cpp', diff --git a/src/libcamera/sysfs.cpp b/src/libcamera/sysfs.cpp index 4372b134..44c3331b 100644 --- a/src/libcamera/sysfs.cpp +++ b/src/libcamera/sysfs.cpp @@ -12,10 +12,9 @@ #include #include +#include #include -#include "libcamera/internal/file.h" - /** * \file sysfs.h * \brief Miscellaneous utility functions to access sysfs -- cgit v1.2.1