[libcamera-devel,1/4] libcamera: file: Add read/write support

Message ID 20200712144419.21457-1-laurent.pinchart@ideasonboard.com
State Superseded
Headers show
Series
  • [libcamera-devel,1/4] libcamera: file: Add read/write support
Related show

Commit Message

Laurent Pinchart July 12, 2020, 2:44 p.m. UTC
Add basic support to read and write data from/to a file, along with
retrieving and setting the current read/write position.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 include/libcamera/internal/file.h |   7 ++
 src/libcamera/file.cpp            | 111 +++++++++++++++++++++++++++++-
 2 files changed, 116 insertions(+), 2 deletions(-)

Comments

Kieran Bingham July 12, 2020, 7:32 p.m. UTC | #1
Hi Laurent,

On 12/07/2020 15:44, Laurent Pinchart wrote:
> Add basic support to read and write data from/to a file, along with
> retrieving and setting the current read/write position.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  include/libcamera/internal/file.h |   7 ++
>  src/libcamera/file.cpp            | 111 +++++++++++++++++++++++++++++-
>  2 files changed, 116 insertions(+), 2 deletions(-)
> 
> diff --git a/include/libcamera/internal/file.h b/include/libcamera/internal/file.h
> index e3e72132e336..ead94cdf29c9 100644
> --- a/include/libcamera/internal/file.h
> +++ b/include/libcamera/internal/file.h
> @@ -49,6 +49,12 @@ public:
>  	int error() const { return error_; }
>  	ssize_t size() const;
>  
> +	off_t pos() const { return pos_; }
> +	off_t seek(off_t pos);
> +
> +	ssize_t read(const Span<uint8_t> &data);
> +	ssize_t write(const Span<const uint8_t> &data);
> +
>  	Span<uint8_t> map(off_t offset = 0, ssize_t size = -1,
>  			  MapFlag flags = MapNoOption);
>  	bool unmap(uint8_t *addr);
> @@ -61,6 +67,7 @@ private:
>  	std::string name_;
>  	int fd_;
>  	OpenMode mode_;
> +	off_t pos_;
>  
>  	int error_;
>  	std::map<void *, size_t> maps_;
> diff --git a/src/libcamera/file.cpp b/src/libcamera/file.cpp
> index c471bde3fc68..8bd109090919 100644
> --- a/src/libcamera/file.cpp
> +++ b/src/libcamera/file.cpp
> @@ -73,7 +73,7 @@ LOG_DEFINE_CATEGORY(File);
>   * before performing I/O operations.
>   */
>  File::File(const std::string &name)
> -	: name_(name), fd_(-1), mode_(NotOpen), error_(0)
> +	: name_(name), fd_(-1), mode_(NotOpen), pos_(0), error_(0)
>  {
>  }
>  
> @@ -84,7 +84,7 @@ File::File(const std::string &name)
>   * setFileName().
>   */
>  File::File()
> -	: fd_(-1), mode_(NotOpen), error_(0)
> +	: fd_(-1), mode_(NotOpen), pos_(0), error_(0)
>  {
>  }
>  
> @@ -202,6 +202,7 @@ void File::close()
>  	::close(fd_);
>  	fd_ = -1;
>  	mode_ = NotOpen;
> +	pos_ = 0;
>  }
>  
>  /**
> @@ -237,6 +238,112 @@ ssize_t File::size() const
>  	return st.st_size;
>  }
>  
> +/**
> + * \fn off_t File::pos() const
> + * \brief Return current read or write position
> + *
> + * If the file is closed, this function returns 0.
> + *
> + * \return The current read or write position
> + */
> +
> +/**
> + * \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;
> +
> +	pos_ = ret;
> +	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<uint8_t> &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)

the man page for read(2) states:


# On  error,  -1  is returned, and errno is set appropriately.  In this
# case, it is left unspecified whether the file position (if any)
# changes.


In the event of an error, that could thus leave pos_ invalid, but I
don't think we care too much about that for an internal helper which
probably isn't going to use pos much anyway.

We could:

A) call lseek to explicitly reset pos
B) Continue to do nothing.

I think we'll choose option B for now, especially as the function
documentation for both explicitly states that the pos will not be moved
on error. ;-)


So ...

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>


> +		return -errno;
> +
> +	pos_ += readBytes;
> +	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<const uint8_t> &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;
> +
> +	pos_ += writtenBytes;> +	return writtenBytes;
> +}
> +
>  /**
>   * \brief Map a region of the file in the process memory
>   * \param[in] offset The region offset within the file
>
Niklas Söderlund July 13, 2020, 6:53 a.m. UTC | #2
Hi Laurent,

Thanks for your work.

On 2020-07-12 17:44:16 +0300, Laurent Pinchart wrote:
> Add basic support to read and write data from/to a file, along with
> retrieving and setting the current read/write position.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  include/libcamera/internal/file.h |   7 ++
>  src/libcamera/file.cpp            | 111 +++++++++++++++++++++++++++++-
>  2 files changed, 116 insertions(+), 2 deletions(-)
> 
> diff --git a/include/libcamera/internal/file.h b/include/libcamera/internal/file.h
> index e3e72132e336..ead94cdf29c9 100644
> --- a/include/libcamera/internal/file.h
> +++ b/include/libcamera/internal/file.h
> @@ -49,6 +49,12 @@ public:
>  	int error() const { return error_; }
>  	ssize_t size() const;
>  
> +	off_t pos() const { return pos_; }

Would it not be nicer to return lseek(fd_, 0, SEEK_CUR) here? That way 
we could avoid keeping pos_ in sync in potential corner cases.

> +	off_t seek(off_t pos);
> +
> +	ssize_t read(const Span<uint8_t> &data);
> +	ssize_t write(const Span<const uint8_t> &data);
> +
>  	Span<uint8_t> map(off_t offset = 0, ssize_t size = -1,
>  			  MapFlag flags = MapNoOption);
>  	bool unmap(uint8_t *addr);
> @@ -61,6 +67,7 @@ private:
>  	std::string name_;
>  	int fd_;
>  	OpenMode mode_;
> +	off_t pos_;
>  
>  	int error_;
>  	std::map<void *, size_t> maps_;
> diff --git a/src/libcamera/file.cpp b/src/libcamera/file.cpp
> index c471bde3fc68..8bd109090919 100644
> --- a/src/libcamera/file.cpp
> +++ b/src/libcamera/file.cpp
> @@ -73,7 +73,7 @@ LOG_DEFINE_CATEGORY(File);
>   * before performing I/O operations.
>   */
>  File::File(const std::string &name)
> -	: name_(name), fd_(-1), mode_(NotOpen), error_(0)
> +	: name_(name), fd_(-1), mode_(NotOpen), pos_(0), error_(0)
>  {
>  }
>  
> @@ -84,7 +84,7 @@ File::File(const std::string &name)
>   * setFileName().
>   */
>  File::File()
> -	: fd_(-1), mode_(NotOpen), error_(0)
> +	: fd_(-1), mode_(NotOpen), pos_(0), error_(0)
>  {
>  }
>  
> @@ -202,6 +202,7 @@ void File::close()
>  	::close(fd_);
>  	fd_ = -1;
>  	mode_ = NotOpen;
> +	pos_ = 0;
>  }
>  
>  /**
> @@ -237,6 +238,112 @@ ssize_t File::size() const
>  	return st.st_size;
>  }
>  
> +/**
> + * \fn off_t File::pos() const
> + * \brief Return current read or write position
> + *
> + * If the file is closed, this function returns 0.
> + *
> + * \return The current read or write position
> + */
> +
> +/**
> + * \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;
> +
> +	pos_ = ret;
> +	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<uint8_t> &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;
> +
> +	pos_ += readBytes;
> +	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<const uint8_t> &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;
> +
> +	pos_ += writtenBytes;
> +	return writtenBytes;
> +}
> +
>  /**
>   * \brief Map a region of the file in the process memory
>   * \param[in] offset The region offset within the file
> -- 
> Regards,
> 
> Laurent Pinchart
> 
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Kieran Bingham July 13, 2020, 8:31 a.m. UTC | #3
Hi Niklas / Laurent,

On 13/07/2020 07:53, Niklas Söderlund wrote:
> Hi Laurent,
> 
> Thanks for your work.
> 
> On 2020-07-12 17:44:16 +0300, Laurent Pinchart wrote:
>> Add basic support to read and write data from/to a file, along with
>> retrieving and setting the current read/write position.
>>
>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>> ---
>>  include/libcamera/internal/file.h |   7 ++
>>  src/libcamera/file.cpp            | 111 +++++++++++++++++++++++++++++-
>>  2 files changed, 116 insertions(+), 2 deletions(-)
>>
>> diff --git a/include/libcamera/internal/file.h b/include/libcamera/internal/file.h
>> index e3e72132e336..ead94cdf29c9 100644
>> --- a/include/libcamera/internal/file.h
>> +++ b/include/libcamera/internal/file.h
>> @@ -49,6 +49,12 @@ public:
>>  	int error() const { return error_; }
>>  	ssize_t size() const;
>>  
>> +	off_t pos() const { return pos_; }
> 
> Would it not be nicer to return lseek(fd_, 0, SEEK_CUR) here? That way 
> we could avoid keeping pos_ in sync in potential corner cases.

Aha ... this would of course address my concerns about pos_ becoming
invalid on error cases ...

--
Kieran


> 
>> +	off_t seek(off_t pos);
>> +
>> +	ssize_t read(const Span<uint8_t> &data);
>> +	ssize_t write(const Span<const uint8_t> &data);
>> +
>>  	Span<uint8_t> map(off_t offset = 0, ssize_t size = -1,
>>  			  MapFlag flags = MapNoOption);
>>  	bool unmap(uint8_t *addr);
>> @@ -61,6 +67,7 @@ private:
>>  	std::string name_;
>>  	int fd_;
>>  	OpenMode mode_;
>> +	off_t pos_;
>>  
>>  	int error_;
>>  	std::map<void *, size_t> maps_;
>> diff --git a/src/libcamera/file.cpp b/src/libcamera/file.cpp
>> index c471bde3fc68..8bd109090919 100644
>> --- a/src/libcamera/file.cpp
>> +++ b/src/libcamera/file.cpp
>> @@ -73,7 +73,7 @@ LOG_DEFINE_CATEGORY(File);
>>   * before performing I/O operations.
>>   */
>>  File::File(const std::string &name)
>> -	: name_(name), fd_(-1), mode_(NotOpen), error_(0)
>> +	: name_(name), fd_(-1), mode_(NotOpen), pos_(0), error_(0)
>>  {
>>  }
>>  
>> @@ -84,7 +84,7 @@ File::File(const std::string &name)
>>   * setFileName().
>>   */
>>  File::File()
>> -	: fd_(-1), mode_(NotOpen), error_(0)
>> +	: fd_(-1), mode_(NotOpen), pos_(0), error_(0)
>>  {
>>  }
>>  
>> @@ -202,6 +202,7 @@ void File::close()
>>  	::close(fd_);
>>  	fd_ = -1;
>>  	mode_ = NotOpen;
>> +	pos_ = 0;
>>  }
>>  
>>  /**
>> @@ -237,6 +238,112 @@ ssize_t File::size() const
>>  	return st.st_size;
>>  }
>>  
>> +/**
>> + * \fn off_t File::pos() const
>> + * \brief Return current read or write position
>> + *
>> + * If the file is closed, this function returns 0.
>> + *
>> + * \return The current read or write position
>> + */
>> +
>> +/**
>> + * \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;
>> +
>> +	pos_ = ret;
>> +	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<uint8_t> &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;
>> +
>> +	pos_ += readBytes;
>> +	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<const uint8_t> &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;
>> +
>> +	pos_ += writtenBytes;
>> +	return writtenBytes;
>> +}
>> +
>>  /**
>>   * \brief Map a region of the file in the process memory
>>   * \param[in] offset The region offset within the file
>> -- 
>> Regards,
>>
>> Laurent Pinchart
>>
>> _______________________________________________
>> libcamera-devel mailing list
>> libcamera-devel@lists.libcamera.org
>> https://lists.libcamera.org/listinfo/libcamera-devel
>
Kieran Bingham July 13, 2020, 8:34 a.m. UTC | #4
On 12/07/2020 20:32, Kieran Bingham wrote:
> Hi Laurent,
> 
> On 12/07/2020 15:44, Laurent Pinchart wrote:
>> Add basic support to read and write data from/to a file, along with
>> retrieving and setting the current read/write position.
>>
>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>> ---
>>  include/libcamera/internal/file.h |   7 ++
>>  src/libcamera/file.cpp            | 111 +++++++++++++++++++++++++++++-
>>  2 files changed, 116 insertions(+), 2 deletions(-)
>>
>> diff --git a/include/libcamera/internal/file.h b/include/libcamera/internal/file.h
>> index e3e72132e336..ead94cdf29c9 100644
>> --- a/include/libcamera/internal/file.h
>> +++ b/include/libcamera/internal/file.h
>> @@ -49,6 +49,12 @@ public:
>>  	int error() const { return error_; }
>>  	ssize_t size() const;
>>  
>> +	off_t pos() const { return pos_; }
>> +	off_t seek(off_t pos);
>> +
>> +	ssize_t read(const Span<uint8_t> &data);
>> +	ssize_t write(const Span<const uint8_t> &data);
>> +
>>  	Span<uint8_t> map(off_t offset = 0, ssize_t size = -1,
>>  			  MapFlag flags = MapNoOption);
>>  	bool unmap(uint8_t *addr);
>> @@ -61,6 +67,7 @@ private:
>>  	std::string name_;
>>  	int fd_;
>>  	OpenMode mode_;
>> +	off_t pos_;
>>  
>>  	int error_;
>>  	std::map<void *, size_t> maps_;
>> diff --git a/src/libcamera/file.cpp b/src/libcamera/file.cpp
>> index c471bde3fc68..8bd109090919 100644
>> --- a/src/libcamera/file.cpp
>> +++ b/src/libcamera/file.cpp
>> @@ -73,7 +73,7 @@ LOG_DEFINE_CATEGORY(File);
>>   * before performing I/O operations.
>>   */
>>  File::File(const std::string &name)
>> -	: name_(name), fd_(-1), mode_(NotOpen), error_(0)
>> +	: name_(name), fd_(-1), mode_(NotOpen), pos_(0), error_(0)
>>  {
>>  }
>>  
>> @@ -84,7 +84,7 @@ File::File(const std::string &name)
>>   * setFileName().
>>   */
>>  File::File()
>> -	: fd_(-1), mode_(NotOpen), error_(0)
>> +	: fd_(-1), mode_(NotOpen), pos_(0), error_(0)
>>  {
>>  }
>>  
>> @@ -202,6 +202,7 @@ void File::close()
>>  	::close(fd_);
>>  	fd_ = -1;
>>  	mode_ = NotOpen;
>> +	pos_ = 0;
>>  }
>>  
>>  /**
>> @@ -237,6 +238,112 @@ ssize_t File::size() const
>>  	return st.st_size;
>>  }
>>  
>> +/**
>> + * \fn off_t File::pos() const
>> + * \brief Return current read or write position
>> + *
>> + * If the file is closed, this function returns 0.
>> + *
>> + * \return The current read or write position
>> + */
>> +
>> +/**
>> + * \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;
>> +
>> +	pos_ = ret;
>> +	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<uint8_t> &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)
> 
> the man page for read(2) states:
> 
> 
> # On  error,  -1  is returned, and errno is set appropriately.  In this
> # case, it is left unspecified whether the file position (if any)
> # changes.
> 
> 
> In the event of an error, that could thus leave pos_ invalid, but I
> don't think we care too much about that for an internal helper which
> probably isn't going to use pos much anyway.
> 
> We could:
> 
> A) call lseek to explicitly reset pos
> B) Continue to do nothing.

C) Call lseek directly to obtain the actual pos...
D) Remove pos_ and follow neg's suggestion ;-)

Even still, once a route forwards on that is addressed this still holds:

> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> 
>> +		return -errno;
>> +
>> +	pos_ += readBytes;
>> +	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<const uint8_t> &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;
>> +
>> +	pos_ += writtenBytes;> +	return writtenBytes;
>> +}
>> +
>>  /**
>>   * \brief Map a region of the file in the process memory
>>   * \param[in] offset The region offset within the file
>>
>
Laurent Pinchart July 13, 2020, 10:55 p.m. UTC | #5
Hi Niklas, Kieran,

On Mon, Jul 13, 2020 at 09:31:42AM +0100, Kieran Bingham wrote:
> Hi Niklas / Laurent,
> 
> On 13/07/2020 07:53, Niklas Söderlund wrote:
> > Hi Laurent,
> > 
> > Thanks for your work.
> > 
> > On 2020-07-12 17:44:16 +0300, Laurent Pinchart wrote:
> >> Add basic support to read and write data from/to a file, along with
> >> retrieving and setting the current read/write position.
> >>
> >> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >> ---
> >>  include/libcamera/internal/file.h |   7 ++
> >>  src/libcamera/file.cpp            | 111 +++++++++++++++++++++++++++++-
> >>  2 files changed, 116 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/include/libcamera/internal/file.h b/include/libcamera/internal/file.h
> >> index e3e72132e336..ead94cdf29c9 100644
> >> --- a/include/libcamera/internal/file.h
> >> +++ b/include/libcamera/internal/file.h
> >> @@ -49,6 +49,12 @@ public:
> >>  	int error() const { return error_; }
> >>  	ssize_t size() const;
> >>  
> >> +	off_t pos() const { return pos_; }
> > 
> > Would it not be nicer to return lseek(fd_, 0, SEEK_CUR) here? That way 
> > we could avoid keeping pos_ in sync in potential corner cases.
> 
> Aha ... this would of course address my concerns about pos_ becoming
> invalid on error cases ...

Wouldn't it be more efficient to update pos_ in case of a read or write
error ? Efficiency may not be our primary concern here though. I don't
mind much either way, does anyone have a preference ?

> >> +	off_t seek(off_t pos);
> >> +
> >> +	ssize_t read(const Span<uint8_t> &data);
> >> +	ssize_t write(const Span<const uint8_t> &data);
> >> +
> >>  	Span<uint8_t> map(off_t offset = 0, ssize_t size = -1,
> >>  			  MapFlag flags = MapNoOption);
> >>  	bool unmap(uint8_t *addr);
> >> @@ -61,6 +67,7 @@ private:
> >>  	std::string name_;
> >>  	int fd_;
> >>  	OpenMode mode_;
> >> +	off_t pos_;
> >>  
> >>  	int error_;
> >>  	std::map<void *, size_t> maps_;
> >> diff --git a/src/libcamera/file.cpp b/src/libcamera/file.cpp
> >> index c471bde3fc68..8bd109090919 100644
> >> --- a/src/libcamera/file.cpp
> >> +++ b/src/libcamera/file.cpp
> >> @@ -73,7 +73,7 @@ LOG_DEFINE_CATEGORY(File);
> >>   * before performing I/O operations.
> >>   */
> >>  File::File(const std::string &name)
> >> -	: name_(name), fd_(-1), mode_(NotOpen), error_(0)
> >> +	: name_(name), fd_(-1), mode_(NotOpen), pos_(0), error_(0)
> >>  {
> >>  }
> >>  
> >> @@ -84,7 +84,7 @@ File::File(const std::string &name)
> >>   * setFileName().
> >>   */
> >>  File::File()
> >> -	: fd_(-1), mode_(NotOpen), error_(0)
> >> +	: fd_(-1), mode_(NotOpen), pos_(0), error_(0)
> >>  {
> >>  }
> >>  
> >> @@ -202,6 +202,7 @@ void File::close()
> >>  	::close(fd_);
> >>  	fd_ = -1;
> >>  	mode_ = NotOpen;
> >> +	pos_ = 0;
> >>  }
> >>  
> >>  /**
> >> @@ -237,6 +238,112 @@ ssize_t File::size() const
> >>  	return st.st_size;
> >>  }
> >>  
> >> +/**
> >> + * \fn off_t File::pos() const
> >> + * \brief Return current read or write position
> >> + *
> >> + * If the file is closed, this function returns 0.
> >> + *
> >> + * \return The current read or write position
> >> + */
> >> +
> >> +/**
> >> + * \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;
> >> +
> >> +	pos_ = ret;
> >> +	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<uint8_t> &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;
> >> +
> >> +	pos_ += readBytes;
> >> +	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<const uint8_t> &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;
> >> +
> >> +	pos_ += writtenBytes;
> >> +	return writtenBytes;
> >> +}
> >> +
> >>  /**
> >>   * \brief Map a region of the file in the process memory
> >>   * \param[in] offset The region offset within the file
Niklas Söderlund July 14, 2020, 5:52 a.m. UTC | #6
Hi Laurent,

On 2020-07-14 01:55:36 +0300, Laurent Pinchart wrote:
> Hi Niklas, Kieran,
> 
> On Mon, Jul 13, 2020 at 09:31:42AM +0100, Kieran Bingham wrote:
> > Hi Niklas / Laurent,
> > 
> > On 13/07/2020 07:53, Niklas Söderlund wrote:
> > > Hi Laurent,
> > > 
> > > Thanks for your work.
> > > 
> > > On 2020-07-12 17:44:16 +0300, Laurent Pinchart wrote:
> > >> Add basic support to read and write data from/to a file, along with
> > >> retrieving and setting the current read/write position.
> > >>
> > >> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > >> ---
> > >>  include/libcamera/internal/file.h |   7 ++
> > >>  src/libcamera/file.cpp            | 111 +++++++++++++++++++++++++++++-
> > >>  2 files changed, 116 insertions(+), 2 deletions(-)
> > >>
> > >> diff --git a/include/libcamera/internal/file.h b/include/libcamera/internal/file.h
> > >> index e3e72132e336..ead94cdf29c9 100644
> > >> --- a/include/libcamera/internal/file.h
> > >> +++ b/include/libcamera/internal/file.h
> > >> @@ -49,6 +49,12 @@ public:
> > >>  	int error() const { return error_; }
> > >>  	ssize_t size() const;
> > >>  
> > >> +	off_t pos() const { return pos_; }
> > > 
> > > Would it not be nicer to return lseek(fd_, 0, SEEK_CUR) here? That way 
> > > we could avoid keeping pos_ in sync in potential corner cases.
> > 
> > Aha ... this would of course address my concerns about pos_ becoming
> > invalid on error cases ...
> 
> Wouldn't it be more efficient to update pos_ in case of a read or write
> error ? Efficiency may not be our primary concern here though. I don't
> mind much either way, does anyone have a preference ?

No strong preference, but if the kernel keeps track of things for you it 
seems folly to reimplement it in user-space :-)

Whatever counters the kernel keep to make SEEK_CUR work it will still do 
disregarding if we use it or not. So the only efficiency gain would be 
if we read it often and modified it a seldom, in that case I rather just 
cache SEEK_CUR and refresh it each time we to a write() or read().

> 
> > >> +	off_t seek(off_t pos);
> > >> +
> > >> +	ssize_t read(const Span<uint8_t> &data);
> > >> +	ssize_t write(const Span<const uint8_t> &data);
> > >> +
> > >>  	Span<uint8_t> map(off_t offset = 0, ssize_t size = -1,
> > >>  			  MapFlag flags = MapNoOption);
> > >>  	bool unmap(uint8_t *addr);
> > >> @@ -61,6 +67,7 @@ private:
> > >>  	std::string name_;
> > >>  	int fd_;
> > >>  	OpenMode mode_;
> > >> +	off_t pos_;
> > >>  
> > >>  	int error_;
> > >>  	std::map<void *, size_t> maps_;
> > >> diff --git a/src/libcamera/file.cpp b/src/libcamera/file.cpp
> > >> index c471bde3fc68..8bd109090919 100644
> > >> --- a/src/libcamera/file.cpp
> > >> +++ b/src/libcamera/file.cpp
> > >> @@ -73,7 +73,7 @@ LOG_DEFINE_CATEGORY(File);
> > >>   * before performing I/O operations.
> > >>   */
> > >>  File::File(const std::string &name)
> > >> -	: name_(name), fd_(-1), mode_(NotOpen), error_(0)
> > >> +	: name_(name), fd_(-1), mode_(NotOpen), pos_(0), error_(0)
> > >>  {
> > >>  }
> > >>  
> > >> @@ -84,7 +84,7 @@ File::File(const std::string &name)
> > >>   * setFileName().
> > >>   */
> > >>  File::File()
> > >> -	: fd_(-1), mode_(NotOpen), error_(0)
> > >> +	: fd_(-1), mode_(NotOpen), pos_(0), error_(0)
> > >>  {
> > >>  }
> > >>  
> > >> @@ -202,6 +202,7 @@ void File::close()
> > >>  	::close(fd_);
> > >>  	fd_ = -1;
> > >>  	mode_ = NotOpen;
> > >> +	pos_ = 0;
> > >>  }
> > >>  
> > >>  /**
> > >> @@ -237,6 +238,112 @@ ssize_t File::size() const
> > >>  	return st.st_size;
> > >>  }
> > >>  
> > >> +/**
> > >> + * \fn off_t File::pos() const
> > >> + * \brief Return current read or write position
> > >> + *
> > >> + * If the file is closed, this function returns 0.
> > >> + *
> > >> + * \return The current read or write position
> > >> + */
> > >> +
> > >> +/**
> > >> + * \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;
> > >> +
> > >> +	pos_ = ret;
> > >> +	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<uint8_t> &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;
> > >> +
> > >> +	pos_ += readBytes;
> > >> +	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<const uint8_t> &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;
> > >> +
> > >> +	pos_ += writtenBytes;
> > >> +	return writtenBytes;
> > >> +}
> > >> +
> > >>  /**
> > >>   * \brief Map a region of the file in the process memory
> > >>   * \param[in] offset The region offset within the file
> 
> -- 
> Regards,
> 
> Laurent Pinchart
Kieran Bingham July 14, 2020, 7:59 a.m. UTC | #7
Hi,

On 14/07/2020 06:52, Niklas Söderlund wrote:
> Hi Laurent,
> 
> On 2020-07-14 01:55:36 +0300, Laurent Pinchart wrote:
>> Hi Niklas, Kieran,
>>
>> On Mon, Jul 13, 2020 at 09:31:42AM +0100, Kieran Bingham wrote:
>>> Hi Niklas / Laurent,
>>>
>>> On 13/07/2020 07:53, Niklas Söderlund wrote:
>>>> Hi Laurent,
>>>>
>>>> Thanks for your work.
>>>>
>>>> On 2020-07-12 17:44:16 +0300, Laurent Pinchart wrote:
>>>>> Add basic support to read and write data from/to a file, along with
>>>>> retrieving and setting the current read/write position.
>>>>>
>>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>>>> ---
>>>>>  include/libcamera/internal/file.h |   7 ++
>>>>>  src/libcamera/file.cpp            | 111 +++++++++++++++++++++++++++++-
>>>>>  2 files changed, 116 insertions(+), 2 deletions(-)
>>>>>
>>>>> diff --git a/include/libcamera/internal/file.h b/include/libcamera/internal/file.h
>>>>> index e3e72132e336..ead94cdf29c9 100644
>>>>> --- a/include/libcamera/internal/file.h
>>>>> +++ b/include/libcamera/internal/file.h
>>>>> @@ -49,6 +49,12 @@ public:
>>>>>  	int error() const { return error_; }
>>>>>  	ssize_t size() const;
>>>>>  
>>>>> +	off_t pos() const { return pos_; }
>>>>
>>>> Would it not be nicer to return lseek(fd_, 0, SEEK_CUR) here? That way 
>>>> we could avoid keeping pos_ in sync in potential corner cases.
>>>
>>> Aha ... this would of course address my concerns about pos_ becoming
>>> invalid on error cases ...
>>
>> Wouldn't it be more efficient to update pos_ in case of a read or write
>> error ? Efficiency may not be our primary concern here though. I don't
>> mind much either way, does anyone have a preference ?
> 
> No strong preference, but if the kernel keeps track of things for you it 
> seems folly to reimplement it in user-space :-)
> 
> Whatever counters the kernel keep to make SEEK_CUR work it will still do 
> disregarding if we use it or not. So the only efficiency gain would be 
> if we read it often and modified it a seldom, in that case I rather just 
> cache SEEK_CUR and refresh it each time we to a write() or read().

My feel is that it's keeping a cached value of the position in
userspace, which is rarely (if ever) used, and not expected to be a
performance gain to cache it, /and/ it can be wrong in error cases, and
thus need updating.

IMO, it's likely adding code for the sake of it.
I'd go for just a direct call to lseek to obtain pos() when requested,
which is probably just a helper and doesn't have any callers anyway?

If that ever became a performance bottle neck, it could be cached, but I
really can't see that being a case we're going to experience in this
instance.

Do we even need to provide pos() at all?

I don't mind adding it as a generic API function on the libcamera::File
API, but it's internal anyway?

Perhaps we'll see more use of the libcamera::File api for
reading/writing configuration files ... is that where you anticipate
needing to obtain the current pos()?

--
Kieran




>>>>> +	off_t seek(off_t pos);
>>>>> +
>>>>> +	ssize_t read(const Span<uint8_t> &data);
>>>>> +	ssize_t write(const Span<const uint8_t> &data);
>>>>> +
>>>>>  	Span<uint8_t> map(off_t offset = 0, ssize_t size = -1,
>>>>>  			  MapFlag flags = MapNoOption);
>>>>>  	bool unmap(uint8_t *addr);
>>>>> @@ -61,6 +67,7 @@ private:
>>>>>  	std::string name_;
>>>>>  	int fd_;
>>>>>  	OpenMode mode_;
>>>>> +	off_t pos_;
>>>>>  
>>>>>  	int error_;
>>>>>  	std::map<void *, size_t> maps_;
>>>>> diff --git a/src/libcamera/file.cpp b/src/libcamera/file.cpp
>>>>> index c471bde3fc68..8bd109090919 100644
>>>>> --- a/src/libcamera/file.cpp
>>>>> +++ b/src/libcamera/file.cpp
>>>>> @@ -73,7 +73,7 @@ LOG_DEFINE_CATEGORY(File);
>>>>>   * before performing I/O operations.
>>>>>   */
>>>>>  File::File(const std::string &name)
>>>>> -	: name_(name), fd_(-1), mode_(NotOpen), error_(0)
>>>>> +	: name_(name), fd_(-1), mode_(NotOpen), pos_(0), error_(0)
>>>>>  {
>>>>>  }
>>>>>  
>>>>> @@ -84,7 +84,7 @@ File::File(const std::string &name)
>>>>>   * setFileName().
>>>>>   */
>>>>>  File::File()
>>>>> -	: fd_(-1), mode_(NotOpen), error_(0)
>>>>> +	: fd_(-1), mode_(NotOpen), pos_(0), error_(0)
>>>>>  {
>>>>>  }
>>>>>  
>>>>> @@ -202,6 +202,7 @@ void File::close()
>>>>>  	::close(fd_);
>>>>>  	fd_ = -1;
>>>>>  	mode_ = NotOpen;
>>>>> +	pos_ = 0;
>>>>>  }
>>>>>  
>>>>>  /**
>>>>> @@ -237,6 +238,112 @@ ssize_t File::size() const
>>>>>  	return st.st_size;
>>>>>  }
>>>>>  
>>>>> +/**
>>>>> + * \fn off_t File::pos() const
>>>>> + * \brief Return current read or write position
>>>>> + *
>>>>> + * If the file is closed, this function returns 0.
>>>>> + *
>>>>> + * \return The current read or write position
>>>>> + */
>>>>> +
>>>>> +/**
>>>>> + * \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;
>>>>> +
>>>>> +	pos_ = ret;
>>>>> +	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<uint8_t> &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;
>>>>> +
>>>>> +	pos_ += readBytes;
>>>>> +	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<const uint8_t> &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;
>>>>> +
>>>>> +	pos_ += writtenBytes;
>>>>> +	return writtenBytes;
>>>>> +}
>>>>> +
>>>>>  /**
>>>>>   * \brief Map a region of the file in the process memory
>>>>>   * \param[in] offset The region offset within the file
>>
>> -- 
>> Regards,
>>
>> Laurent Pinchart
>

Patch

diff --git a/include/libcamera/internal/file.h b/include/libcamera/internal/file.h
index e3e72132e336..ead94cdf29c9 100644
--- a/include/libcamera/internal/file.h
+++ b/include/libcamera/internal/file.h
@@ -49,6 +49,12 @@  public:
 	int error() const { return error_; }
 	ssize_t size() const;
 
+	off_t pos() const { return pos_; }
+	off_t seek(off_t pos);
+
+	ssize_t read(const Span<uint8_t> &data);
+	ssize_t write(const Span<const uint8_t> &data);
+
 	Span<uint8_t> map(off_t offset = 0, ssize_t size = -1,
 			  MapFlag flags = MapNoOption);
 	bool unmap(uint8_t *addr);
@@ -61,6 +67,7 @@  private:
 	std::string name_;
 	int fd_;
 	OpenMode mode_;
+	off_t pos_;
 
 	int error_;
 	std::map<void *, size_t> maps_;
diff --git a/src/libcamera/file.cpp b/src/libcamera/file.cpp
index c471bde3fc68..8bd109090919 100644
--- a/src/libcamera/file.cpp
+++ b/src/libcamera/file.cpp
@@ -73,7 +73,7 @@  LOG_DEFINE_CATEGORY(File);
  * before performing I/O operations.
  */
 File::File(const std::string &name)
