summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrandomdan <randomdan@localhost>2009-10-23 19:17:14 +0000
committerrandomdan <randomdan@localhost>2009-10-23 19:17:14 +0000
commit212a4763b7f5236e05b38f6ab5190678e1926c0a (patch)
treeecd19468f47e1993a6e669b826a00c2f09d43337
parentAll messages coded, change to ensure msg IDs are unique at compile time (diff)
downloadnetfs-212a4763b7f5236e05b38f6ab5190678e1926c0a.tar.bz2
netfs-212a4763b7f5236e05b38f6ab5190678e1926c0a.tar.xz
netfs-212a4763b7f5236e05b38f6ab5190678e1926c0a.zip
All basic funcs implemented, not tested
-rw-r--r--netfs/comms.cpp10
-rw-r--r--netfs/comms.h19
-rw-r--r--netfs/daemon.cpp183
-rw-r--r--netfs/daemon.h49
-rw-r--r--netfs/daemonDirs.cpp82
-rw-r--r--netfs/daemonFS.cpp20
-rw-r--r--netfs/daemonFiles.cpp118
-rw-r--r--netfs/daemonMisc.cpp108
-rw-r--r--netfs/fuse.cpp190
-rw-r--r--netfs/fuse.h60
-rw-r--r--netfs/fuseDirs.cpp48
-rw-r--r--netfs/fuseFS.cpp13
-rw-r--r--netfs/fuseFiles.cpp75
-rw-r--r--netfs/fuseMisc.cpp87
-rw-r--r--netfs/makefile.in8
-rw-r--r--netfs/msgtypes.cpp30
-rw-r--r--netfs/msgtypes.h10
17 files changed, 793 insertions, 317 deletions
diff --git a/netfs/comms.cpp b/netfs/comms.cpp
index 86a7313..5993ac7 100644
--- a/netfs/comms.cpp
+++ b/netfs/comms.cpp
@@ -49,11 +49,11 @@ packetSender(FILE * host)
Packet::Packet(FILE * host)
{
if (fread(&index, sizeof(index), 1, host) != 1) {
- // error
+ throw ReadMessageFailed();
}
DataPayload::TypeID t = 0;
if (fread(&t, sizeof(t), 1, host) != 1) {
- // error
+ throw ReadMessageFailed();
}
data = MsgFacs()[t]();
data->Read(host);
@@ -62,15 +62,15 @@ void
Packet::write(FILE * host) const
{
if (fwrite(&index, sizeof(index), 1, host) != 1) {
- // error
+ throw WriteMessageFailed();
}
DataPayload::TypeID t = data->dataTypeID();
if (fwrite(&t, sizeof(t), 1, host) != 1) {
- // error
+ throw WriteMessageFailed();
}
data->Send(host);
if (fflush(host) != 0) {
- //
+ throw WriteMessageFailed();
}
}
diff --git a/netfs/comms.h b/netfs/comms.h
index 54681b5..cd25a9e 100644
--- a/netfs/comms.h
+++ b/netfs/comms.h
@@ -3,9 +3,13 @@
#include <stdio.h>
#include <stdint.h>
-#include <fuse/fuse.h>
#include "smartpointer.h"
+#include "fuseapp.h"
+class WriteMessageFailed : std::exception {
+};
+class ReadMessageFailed : std::exception {
+};
class DataPayload : public IsRefCounted {
public:
typedef uint16_t TypeID;
@@ -24,6 +28,9 @@ class TypedPayload : public DataPayload {
void Send(FILE * h) const { data.Send(h); }
void Read(FILE * h) { data.Read(h); }
};
+typedef SmartPointer<DataPayload> DataPayloadPtr;
+typedef SmartPointer<const DataPayload> DataPayloadCPtr;
+
template <class Type>
class TypedPayloadReq : public TypedPayload<Type> {
public:
@@ -35,11 +42,15 @@ class TypedPayloadReq : public TypedPayload<Type> {
TypedPayload<Type>::data.uid = fc->uid;
TypedPayload<Type>::data.gid = fc->gid;
}
- ReplyPtr exchange(NetFS *);
+ ReplyPtr exchange(NetFS * net);
+ /*
+ {
+ DataPayloadPtr p = net->exchange(this);
+ return p.as<TypedPayload<typename Type::Reply> >();
+ }
+ */
};
-typedef SmartPointer<DataPayload> DataPayloadPtr;
-typedef SmartPointer<const DataPayload> DataPayloadCPtr;
class Packet : public IsRefCounted {
public:
diff --git a/netfs/daemon.cpp b/netfs/daemon.cpp
index 1cbb5bc..b029316 100644
--- a/netfs/daemon.cpp
+++ b/netfs/daemon.cpp
@@ -1,7 +1,6 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
-#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <set>
@@ -9,163 +8,19 @@
#include "comms.h"
#include "msgtypes.h"
#include "misc.h"
+#include "daemon.h"
-class DaemonGlobalState : public IsRefCounted {
-};
-typedef SmartPointer<DaemonGlobalState> DaemonGlobalStatePtr;
-class Sender {
- public:
- Sender(int i, FILE * f)
- {
- idx = i;
- host = f;
- }
- void Send(DataPayloadPtr p)
- {
- PacketPtr r = new Packet(idx, p);
- r->write(host);
- }
- private:
- int idx;
- FILE * host;
-};
-#define handler(type) void handle(SmartPointer<TypedPayload<type> > req, Sender & s)
-handler(AccessRequest)
-{
- TypedPayload<AccessRequest::Reply> * r = new TypedPayload<AccessRequest::Reply>();
- r->data.value = access(req->data.path.c_str(), req->data.access);
- s.Send(r);
-}
-handler(GetAttrRequest)
-{
- TypedPayload<GetAttrRequest::Reply> * r = new TypedPayload<GetAttrRequest::Reply>();
- r->data.res = stat(req->data.path.c_str(), &r->data.val);
- s.Send(r);
-}
-int dirNo = 0;
-std::map<int, DIR*> dirs;
-handler(OpenDirRequest)
-{
- TypedPayload<OpenDirRequest::Reply> * r = new TypedPayload<OpenDirRequest::Reply>();
- errno = 0;
- DIR * od = opendir(req->data.path.c_str());
- if (od) {
- dirs[++dirNo] = od;
- r->data.handle = dirNo;
- }
- r->data.error = errno;
- s.Send(r);
-}
-handler(CloseDirRequest)
-{
- TypedPayload<CloseDirRequest::Reply> * r = new TypedPayload<CloseDirRequest::Reply>();
- if (dirs.find(req->data.handle) != dirs.end()) {
- errno = 0;
- if (closedir(dirs[req->data.handle]) != 0) {
- r->data.error = errno;
- }
- else {
- dirs.erase(req->data.handle);
- }
- }
- else {
- r->data.error = EBADF;
- }
- s.Send(r);
-}
-typedef TypedPayload<ReadDirContent>::Ptr DirContPLPtr;
-typedef TypedPayload<ReadDirContent> DirContPL;
-typedef std::set<DirContPLPtr> DirContPLs;
-handler(ReadDirRequest)
-{
- TypedPayload<ReadDirRequest::Reply> * r = new TypedPayload<ReadDirRequest::Reply>();
- if (dirs.find(req->data.handle) != dirs.end()) {
- errno = 0;
- dirent * d;
- DirContPLs ds;
- while ((d = readdir(dirs[req->data.handle])) && errno == 0) {
- DirContPLPtr dc = new DirContPL();
- dc->data.path = d->d_name;
- ds.insert(dc);
- }
- r->data.error = errno;
- r->data.count = ds.size();
- s.Send(r);
- foreach(DirContPLs::const_iterator, ds, dir) {
- s.Send(*dir);
- }
- }
- else {
- r->data.error = EBADF;
- s.Send(r);
- }
-}
-int fileNo = 0;
-std::map<int, int> files;
-handler(OpenRequest)
+Sender::Sender(int i, FILE * f)
{
- TypedPayload<OpenRequest::Reply> * r = new TypedPayload<OpenRequest::Reply>();
- errno = 0;
- int fd = open(req->data.path.c_str(), 0);
- if (fd != -1) {
- files[++fileNo] = fd;
- r->data.handle = fileNo;
- }
- r->data.error = errno;
- s.Send(r);
+ idx = i;
+ host = f;
}
-handler(CloseRequest)
+void Sender::Send(DataPayloadPtr p)
{
- TypedPayload<CloseRequest::Reply> * r = new TypedPayload<CloseRequest::Reply>();
- if (files.find(req->data.handle) != files.end()) {
- errno = 0;
- if (close(files[req->data.handle]) != 0) {
- r->data.error = errno;
- }
- else {
- files.erase(req->data.handle);
- }
- }
- else {
- r->data.error = EBADF;
- }
- s.Send(r);
-}
-handler(ReadRequest)
-{
- TypedPayload<ReadRequest::Reply> * r = new TypedPayload<ReadRequest::Reply>();
- if (files.find(req->data.handle) != files.end()) {
- char * tmpdata = new char[req->data.size]();
- errno = 0;
- if (pread(files[req->data.handle], tmpdata, req->data.size, req->data.offset) != -1) {
- r->data.data = tmpdata;
- r->data.size = req->data.size;
- }
- else {
- r->data.size = 0;
- r->data.data = NULL;
- }
- r->data.error = errno;
- }
- else {
- r->data.error = EBADF;
- }
- s.Send(r);
-}
-handler(WriteRequest)
-{
- TypedPayload<WriteRequest::Reply> * r = new TypedPayload<WriteRequest::Reply>();
- if (files.find(req->data.handle) != files.end()) {
- errno = 0;
- if (pwrite(files[req->data.handle], req->data.data, req->data.size, req->data.offset) != -1) {
- }
- r->data.error = errno;
- }
- else {
- r->data.error = EBADF;
- }
- s.Send(r);
+ PacketPtr r = new Packet(idx, p);
+ r->write(host);
}
+
#define TRYCLASS(cls) \
if (type == cls::TypeID) { handle(p->data.as<TypedPayload<cls> >(), s); }
@@ -176,15 +31,33 @@ runDaemonOn(FILE * f, DaemonConfigPtr, DaemonGlobalStatePtr)
PacketPtr p = new Packet(f);
Sender s(p->index, f);
uint16_t type (p->data->dataTypeID());
- TRYCLASS(AccessRequest);
- TRYCLASS(GetAttrRequest);
+ // Dirs
TRYCLASS(OpenDirRequest);
TRYCLASS(CloseDirRequest);
TRYCLASS(ReadDirRequest);
+ TRYCLASS(RmDirRequest);
+ TRYCLASS(MkDirRequest);
+ // Misc
+ TRYCLASS(AccessRequest);
+ TRYCLASS(GetAttrRequest);
+ TRYCLASS(FgetAttrRequest);
+ TRYCLASS(UnlinkRequest);
+ TRYCLASS(SymlinkRequest);
+ TRYCLASS(LinkRequest);
+ TRYCLASS(RenameRequest);
+ TRYCLASS(ReadlinkRequest);
+ TRYCLASS(ChmodRequest);
+ TRYCLASS(ChownRequest);
+ // Files
TRYCLASS(OpenRequest);
TRYCLASS(CloseRequest);
TRYCLASS(ReadRequest);
TRYCLASS(WriteRequest);
+ TRYCLASS(TruncateRequest);
+ TRYCLASS(FtruncateRequest);
+ TRYCLASS(CreateRequest);
+ // FS
+ TRYCLASS(StatfsRequest);
}
fclose(f);
}
diff --git a/netfs/daemon.h b/netfs/daemon.h
new file mode 100644
index 0000000..2517529
--- /dev/null
+++ b/netfs/daemon.h
@@ -0,0 +1,49 @@
+#ifndef DAEMON_H
+#define DAEMON_H
+
+#include "comms.h"
+#include "msgtypes.h"
+
+class DaemonGlobalState : public IsRefCounted {
+};
+typedef SmartPointer<DaemonGlobalState> DaemonGlobalStatePtr;
+class Sender {
+ public:
+ Sender(int i, FILE * f);
+ void Send(DataPayloadPtr p);
+ private:
+ int idx;
+ FILE * host;
+};
+
+#define handler(type) void handle(SmartPointer<TypedPayload<type> > req, Sender & s)
+// Dirs
+handler(OpenDirRequest);
+handler(CloseDirRequest);
+handler(ReadDirRequest);
+handler(RmDirRequest);
+handler(MkDirRequest);
+// Misc
+handler(AccessRequest);
+handler(GetAttrRequest);
+handler(FgetAttrRequest);
+handler(UnlinkRequest);
+handler(SymlinkRequest);
+handler(LinkRequest);
+handler(RenameRequest);
+handler(ReadlinkRequest);
+handler(ChmodRequest);
+handler(ChownRequest);
+// Files
+handler(OpenRequest);
+handler(CloseRequest);
+handler(ReadRequest);
+handler(WriteRequest);
+handler(TruncateRequest);
+handler(FtruncateRequest);
+handler(CreateRequest);
+// FS
+handler(StatfsRequest);
+
+#endif
+
diff --git a/netfs/daemonDirs.cpp b/netfs/daemonDirs.cpp
new file mode 100644
index 0000000..89b2426
--- /dev/null
+++ b/netfs/daemonDirs.cpp
@@ -0,0 +1,82 @@
+#include <dirent.h>
+#include <errno.h>
+#include <set>
+#include "comms.h"
+#include "msgtypes.h"
+#include "daemon.h"
+#include "misc.h"
+
+int dirNo = 0;
+std::map<int, DIR*> dirs;
+handler(OpenDirRequest)
+{
+ TypedPayload<OpenDirRequest::Reply> * r = new TypedPayload<OpenDirRequest::Reply>();
+ errno = 0;
+ DIR * od = opendir(req->data.path.c_str());
+ if (od) {
+ dirs[++dirNo] = od;
+ r->data.handle = dirNo;
+ }
+ r->data.error = errno;
+ s.Send(r);
+}
+handler(CloseDirRequest)
+{
+ TypedPayload<CloseDirRequest::Reply> * r = new TypedPayload<CloseDirRequest::Reply>();
+ if (dirs.find(req->data.handle) != dirs.end()) {
+ errno = 0;
+ if (closedir(dirs[req->data.handle]) != 0) {
+ r->data.error = errno;
+ }
+ else {
+ dirs.erase(req->data.handle);
+ }
+ }
+ else {
+ r->data.error = EBADF;
+ }
+ s.Send(r);
+}
+typedef TypedPayload<ReadDirContent>::Ptr DirContPLPtr;
+typedef TypedPayload<ReadDirContent> DirContPL;
+typedef std::set<DirContPLPtr> DirContPLs;
+handler(ReadDirRequest)
+{
+ TypedPayload<ReadDirRequest::Reply> * r = new TypedPayload<ReadDirRequest::Reply>();
+ if (dirs.find(req->data.handle) != dirs.end()) {
+ errno = 0;
+ dirent * d;
+ DirContPLs ds;
+ while ((d = readdir(dirs[req->data.handle])) && errno == 0) {
+ DirContPLPtr dc = new DirContPL();
+ dc->data.path = d->d_name;
+ ds.insert(dc);
+ }
+ r->data.error = errno;
+ r->data.count = ds.size();
+ s.Send(r);
+ foreach(DirContPLs::const_iterator, ds, dir) {
+ s.Send(*dir);
+ }
+ }
+ else {
+ r->data.error = EBADF;
+ s.Send(r);
+ }
+}
+handler(MkDirRequest)
+{
+ TypedPayload<MkDirRequest::Reply> * r = new TypedPayload<MkDirRequest::Reply>();
+ errno = 0;
+ mkdir(req->data.path.c_str(), req->data.mode);
+ r->data.value = errno;
+ s.Send(r);
+}
+handler(RmDirRequest)
+{
+ TypedPayload<MkDirRequest::Reply> * r = new TypedPayload<MkDirRequest::Reply>();
+ errno = 0;
+ rmdir(req->data.path.c_str());
+ r->data.value = errno;
+ s.Send(r);
+}
diff --git a/netfs/daemonFS.cpp b/netfs/daemonFS.cpp
new file mode 100644
index 0000000..f8220ea
--- /dev/null
+++ b/netfs/daemonFS.cpp
@@ -0,0 +1,20 @@
+#include <errno.h>
+#include <sys/vfs.h>
+#include "comms.h"
+#include "msgtypes.h"
+#include "daemon.h"
+#include "misc.h"
+
+handler(StatfsRequest)
+{
+ TypedPayload<StatfsRequest::Reply> * r = new TypedPayload<StatfsRequest::Reply>();
+ errno = 0;
+ if (statvfs(req->data.path.c_str(), &r->data.statfs) != 0) {
+ r->data.error = errno;
+ }
+ else {
+ r->data.error = 0;
+ }
+ s.Send(r);
+}
+
diff --git a/netfs/daemonFiles.cpp b/netfs/daemonFiles.cpp
new file mode 100644
index 0000000..fe518ae
--- /dev/null
+++ b/netfs/daemonFiles.cpp
@@ -0,0 +1,118 @@
+#include <errno.h>
+#include <set>
+#include "comms.h"
+#include "msgtypes.h"
+#include "daemon.h"
+#include "misc.h"
+
+handler(TruncateRequest)
+{
+ TypedPayload<TruncateRequest::Reply> * r = new TypedPayload<TruncateRequest::Reply>();
+ errno = 0;
+ if (truncate(req->data.path.c_str(), req->data.size) != 0) {
+ r->data.value = errno;
+ }
+ else {
+ r->data.value = 0;
+ }
+ s.Send(r);
+}
+handler(FtruncateRequest)
+{
+ TypedPayload<FtruncateRequest::Reply> * r = new TypedPayload<FtruncateRequest::Reply>();
+ errno = 0;
+ if (ftruncate(req->data.handle, req->data.size) != 0) {
+ r->data.value = errno;
+ }
+ else {
+ r->data.value = 0;
+ }
+ s.Send(r);
+}
+handler(UnlinkRequest)
+{
+ TypedPayload<UnlinkRequest::Reply> * r = new TypedPayload<UnlinkRequest::Reply>();
+ errno = 0;
+ unlink(req->data.path.c_str());
+ r->data.value = errno;
+ s.Send(r);
+}
+int fileNo = 0;
+std::map<int, int> files;
+handler(OpenRequest)
+{
+ TypedPayload<OpenRequest::Reply> * r = new TypedPayload<OpenRequest::Reply>();
+ errno = 0;
+ int fd = open(req->data.path.c_str(), 0);
+ if (fd != -1) {
+ files[++fileNo] = fd;
+ r->data.handle = fileNo;
+ }
+ r->data.error = errno;
+ s.Send(r);
+}
+handler(CreateRequest)
+{
+ TypedPayload<OpenRequest::Reply> * r = new TypedPayload<OpenRequest::Reply>();
+ errno = 0;
+ int fd = open(req->data.path.c_str(), O_CREAT, req->data.mode);
+ if (fd != -1) {
+ files[++fileNo] = fd;
+ r->data.handle = fileNo;
+ }
+ r->data.error = errno;
+ s.Send(r);
+}
+handler(CloseRequest)
+{
+ TypedPayload<CloseRequest::Reply> * r = new TypedPayload<CloseRequest::Reply>();
+ if (files.find(req->data.handle) != files.end()) {
+ errno = 0;
+ if (close(files[req->data.handle]) != 0) {
+ r->data.error = errno;
+ }
+ else {
+ files.erase(req->data.handle);
+ }
+ }
+ else {
+ r->data.error = EBADF;
+ }
+ s.Send(r);
+}
+handler(ReadRequest)
+{
+ TypedPayload<ReadRequest::Reply> * r = new TypedPayload<ReadRequest::Reply>();
+ if (files.find(req->data.handle) != files.end()) {
+ char * tmpdata = new char[req->data.size]();
+ errno = 0;
+ if (pread(files[req->data.handle], tmpdata, req->data.size, req->data.offset) != -1) {
+ r->data.data = tmpdata;
+ r->data.size = req->data.size;
+ }
+ else {
+ r->data.size = 0;
+ r->data.data = NULL;
+ }
+ r->data.error = errno;
+ }
+ else {
+ r->data.error = EBADF;
+ }
+ s.Send(r);
+}
+handler(WriteRequest)
+{
+ TypedPayload<WriteRequest::Reply> * r = new TypedPayload<WriteRequest::Reply>();
+ if (files.find(req->data.handle) != files.end()) {
+ errno = 0;
+ if (pwrite(files[req->data.handle], req->data.data, req->data.size, req->data.offset) != -1) {
+ }
+ r->data.error = errno;
+ }
+ else {
+ r->data.error = EBADF;
+ }
+ s.Send(r);
+}
+
diff --git a/netfs/daemonMisc.cpp b/netfs/daemonMisc.cpp
new file mode 100644
index 0000000..e3cebc3
--- /dev/null
+++ b/netfs/daemonMisc.cpp
@@ -0,0 +1,108 @@
+#include <errno.h>
+#include <set>
+#include <unistd.h>
+#include <limits.h>
+#include "comms.h"
+#include "msgtypes.h"
+#include "daemon.h"
+#include "misc.h"
+
+handler(AccessRequest)
+{
+ TypedPayload<AccessRequest::Reply> * r = new TypedPayload<AccessRequest::Reply>();
+ r->data.value = access(req->data.path.c_str(), req->data.access);
+ s.Send(r);
+}
+handler(GetAttrRequest)
+{
+ TypedPayload<GetAttrRequest::Reply> * r = new TypedPayload<GetAttrRequest::Reply>();
+ errno = 0;
+ r->data.res = stat(req->data.path.c_str(), &r->data.val);
+ r->data.res = errno;
+ s.Send(r);
+}
+handler(FgetAttrRequest)
+{
+ TypedPayload<FgetAttrRequest::Reply> * r = new TypedPayload<FgetAttrRequest::Reply>();
+ errno = 0;
+ r->data.res = fstat(req->data.handle, &r->data.val);
+ r->data.res = errno;
+ s.Send(r);
+}
+handler(SymlinkRequest)
+{
+ TypedPayload<SymlinkRequest::Reply> * r = new TypedPayload<SymlinkRequest::Reply>();
+ errno = 0;
+ if (symlink(req->data.path1.c_str(), req->data.path2.c_str()) != 0) {
+ r->data.value = errno;
+ }
+ else {
+ r->data.value = 0;
+ }
+ s.Send(r);
+}
+handler(LinkRequest)
+{
+ TypedPayload<LinkRequest::Reply> * r = new TypedPayload<LinkRequest::Reply>();
+ errno = 0;
+ if (link(req->data.path1.c_str(), req->data.path2.c_str()) != 0) {
+ r->data.value = errno;
+ }
+ else {
+ r->data.value = 0;
+ }
+ s.Send(r);
+}
+handler(RenameRequest)
+{
+ TypedPayload<RenameRequest::Reply> * r = new TypedPayload<RenameRequest::Reply>();
+ errno = 0;
+ if (rename(req->data.path1.c_str(), req->data.path2.c_str()) != 0) {
+ r->data.value = errno;
+ }
+ else {
+ r->data.value = 0;
+ }
+ s.Send(r);
+}
+handler(ReadlinkRequest)
+{
+ TypedPayload<ReadlinkRequest::Reply> * r = new TypedPayload<ReadlinkRequest::Reply>();
+ errno = 0;
+ char buf[PATH_MAX];
+ ssize_t rc = readlink(req->data.path.c_str(), buf, PATH_MAX);
+ if (rc == -1) {
+ r->data.error = errno;
+ }
+ else {
+ buf[rc] = '\0';
+ r->data.path = buf;
+ r->data.error = 0;
+ }
+ s.Send(r);
+}
+handler(ChmodRequest)
+{
+ TypedPayload<ChmodRequest::Reply> * r = new TypedPayload<ChmodRequest::Reply>();
+ errno = 0;
+ if (chmod(req->data.path.c_str(), req->data.mode) != 0) {
+ r->data.value = errno;
+ }
+ else {
+ r->data.value = 0;
+ }
+ s.Send(r);
+}
+handler(ChownRequest)
+{
+ TypedPayload<ChownRequest::Reply> * r = new TypedPayload<ChownRequest::Reply>();
+ errno = 0;
+ if (chown(req->data.path.c_str(), req->data.user, req->data.group) != 0) {
+ r->data.value = errno;
+ }
+ else {
+ r->data.value = 0;
+ }
+ s.Send(r);
+}
+
diff --git a/netfs/fuse.cpp b/netfs/fuse.cpp
index 610605d..c540af1 100644
--- a/netfs/fuse.cpp
+++ b/netfs/fuse.cpp
@@ -1,12 +1,10 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
-#include "fuseapp.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
-#include "comms.h"
-#include "msgtypes.h"
+#include "fuse.h"
static FuseAppBase * fuseApp;
@@ -70,153 +68,55 @@ static void fuseDestroy(void * x)
delete (App*)x;
}
-class NetFS : public FuseAppBase
+NetFS::NetFS() :
+ f(NULL)
{
- int access(const char * p, int a)
- {
- TypedPayloadReq<AccessRequest>::Ptr msg = new TypedPayloadReq<AccessRequest>(fuse_get_context());
- msg->data.access = a;
- msg->data.path = p;
- return -msg->exchange(this)->data.value;
- }
- int getattr(const char * p, struct stat * s)
- {
- TypedPayloadReq<GetAttrRequest>::Ptr msg = new TypedPayloadReq<GetAttrRequest>(fuse_get_context());
- msg->data.path = p;
- TypedPayload<GetAttrReply>::Ptr rep = msg->exchange(this);
- *s = rep->data.val;
- return -rep->data.res;
- }
- int opendir(const char * p, struct fuse_file_info * fi)
- {
- TypedPayloadReq<OpenDirRequest>::Ptr msg = new TypedPayloadReq<OpenDirRequest>(fuse_get_context());
- msg->data.path = p;
- TypedPayload<OpenDirReply>::Ptr rep = msg->exchange(this);
- fi->fh = rep->data.handle;
- return -rep->data.error;
- }
- int releasedir(const char *, struct fuse_file_info * fi)
- {
- TypedPayloadReq<CloseDirRequest>::Ptr msg = new TypedPayloadReq<CloseDirRequest>(fuse_get_context());
- msg->data.handle = fi->fh;
- TypedPayload<CloseDirReply>::Ptr rep = msg->exchange(this);
- return -rep->data.error;
- }
- int readdir(const char *, void * buf, fuse_fill_dir_t filler, off_t, struct fuse_file_info * fi)
- {
- TypedPayloadReq<ReadDirRequest>::Ptr msg = new TypedPayloadReq<ReadDirRequest>(fuse_get_context());
- msg->data.handle = fi->fh;
- TypedPayload<ReadDirReply>::Ptr rep = msg->exchange(this);
- size_t cnt = rep->data.count;
- while (cnt--) {
- TypedPayload<ReadDirContent>::Ptr dir = recvType<ReadDirContent>();
- filler(buf, dir->data.path.c_str(), &dir->data.val, 0);
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ struct addrinfo *result, *rp;
+ int s = getaddrinfo("localhost", "4000", &hints, &result);
+ if (s) {
+ // Error
+ }
+ for (rp = result; rp != NULL; rp = rp->ai_next) {
+ int sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (sfd == -1)
+ // Error
+ continue;
+ if (connect(sfd, rp->ai_addr, rp->ai_addrlen)) {
+ // Error
+ continue;
}
- return -rep->data.error;
- }
- int open(const char * p, struct fuse_file_info * fi)
- {
- TypedPayloadReq<OpenRequest>::Ptr msg = new TypedPayloadReq<OpenRequest>(fuse_get_context());
- msg->data.path = p;
- TypedPayload<OpenReply>::Ptr rep = msg->exchange(this);
- fi->fh = rep->data.handle;
- return -rep->data.error;
- }
- int release(const char *, struct fuse_file_info * fi)
- {
- TypedPayloadReq<CloseRequest>::Ptr msg = new TypedPayloadReq<CloseRequest>(fuse_get_context());
- msg->data.handle = fi->fh;
- TypedPayload<CloseReply>::Ptr rep = msg->exchange(this);
- return -rep->data.error;
+ f = fdopen(sfd, "a+");
+ break;
}
- int read(const char *, char * buf, size_t s, off_t o, struct fuse_file_info * fi)
- {
- TypedPayloadReq<ReadRequest>::Ptr msg = new TypedPayloadReq<ReadRequest>(fuse_get_context());
- msg->data.handle = fi->fh;
- msg->data.offset = o;
- msg->data.size = s;
- TypedPayload<ReadReply>::Ptr rep = msg->exchange(this);
- if (rep->data.size) {
- memcpy(buf, rep->data.data, std::min(s, rep->data.size));
- return rep->data.size;
- }
- else {
- return -rep->data.error;
- }
- }
- int write(const char *, const char * buf, size_t s, off_t o, struct fuse_file_info * fi)
- {
- TypedPayloadReq<WriteRequest>::Ptr msg = new TypedPayloadReq<WriteRequest>(fuse_get_context());
- msg->data.handle = fi->fh;
- msg->data.offset = o;
- msg->data.size = s;
- msg->data.data = new char[s]();
- memcpy(msg->data.data, buf, s);
- TypedPayload<WriteReply>::Ptr rep = msg->exchange(this);
- return -rep->data.error;
+}
+NetFS::~NetFS()
+{
+ if (f) {
+ fclose(f);
}
- public:
- NetFS() :
- f(NULL)
- {
- struct addrinfo hints;
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_PASSIVE;
- struct addrinfo *result, *rp;
- int s = getaddrinfo("localhost", "4000", &hints, &result);
- if (s) {
- // Error
- }
- for (rp = result; rp != NULL; rp = rp->ai_next) {
- int sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
- if (sfd == -1)
- // Error
- continue;
- if (connect(sfd, rp->ai_addr, rp->ai_addrlen)) {
- // Error
- continue;
- }
- f = fdopen(sfd, "a+");
- break;
- }
- }
- ~NetFS()
- {
- if (f) {
- fclose(f);
- }
- }
- DataPayloadPtr exchange(DataPayloadPtr dp)
- {
- send(dp);
- return recv();
- }
- void send(DataPayloadPtr dp)
- {
- PacketPtr sendme = new Packet(0, dp);
- sendme->write(f);
- }
- DataPayloadPtr recv()
- {
- PacketPtr recvd = new Packet(f);
- return recvd->data;
- }
- template <typename Type>
- SmartPointer<TypedPayload<Type> > recvType()
- {
- DataPayloadPtr p = this->recv();
- return p.as<TypedPayload<Type> >();
- }
- private:
- FILE * f;
-};
-template <typename Type>
-SmartPointer<TypedPayload<typename Type::Reply> > TypedPayloadReq<Type>::exchange(NetFS * net)
+}
+DataPayloadPtr
+NetFS::exchange(DataPayloadPtr dp)
+{
+ send(dp);
+ return recv();
+}
+void
+NetFS::send(DataPayloadPtr dp)
+{
+ PacketPtr sendme = new Packet(0, dp);
+ sendme->write(f);
+}
+DataPayloadPtr
+NetFS::recv()
{
- DataPayloadPtr p = net->exchange(this);
- return p.as<TypedPayload<typename Type::Reply> >();
+ PacketPtr recvd = new Packet(f);
+ return recvd->data;
}
template <class FuseApp>
diff --git a/netfs/fuse.h b/netfs/fuse.h
new file mode 100644
index 0000000..dc0a124
--- /dev/null
+++ b/netfs/fuse.h
@@ -0,0 +1,60 @@
+#ifndef FUSE_H
+#define FUSE_H
+
+#include "fuseapp.h"
+#include "comms.h"
+
+class NetFS : public FuseAppBase
+{
+ // misc
+ int access(const char * p, int a);
+ int getattr(const char * p, struct stat * s);
+ int fgetattr(const char *, struct stat *, struct fuse_file_info *);
+ int chmod(const char *, mode_t);
+ int chown(const char *, uid_t, gid_t);
+ int link(const char *, const char *);
+ int readlink(const char *, char *, size_t);
+ int rename(const char *, const char *);
+ int symlink(const char *, const char *);
+ int unlink(const char *);
+ // dirs
+ int opendir(const char * p, struct fuse_file_info * fi);
+ int releasedir(const char *, struct fuse_file_info * fi);
+ int readdir(const char *, void * buf, fuse_fill_dir_t filler, off_t, struct fuse_file_info * fi);
+ int mkdir(const char *, mode_t);
+ int rmdir(const char *);
+ // files
+ int open(const char * p, struct fuse_file_info * fi);
+ int create(const char *, mode_t, struct fuse_file_info *);
+ int release(const char *, struct fuse_file_info * fi);
+ int read(const char *, char * buf, size_t s, off_t o, struct fuse_file_info * fi);
+ int write(const char *, const char * buf, size_t s, off_t o, struct fuse_file_info * fi);
+ int truncate(const char *, off_t);
+ int ftruncate(const char *, off_t, struct fuse_file_info *);
+ // fs
+ int statfs(const char *, struct statvfs *);
+ // stuff
+ public:
+ NetFS();
+ ~NetFS();
+ DataPayloadPtr exchange(DataPayloadPtr dp);
+ void send(DataPayloadPtr dp);
+ DataPayloadPtr recv();
+ template <typename Type>
+ SmartPointer<TypedPayload<Type> > recvType()
+ {
+ DataPayloadPtr p = this->recv();
+ return p.as<TypedPayload<Type> >();
+ }
+ private:
+ FILE * f;
+};
+
+// this needs to go here to avoid circular includes regarding the net->exchange call
+template<class X>
+SmartPointer<TypedPayload<typename X::Reply> > TypedPayloadReq<X>::exchange(NetFS * net)
+{
+ DataPayloadPtr p = net->exchange(this);
+ return p.as<TypedPayload<typename X::Reply> >();
+}
+#endif
diff --git a/netfs/fuseDirs.cpp b/netfs/fuseDirs.cpp
new file mode 100644
index 0000000..17e4a3e
--- /dev/null
+++ b/netfs/fuseDirs.cpp
@@ -0,0 +1,48 @@
+#include "fuse.h"
+#include "msgtypes.h"
+
+int
+NetFS::opendir(const char * p, struct fuse_file_info * fi)
+{
+ TypedPayloadReq<OpenDirRequest>::Ptr msg = new TypedPayloadReq<OpenDirRequest>(fuse_get_context());
+ msg->data.path = p;
+ TypedPayload<OpenDirReply>::Ptr rep = msg->exchange(this);
+ fi->fh = rep->data.handle;
+ return -rep->data.error;
+}
+int
+NetFS::releasedir(const char *, struct fuse_file_info * fi)
+{
+ TypedPayloadReq<CloseDirRequest>::Ptr msg = new TypedPayloadReq<CloseDirRequest>(fuse_get_context());
+ msg->data.handle = fi->fh;
+ TypedPayload<CloseDirReply>::Ptr rep = msg->exchange(this);
+ return -rep->data.error;
+}
+int
+NetFS::readdir(const char *, void * buf, fuse_fill_dir_t filler, off_t, struct fuse_file_info * fi)
+{
+ TypedPayloadReq<ReadDirRequest>::Ptr msg = new TypedPayloadReq<ReadDirRequest>(fuse_get_context());
+ msg->data.handle = fi->fh;
+ TypedPayload<ReadDirReply>::Ptr rep = msg->exchange(this);
+ size_t cnt = rep->data.count;
+ while (cnt--) {
+ TypedPayload<ReadDirContent>::Ptr dir = recvType<ReadDirContent>();
+ filler(buf, dir->data.path.c_str(), &dir->data.val, 0);
+ }
+ return -rep->data.error;
+}
+int
+NetFS::mkdir(const char * d, mode_t m)
+{
+ TypedPayloadReq<MkDirRequest>::Ptr msg = new TypedPayloadReq<MkDirRequest>(fuse_get_context());
+ msg->data.path = d;
+ msg->data.mode = m;
+ return -msg->exchange(this)->data.value;
+}
+int
+NetFS::rmdir(const char * d)
+{
+ TypedPayloadReq<RmDirRequest>::Ptr msg = new TypedPayloadReq<RmDirRequest>(fuse_get_context());
+ msg->data.path = d;
+ return -msg->exchange(this)->data.value;
+}
diff --git a/netfs/fuseFS.cpp b/netfs/fuseFS.cpp
new file mode 100644
index 0000000..ab27044
--- /dev/null
+++ b/netfs/fuseFS.cpp
@@ -0,0 +1,13 @@
+#include "fuse.h"
+#include "msgtypes.h"
+
+int
+NetFS::statfs(const char * p, struct statvfs * vfs)
+{
+ TypedPayloadReq<StatfsRequest>::Ptr msg = new TypedPayloadReq<StatfsRequest>(fuse_get_context());
+ msg->data.path = p;
+ TypedPayload<StatfsReply>::Ptr rep = msg->exchange(this);
+ *vfs = rep->data.statfs;
+ return -rep->data.error;
+}
+
diff --git a/netfs/fuseFiles.cpp b/netfs/fuseFiles.cpp
new file mode 100644
index 0000000..42b3012
--- /dev/null
+++ b/netfs/fuseFiles.cpp
@@ -0,0 +1,75 @@
+#include <string.h>
+#include "fuse.h"
+#include "msgtypes.h"
+
+int
+NetFS::open(const char * p, struct fuse_file_info * fi)
+{
+ TypedPayloadReq<OpenRequest>::Ptr msg = new TypedPayloadReq<OpenRequest>(fuse_get_context());
+ msg->data.path = p;
+ TypedPayload<OpenReply>::Ptr rep = msg->exchange(this);
+ fi->fh = rep->data.handle;
+ return -rep->data.error;
+}
+int
+NetFS::create(const char * p, mode_t m, struct fuse_file_info * fi)
+{
+ TypedPayloadReq<CreateRequest>::Ptr msg = new TypedPayloadReq<CreateRequest>(fuse_get_context());
+ msg->data.path = p;
+ msg->data.mode = m;
+ TypedPayload<OpenReply>::Ptr rep = msg->exchange(this);
+ fi->fh = rep->data.handle;
+ return -rep->data.error;
+}
+int
+NetFS::release(const char *, struct fuse_file_info * fi)
+{
+ TypedPayloadReq<CloseRequest>::Ptr msg = new TypedPayloadReq<CloseRequest>(fuse_get_context());
+ msg->data.handle = fi->fh;
+ TypedPayload<CloseReply>::Ptr rep = msg->exchange(this);
+ return -rep->data.error;
+}
+int
+NetFS::read(const char *, char * buf, size_t s, off_t o, struct fuse_file_info * fi)
+{
+ TypedPayloadReq<ReadRequest>::Ptr msg = new TypedPayloadReq<ReadRequest>(fuse_get_context());
+ msg->data.handle = fi->fh;
+ msg->data.offset = o;
+ msg->data.size = s;
+ TypedPayload<ReadReply>::Ptr rep = msg->exchange(this);
+ if (rep->data.size) {
+ memcpy(buf, rep->data.data, std::min(s, rep->data.size));
+ return rep->data.size;
+ }
+ else {
+ return -rep->data.error;
+ }
+}
+int
+NetFS::write(const char *, const char * buf, size_t s, off_t o, struct fuse_file_info * fi)
+{
+ TypedPayloadReq<WriteRequest>::Ptr msg = new TypedPayloadReq<WriteRequest>(fuse_get_context());
+ msg->data.handle = fi->fh;
+ msg->data.offset = o;
+ msg->data.size = s;
+ msg->data.data = new char[s]();
+ memcpy(msg->data.data, buf, s);
+ TypedPayload<WriteReply>::Ptr rep = msg->exchange(this);
+ return -rep->data.error;
+}
+int
+NetFS::truncate(const char * p, off_t o)
+{
+ TypedPayloadReq<TruncateRequest>::Ptr msg = new TypedPayloadReq<TruncateRequest>(fuse_get_context());
+ msg->data.path = p;
+ msg->data.size = o;
+ return -msg->exchange(this)->data.value;
+}
+int
+NetFS::ftruncate(const char *, off_t o, fuse_file_info * fi)
+{
+ TypedPayloadReq<FtruncateRequest>::Ptr msg = new TypedPayloadReq<FtruncateRequest>(fuse_get_context());
+ msg->data.handle = fi->fh;
+ msg->data.size = o;
+ return -msg->exchange(this)->data.value;
+}
diff --git a/netfs/fuseMisc.cpp b/netfs/fuseMisc.cpp
new file mode 100644
index 0000000..2746916
--- /dev/null
+++ b/netfs/fuseMisc.cpp
@@ -0,0 +1,87 @@
+#include "fuse.h"
+#include "msgtypes.h"
+#include <string.h>
+
+int
+NetFS::access(const char * p, int a)
+{
+ TypedPayloadReq<AccessRequest>::Ptr msg = new TypedPayloadReq<AccessRequest>(fuse_get_context());
+ msg->data.access = a;
+ msg->data.path = p;
+ return -msg->exchange(this)->data.value;
+}
+int
+NetFS::getattr(const char * p, struct stat * s)
+{
+ TypedPayloadReq<GetAttrRequest>::Ptr msg = new TypedPayloadReq<GetAttrRequest>(fuse_get_context());
+ msg->data.path = p;
+ TypedPayload<GetAttrReply>::Ptr rep = msg->exchange(this);
+ *s = rep->data.val;
+ return -rep->data.res;
+}
+int
+NetFS::fgetattr(const char *, struct stat * s, fuse_file_info * fi)
+{
+ TypedPayloadReq<FgetAttrRequest>::Ptr msg = new TypedPayloadReq<FgetAttrRequest>(fuse_get_context());
+ msg->data.handle = fi->fh;
+ TypedPayload<GetAttrReply>::Ptr rep = msg->exchange(this);
+ *s = rep->data.val;
+ return -rep->data.res;
+}
+int
+NetFS::chmod(const char * p, mode_t m)
+{
+ TypedPayloadReq<ChmodRequest>::Ptr msg = new TypedPayloadReq<ChmodRequest>(fuse_get_context());
+ msg->data.path = p;
+ msg->data.mode = m;
+ return msg->exchange(this)->data.value;
+}
+int
+NetFS::chown(const char * p, uid_t u, gid_t g)
+{
+ TypedPayloadReq<ChownRequest>::Ptr msg = new TypedPayloadReq<ChownRequest>(fuse_get_context());
+ msg->data.path = p;
+ msg->data.user = u;
+ msg->data.group = g;
+ return msg->exchange(this)->data.value;
+}
+int
+NetFS::link(const char * p1, const char * p2)
+{
+ TypedPayloadReq<LinkRequest>::Ptr msg = new TypedPayloadReq<LinkRequest>(fuse_get_context());
+ msg->data.path1 = p1;
+ msg->data.path2 = p2;
+ return msg->exchange(this)->data.value;
+}
+int
+NetFS::symlink(const char * p1, const char * p2)
+{
+ TypedPayloadReq<SymlinkRequest>::Ptr msg = new TypedPayloadReq<SymlinkRequest>(fuse_get_context());
+ msg->data.path1 = p1;
+ msg->data.path2 = p2;
+ return msg->exchange(this)->data.value;
+}
+int
+NetFS::readlink(const char * p, char * p2, size_t s)
+{
+ TypedPayloadReq<ReadlinkRequest>::Ptr msg = new TypedPayloadReq<ReadlinkRequest>(fuse_get_context());
+ msg->data.path = p;
+ TypedPayload<ReadlinkReply>::Ptr rep = msg->exchange(this);
+ strncpy(p2, rep->data.path.c_str(), std::min(rep->data.path.length(), s));
+ return -rep->data.error;
+}
+int
+NetFS::rename(const char * p1, const char * p2)
+{
+ TypedPayloadReq<RenameRequest>::Ptr msg = new TypedPayloadReq<RenameRequest>(fuse_get_context());
+ msg->data.path1 = p1;
+ msg->data.path2 = p2;
+ return msg->exchange(this)->data.value;
+}
+int
+NetFS::unlink(const char * p)
+{
+ TypedPayloadReq<UnlinkRequest>::Ptr msg = new TypedPayloadReq<UnlinkRequest>(fuse_get_context());
+ msg->data.path = p;
+ return msg->exchange(this)->data.value;
+}
diff --git a/netfs/makefile.in b/netfs/makefile.in
index f7901ba..4086315 100644
--- a/netfs/makefile.in
+++ b/netfs/makefile.in
@@ -30,11 +30,19 @@ NETFSOBJS= \
comms.o \
fuse.o \
fuseapp.o \
+ fuseFiles.o \
+ fuseDirs.o \
+ fuseMisc.o \
+ fuseFS.o \
msgtypes.o \
NETFSDOBJS= \
comms.o \
daemon.o \
+ daemonDirs.o \
+ daemonFiles.o \
+ daemonMisc.o \
+ daemonFS.o \
daemonConfig.o \
msgtypes.o \
diff --git a/netfs/msgtypes.cpp b/netfs/msgtypes.cpp
index b2d449e..9ac6b04 100644
--- a/netfs/msgtypes.cpp
+++ b/netfs/msgtypes.cpp
@@ -14,7 +14,8 @@ template<> void operator<<(FILE & f, const std::string & t)
{
std::string::size_type len = t.length();
f << len;
- if (fwrite(t.data(), len, 1, &f) < len) {
+ if (fwrite(t.data(), len, 1, &f) != 1) {
+ throw WriteMessageFailed();
}
}
template<> void operator>>(FILE & f, std::string & t)
@@ -24,17 +25,23 @@ template<> void operator>>(FILE & f, std::string & t)
t.clear();
t.reserve(len);
while (len --) {
- t.push_back(fgetc(&f));
+ int c = fgetc(&f);
+ if (c == EOF) {
+ throw ReadMessageFailed();
+ }
+ t.push_back(c);
}
}
template<class Type> void operator<<(FILE & f, const Type & t)
{
- if (fwrite(&t, sizeof(t), 1, &f) < sizeof(t)) {
+ if (fwrite(&t, sizeof(t), 1, &f) != 1) {
+ throw WriteMessageFailed();
}
}
template<class Type> void operator>>(FILE & f, Type & t)
{
- if (fread(&t, sizeof(t), 1, &f) < sizeof(t)) {
+ if (fread(&t, sizeof(t), 1, &f) != 1) {
+ throw ReadMessageFailed();
}
}
@@ -90,6 +97,9 @@ Xfer2(29, TruncateRequest, RequestBase, path, size)
Xfer2(30, FtruncateRequest, RequestBase, handle, size)
Xfer1(31, StatfsRequest, RequestBase, path)
Xfer2(32, StatfsReply, ContentBase, statfs, error)
+Xfer1(33, FgetAttrRequest, RequestBase, handle)
+Xfer2(34, CreateRequest, RequestBase, path, mode)
+Xfer1(35, RmDirRequest, RequestBase, path)
MSGTYPE(2, ReadReply)
ReadReply::~ReadReply()
@@ -105,7 +115,8 @@ ReadReply::Send(FILE*f) const
*f << error;
*f << size;
if (size) {
- if (fwrite(data, size, 1, f) < size) {
+ if (fwrite(data, size, 1, f) != 1) {
+ throw WriteMessageFailed();
}
}
}
@@ -117,7 +128,8 @@ ReadReply::Read(FILE*f)
*f >> size;
if (size) {
data = new char[size]();
- if (fread(data, size, 1, f) < size) {
+ if (fread(data, size, 1, f) != 1) {
+ throw ReadMessageFailed();
}
}
else {
@@ -139,7 +151,8 @@ WriteRequest::Send(FILE*f) const
*f << handle;
*f << size;
*f << offset;
- if (fwrite(data, size, 1, f) < size) {
+ if (fwrite(data, size, 1, f) != 1) {
+ throw WriteMessageFailed();
}
}
void
@@ -151,7 +164,8 @@ WriteRequest::Read(FILE*f)
*f >> offset;
if (size) {
data = new char[size]();
- if (fread(data, size, 1, f) < size) {
+ if (fread(data, size, 1, f) != 1) {
+ throw ReadMessageFailed();
}
}
else {
diff --git a/netfs/msgtypes.h b/netfs/msgtypes.h
index 78986d3..71909cc 100644
--- a/netfs/msgtypes.h
+++ b/netfs/msgtypes.h
@@ -224,6 +224,15 @@ class WriteRequest : public RequestBase {
class MkDirRequest : public RequestBase {
public:
std::string path;
+ mode_t mode;
+ typedef SimpleInt Reply;
+ const static uint16_t TypeID;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+class RmDirRequest : public RequestBase {
+ public:
+ std::string path;
typedef SimpleInt Reply;
const static uint16_t TypeID;
virtual void Send(FILE*) const;
@@ -267,6 +276,7 @@ class RenameRequest : public RequestBase {
class ReadlinkReply : public ContentBase {
public:
std::string path;
+ int error;
const static uint16_t TypeID;
virtual void Send(FILE*) const;
virtual void Read(FILE*);