genode/repos/os/include/file_system/util.h
Emery Hemingway 2f1db06deb rump_fs/fuse_fs/lx_fs/ram_fs: symlink fixup
Allow symlinks to be passed to the read and write file system utilities.

Disallow writes to symlinks with offsets in file system servers, this is
to ensure that writing the target of a symlink is an atomic operation.

Fixes #1604
2015-07-21 09:40:19 +02:00

244 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),
0,
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),
0,
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_ */