-	: name_(name), fd_(-1), mode_(NotOpen), error_(0)
+	: name_(name), fd_(-1), mode_(NotOpen), pos_(0), error_(0)
 {
 }
 
@@ -84,7 +84,7 @@  File::File(const std::string &name)
  * setFileName().
  */
 File::File()
-	: fd_(-1), mode_(NotOpen), error_(0)
+	: fd_(-1), mode_(NotOpen), pos_(0), error_(0)
 {
 }
 
@@ -202,6 +202,7 @@  void File::close()
 	::close(fd_);
 	fd_ = -1;
 	mode_ = NotOpen;
+	pos_ = 0;
 }
 
 /**
@@ -237,6 +238,112 @@  ssize_t File::size() const
 	return st.st_size;
 }
 
+/**
+ * \fn off_t File::pos() const
+ * \brief Return current read or write position
+ *
+ * If the file is closed, this function returns 0.
+ *
+ * \return The current read or write position
+ */
+
+/**
+ * \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;
+
+	pos_ = ret;
+	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<uint8_t> &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;
+
+	pos_ += readBytes;
+	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<const uint8_t> &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;
+
+	pos_ += writtenBytes;
+	return writtenBytes;
+}
+
 /**
  * \brief Map a region of the file in the process memory
  * \param[in] offset The region offset within the file