genode/repos/os/include/file_system/util.h
Christian Helmuth 24b1f269be Remove Packet_ref from File_system::Packet_descriptor
The intention of Packet_ref was to allow clients to place opaque
references into the packet descriptor itself, which could be observed on
packet completion. Currently no component in our sources uses this
feature and beyond that it is questionable if it should be used at all:
If the server tampers with the ref the client may easily be confused
into observing an incorrect or invalid context. It seems better to
remove the opaque context from the descriptor and leave the actual
implementation to the client and its needs.
2015-09-30 12:20:37 +02:00

242 lines
5.2 KiB
C++

/*
* \brief Utilities
* \author Norman Feske
* \date 2012-04-11
*/
#ifndef _FILE_SYSTEM__UTIL_H_
#define _FILE_SYSTEM__UTIL_H_
#include <file_system_session/file_system_session.h>
#include <os/path.h>
namespace File_system {
/**
* Return true if character 'c' occurs in null-terminated string 'str'
*/
inline bool string_contains(char const *str, char c)
{
for (; *str; str++)
if (*str == c)
return true;
return false;
}
/**
* Return base-name portion of null-terminated path string
*/
static inline char const *basename(char const *path)
{
char const *start = path;
for (; *path; path++)
if (*path == '/')
start = path + 1;
return start;
}
/**
* Return true if specified path is a base name (contains no path delimiters)
*/
static inline bool is_basename(char const *path)
{
for (; *path; path++)
if (*path == '/')
return false;
return true;
}
/**
* Return true if 'str' is a valid node name
*/
static inline bool valid_name(char const *str)
{
if (string_contains(str, '/')) return false;
/* must have at least one character */
if (str[0] == 0) return false;
return true;
}
/**
* Open a directory, ensuring all parent directories exists.
*/
static inline Dir_handle ensure_dir(Session &fs, char const *path)
{
try {
return fs.dir(path, false);
} catch (Lookup_failed) {
try {
return fs.dir(path, true);
} catch (Lookup_failed) {
Genode::Path<MAX_PATH_LEN> target(path);
target.strip_last_element();
target.remove_trailing('/');
fs.close(ensure_dir(fs, target.base()));
}
}
return fs.dir(path, true);
}
/**
* Collect pending packet acknowledgements, freeing the space occupied
* by the packet in the bulk buffer
*
* This function should be called prior enqueing new packets into the
* packet stream to free up space in the bulk buffer.
*/
static void collect_acknowledgements(Session::Tx::Source &source)
{
while (source.ack_avail())
source.release_packet(source.get_acked_packet());
}
/**
* Read file content
*/
static inline size_t read(Session &fs, Node_handle const &node_handle,
void *dst, size_t count, seek_off_t seek_offset = 0)
{
bool success = true;
Session::Tx::Source &source = *fs.tx();
size_t const max_packet_size = source.bulk_buffer_size() / 2;
size_t remaining_count = count;
while (remaining_count && success) {
collect_acknowledgements(source);
size_t const curr_packet_size = min(remaining_count, max_packet_size);
Packet_descriptor
packet(source.alloc_packet(curr_packet_size),
node_handle,
File_system::Packet_descriptor::READ,
curr_packet_size,
seek_offset);
/* pass packet to server side */
source.submit_packet(packet);
packet = source.get_acked_packet();
success = packet.succeeded();
size_t const read_num_bytes = min(packet.length(), curr_packet_size);
/* copy-out payload into destination buffer */
memcpy(dst, source.packet_content(packet), read_num_bytes);
source.release_packet(packet);
/* prepare next iteration */
seek_offset += read_num_bytes;
dst = (void *)((Genode::addr_t)dst + read_num_bytes);
remaining_count -= read_num_bytes;
/*
* If we received less bytes than requested, we reached the end
* of the file.
*/
if (read_num_bytes < curr_packet_size)
break;
}
return count - remaining_count;
}
/**
* Write file content
*/
static inline size_t write(Session &fs, Node_handle const &node_handle,
void const *src, size_t count, seek_off_t seek_offset = 0)
{
bool success = true;
Session::Tx::Source &source = *fs.tx();
size_t const max_packet_size = source.bulk_buffer_size() / 2;
size_t remaining_count = count;
while (remaining_count && success) {
collect_acknowledgements(source);
size_t const curr_packet_size = min(remaining_count, max_packet_size);
Packet_descriptor
packet(source.alloc_packet(curr_packet_size),
node_handle,
File_system::Packet_descriptor::WRITE,
curr_packet_size,
seek_offset);
/* copy-out source buffer into payload */
memcpy(source.packet_content(packet), src, curr_packet_size);
/* pass packet to server side */
source.submit_packet(packet);
packet = source.get_acked_packet();;
success = packet.succeeded();
source.release_packet(packet);
/* prepare next iteration */
seek_offset += curr_packet_size;
src = (void *)((Genode::addr_t)src + curr_packet_size);
remaining_count -= curr_packet_size;
}
return count - remaining_count;
}
class Handle_guard
{
private:
Session &_session;
Node_handle _handle;
public:
Handle_guard(Session &session, Node_handle handle)
: _session(session), _handle(handle) { }
~Handle_guard() { _session.close(_handle); }
};
class Packet_guard
{
private:
Session::Tx::Source &_source;
File_system::Packet_descriptor _packet;
public:
Packet_guard(Session::Tx::Source &source,
File_system::Packet_descriptor packet)
: _source(source), _packet(packet) { }
~Packet_guard()
{
_source.release_packet(_packet);
}
};
}
#endif /* _FILE_SYSTEM__UTIL_H_ */