summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--netfs/comms.cpp90
-rw-r--r--netfs/comms.h60
-rwxr-xr-xnetfs/configure1
-rw-r--r--netfs/daemon.cpp223
-rw-r--r--netfs/daemon.xml10
-rw-r--r--netfs/daemonConfig.cpp27
-rw-r--r--netfs/daemonConfig.h31
-rw-r--r--netfs/fuse.cpp283
-rw-r--r--netfs/fuseapp.cpp154
-rw-r--r--netfs/fuseapp.h50
-rw-r--r--netfs/fusecallers.h4
-rw-r--r--netfs/makefile.in63
-rw-r--r--netfs/msgtypes.cpp152
-rw-r--r--netfs/msgtypes.h212
-rw-r--r--netfs/xfers.h53
15 files changed, 1413 insertions, 0 deletions
diff --git a/netfs/comms.cpp b/netfs/comms.cpp
new file mode 100644
index 0000000..86a7313
--- /dev/null
+++ b/netfs/comms.cpp
@@ -0,0 +1,90 @@
+#include <queue>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "threads.h"
+#include "comms.h"
+#include "msgtypes.h"
+
+Semaphore outQueueSl(0);
+Semaphore outQueueSu(30);
+Semaphore inQueueS(0);
+Mutex outQueueM;
+Mutex inQueueM;
+std::queue<PacketPtr> outQueue;
+std::queue<PacketPtr> inQueue;
+
+Packet::Packet(uint8_t i, DataPayloadPtr p) :
+ index(i),
+ data(p)
+{
+}
+void
+sendPacket(PacketPtr p)
+{
+ outQueueSu.wait();
+ outQueueSl.post();
+ AutoMutex am(outQueueM);
+ outQueue.push(p);
+}
+
+void
+packetSender(FILE * host)
+{
+ while (1) {
+ outQueueSl.wait();
+ AutoMutex am(outQueueM);
+ // We can indicate an exit request by posting the semaphore without putting
+ // anything in the queue
+ if (outQueue.size() == 0) {
+ break;
+ }
+ outQueue.front()->write(host);
+ outQueueSu.post();
+ }
+}
+
+Packet::Packet(FILE * host)
+{
+ if (fread(&index, sizeof(index), 1, host) != 1) {
+ // error
+ }
+ DataPayload::TypeID t = 0;
+ if (fread(&t, sizeof(t), 1, host) != 1) {
+ // error
+ }
+ data = MsgFacs()[t]();
+ data->Read(host);
+}
+void
+Packet::write(FILE * host) const
+{
+ if (fwrite(&index, sizeof(index), 1, host) != 1) {
+ // error
+ }
+ DataPayload::TypeID t = data->dataTypeID();
+ if (fwrite(&t, sizeof(t), 1, host) != 1) {
+ // error
+ }
+ data->Send(host);
+ if (fflush(host) != 0) {
+ //
+ }
+}
+
+PacketPtr
+recvPacket()
+{
+ inQueueS.wait();
+ AutoMutex am(inQueueM);
+ PacketPtr rtn = inQueue.front();
+ inQueue.pop();
+ return rtn;
+}
+
+ContentBase::~ContentBase()
+{
+}
+
diff --git a/netfs/comms.h b/netfs/comms.h
new file mode 100644
index 0000000..54681b5
--- /dev/null
+++ b/netfs/comms.h
@@ -0,0 +1,60 @@
+#ifndef COMMS_H
+#define COMMS_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <fuse/fuse.h>
+#include "smartpointer.h"
+
+class DataPayload : public IsRefCounted {
+ public:
+ typedef uint16_t TypeID;
+
+ virtual TypeID dataTypeID() const = 0;
+ virtual void Send(FILE *) const = 0;
+ virtual void Read(FILE *) = 0;
+};
+class NetFS;
+template <class Type>
+class TypedPayload : public DataPayload {
+ public:
+ typedef SmartPointer<TypedPayload<Type> > Ptr;
+ Type data;
+ TypeID dataTypeID() const { return Type::TypeID; }
+ void Send(FILE * h) const { data.Send(h); }
+ void Read(FILE * h) { data.Read(h); }
+};
+template <class Type>
+class TypedPayloadReq : public TypedPayload<Type> {
+ public:
+ typedef typename Type::Reply Reply;
+ typedef SmartPointer<TypedPayloadReq<Type> > Ptr;
+ typedef SmartPointer<TypedPayload<Reply> > ReplyPtr;
+ TypedPayloadReq(fuse_context * fc)
+ {
+ TypedPayload<Type>::data.uid = fc->uid;
+ TypedPayload<Type>::data.gid = fc->gid;
+ }
+ ReplyPtr exchange(NetFS *);
+};
+
+typedef SmartPointer<DataPayload> DataPayloadPtr;
+typedef SmartPointer<const DataPayload> DataPayloadCPtr;
+
+class Packet : public IsRefCounted {
+ public:
+ Packet(FILE *);
+ Packet(uint8_t i, DataPayloadPtr p);
+
+ void write(FILE *) const;
+
+ uint8_t index;
+ DataPayloadPtr data;
+};
+typedef SmartPointer<Packet> PacketPtr;
+
+void
+sendPacket(PacketPtr p);
+
+#endif
+
diff --git a/netfs/configure b/netfs/configure
new file mode 100755
index 0000000..4d06840
--- /dev/null
+++ b/netfs/configure
@@ -0,0 +1 @@
+cp makefile.in makefile
diff --git a/netfs/daemon.cpp b/netfs/daemon.cpp
new file mode 100644
index 0000000..1cbb5bc
--- /dev/null
+++ b/netfs/daemon.cpp
@@ -0,0 +1,223 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <set>
+#include "daemonConfig.h"
+#include "comms.h"
+#include "msgtypes.h"
+#include "misc.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)
+{
+ 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(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);
+}
+#define TRYCLASS(cls) \
+ if (type == cls::TypeID) { handle(p->data.as<TypedPayload<cls> >(), s); }
+
+void
+runDaemonOn(FILE * f, DaemonConfigPtr, DaemonGlobalStatePtr)
+{
+ while (true) {
+ PacketPtr p = new Packet(f);
+ Sender s(p->index, f);
+ uint16_t type (p->data->dataTypeID());
+ TRYCLASS(AccessRequest);
+ TRYCLASS(GetAttrRequest);
+ TRYCLASS(OpenDirRequest);
+ TRYCLASS(CloseDirRequest);
+ TRYCLASS(ReadDirRequest);
+ TRYCLASS(OpenRequest);
+ TRYCLASS(CloseRequest);
+ TRYCLASS(ReadRequest);
+ TRYCLASS(WriteRequest);
+ }
+ fclose(f);
+}
+
+int main(int, char* [])
+{
+ DaemonConfigPtr dc = DaemonConfig::Load("daemon.xml");
+ 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(NULL, dc->tcpPort.c_str(), &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)
+ continue;
+ if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) {
+ if (listen(sfd, 5)) {
+ // Error
+ }
+ DaemonGlobalStatePtr dgs = new DaemonGlobalState();
+ for (int newfd; (newfd = accept(sfd, NULL, NULL)) > 0; ) {
+ runDaemonOn(fdopen(newfd, "a+"), dc, dgs);
+ }
+ break;
+ }
+ close(sfd);
+ }
+ return 0;
+}
+
diff --git a/netfs/daemon.xml b/netfs/daemon.xml
new file mode 100644
index 0000000..2454ff6
--- /dev/null
+++ b/netfs/daemon.xml
@@ -0,0 +1,10 @@
+<config>
+ <tcpPort>4000</tcpPort>
+ <exports>
+ <export>
+ <name>tmp</name>
+ <root>/tmp</root>
+ </export>
+ </exports>
+</config>
+
diff --git a/netfs/daemonConfig.cpp b/netfs/daemonConfig.cpp
new file mode 100644
index 0000000..520cec1
--- /dev/null
+++ b/netfs/daemonConfig.cpp
@@ -0,0 +1,27 @@
+#include "daemonConfig.h"
+#include <string.h>
+
+DaemonConfigPtr
+DaemonConfig::Load(const char * path)
+{
+ xmlDoc * doc = xmlReadFile(path, NULL, 0);
+ DaemonConfigPtr dc = new DaemonConfig(doc->children);
+ xmlFreeDoc(doc);
+ return dc;
+}
+
+DaemonConfig::DaemonConfig(xmlNodePtr conf) :
+ tcpPort(xmlGetNodeValue(conf, "tcpPort"))
+{
+ foreachxml(exp, xmlGetNode(conf, "exports"), "export") {
+ ExportPtr e = new Export(exp);
+ exports[e->name] = e;
+ }
+}
+
+DaemonConfig::Export::Export(xmlNodePtr exp) :
+ root(xmlGetNodeValue(exp, "root")),
+ name(xmlGetNodeValue(exp, "name"))
+{
+}
+
diff --git a/netfs/daemonConfig.h b/netfs/daemonConfig.h
new file mode 100644
index 0000000..d15fade
--- /dev/null
+++ b/netfs/daemonConfig.h
@@ -0,0 +1,31 @@
+#ifndef DAEMONCONFIG_H
+#define DAEMONCONFIG_H
+
+#include <string>
+#include <map>
+#include "smartpointer.h"
+#include "xml.h"
+
+class DaemonConfig : public virtual IsRefCounted {
+ public:
+ class Export : public virtual IsRefCounted {
+ public:
+ Export(xmlNodePtr);
+
+ std::string root;
+ std::string name;
+ };
+ typedef SmartPointer<Export> ExportPtr;
+ typedef std::map<std::string, ExportPtr> ExportMap;
+
+ DaemonConfig(xmlNodePtr);
+ static SmartPointer<DaemonConfig> Load(const char * path);
+
+
+ std::string tcpPort;
+ ExportMap exports;
+};
+typedef SmartPointer<DaemonConfig> DaemonConfigPtr;
+
+#endif
+
diff --git a/netfs/fuse.cpp b/netfs/fuse.cpp
new file mode 100644
index 0000000..610605d
--- /dev/null
+++ b/netfs/fuse.cpp
@@ -0,0 +1,283 @@
+#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"
+
+static FuseAppBase * fuseApp;
+
+template <class A, int (FuseAppBase::*f)(A)>
+static int fuseCall(A a)
+{
+ try {
+ return (fuseApp->*f)(a);
+ }
+ catch (...) {
+ return -ENOSYS;
+ }
+}
+template <class A, class B, int (FuseAppBase::*f)(A, B)>
+static int fuseCall(A a, B b)
+{
+ try {
+ return (fuseApp->*f)(a, b);
+ }
+ catch (...) {
+ return -ENOSYS;
+ }
+}
+template <class A, class B, class C, int (FuseAppBase::*f)(A, B, C)>
+static int fuseCall(A a, B b, C c)
+{
+ try {
+ return (fuseApp->*f)(a, b, c);
+ }
+ catch (...) {
+ return -ENOSYS;
+ }
+}
+template <class A, class B, class C, class D, int (FuseAppBase::*f)(A, B, C, D)>
+static int fuseCall(A a, B b, C c, D d)
+{
+ try {
+ return (fuseApp->*f)(a, b, c, d);
+ }
+ catch (...) {
+ return -ENOSYS;
+ }
+}
+template <class A, class B, class C, class D, class E, int (FuseAppBase::*f)(A, B, C, D, E)>
+static int fuseCall(A a, B b, C c, D d, E e)
+{
+ try {
+ return (fuseApp->*f)(a, b, c, d, e);
+ }
+ catch (...) {
+ return -ENOSYS;
+ }
+}
+static void * fuseInit (struct fuse_conn_info *conn)
+{
+ return fuseApp->init(conn);
+}
+template <class App>
+static void fuseDestroy(void * x)
+{
+ delete (App*)x;
+}
+
+class NetFS : public FuseAppBase
+{
+ 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);
+ }
+ 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;
+ }
+ 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;
+ }
+ 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 p = net->exchange(this);
+ return p.as<TypedPayload<typename Type::Reply> >();
+}
+
+template <class FuseApp>
+int runFuseApp(int argc, char* argv[])
+{
+ struct fuse_opt fuse_opts[] = {
+ { NULL, 0, 0 }
+ };
+
+ fuseApp = new FuseApp();
+ struct fuse_operations operations = {
+ fuseCall<const char *, struct stat *, &FuseAppBase::getattr>,
+ fuseCall<const char *, char *, size_t, &FuseAppBase::readlink>,
+ NULL, // getdir deprecated
+ fuseCall<const char *, mode_t, dev_t, &FuseAppBase::mknod>,
+ fuseCall<const char *, mode_t, &FuseAppBase::mkdir>,
+ fuseCall<const char *, &FuseAppBase::unlink>,
+ fuseCall<const char *, &FuseAppBase::rmdir>,
+ fuseCall<const char *, const char *, &FuseAppBase::symlink>,
+ fuseCall<const char *, const char *, &FuseAppBase::rename>,
+ fuseCall<const char *, const char *, &FuseAppBase::link>,
+ fuseCall<const char *, mode_t, &FuseAppBase::chmod>,
+ fuseCall<const char *, uid_t, gid_t, &FuseAppBase::chown>,
+ fuseCall<const char *, off_t, &FuseAppBase::truncate>,
+ NULL, // utime deprecated
+ fuseCall<const char *, struct fuse_file_info *, &FuseAppBase::open>,
+ fuseCall<const char *, char *, size_t, off_t, struct fuse_file_info *, &FuseAppBase::read>,
+ fuseCall<const char *, const char *, size_t, off_t, struct fuse_file_info *, &FuseAppBase::write>,
+ fuseCall<const char *, struct statvfs *, &FuseAppBase::statfs>,
+ fuseCall<const char *, struct fuse_file_info *, &FuseAppBase::flush>,
+ fuseCall<const char *, struct fuse_file_info *, &FuseAppBase::release>,
+ fuseCall<const char *, int, struct fuse_file_info *, &FuseAppBase::fsync>,
+ fuseCall<const char *, const char *, const char *, size_t, int, &FuseAppBase::setxattr>,
+ fuseCall<const char *, const char *, char *, size_t, &FuseAppBase::getxattr>,
+ fuseCall<const char *, char *, size_t, &FuseAppBase::listxattr>,
+ fuseCall<const char *, const char *, &FuseAppBase::removexattr>,
+ fuseCall<const char *, struct fuse_file_info *, &FuseAppBase::opendir>,
+ fuseCall<const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *, &FuseAppBase::readdir>,
+ fuseCall<const char *, struct fuse_file_info *, &FuseAppBase::releasedir>,
+ fuseCall<const char *, int, struct fuse_file_info *, &FuseAppBase::fsyncdir>,
+ fuseInit,
+ fuseDestroy<FuseApp>,
+ fuseCall<const char *, int, &FuseAppBase::access>,
+ fuseCall<const char *, mode_t, struct fuse_file_info *, &FuseAppBase::create>,
+ fuseCall<const char *, off_t, struct fuse_file_info *, &FuseAppBase::ftruncate>,
+ fuseCall<const char *, struct stat *, struct fuse_file_info *, &FuseAppBase::fgetattr>,
+ fuseCall<const char *, struct fuse_file_info *, int, struct flock *, &FuseAppBase::lock>,
+ fuseCall<const char *, const struct timespec [2], &FuseAppBase::utimens>,
+ fuseCall<const char *, size_t, uint64_t *, &FuseAppBase::bmap>
+ };
+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+ if (fuse_opt_parse(&args, fuseApp, fuse_opts,
+ fuseCall<void *, const char *, int, struct fuse_args *, &FuseAppBase::opt_parse>) == -1) {
+ exit(1);
+ }
+ return fuse_main(args.argc, args.argv, &operations, NULL);
+}
+
+int
+main(int argc, char* argv[])
+{
+ return runFuseApp<NetFS>(argc, argv);
+}
+
diff --git a/netfs/fuseapp.cpp b/netfs/fuseapp.cpp
new file mode 100644
index 0000000..b46d3ad
--- /dev/null
+++ b/netfs/fuseapp.cpp
@@ -0,0 +1,154 @@
+#include "fuseapp.h"
+#include <errno.h>
+
+FuseAppBase::FuseAppBase()
+{
+}
+FuseAppBase::~FuseAppBase()
+{
+}
+void * FuseAppBase::init(fuse_conn_info*)
+{
+ return NULL;
+}
+int FuseAppBase::opt_parse(void*, const char *, int, fuse_args*)
+{
+ return 1;
+}
+int FuseAppBase::access(const char *, int)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::chmod(const char *, mode_t)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::chown(const char *, uid_t, gid_t)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::create(const char *, mode_t, struct fuse_file_info *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::fgetattr(const char *, struct stat *, struct fuse_file_info *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::flush(const char *, struct fuse_file_info *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::fsync(const char *, int, struct fuse_file_info *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::fsyncdir(const char *, int, struct fuse_file_info *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::ftruncate(const char *, off_t, struct fuse_file_info *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::getattr(const char *, struct stat *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::getxattr(const char *, const char *, char *, size_t)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::link(const char *, const char *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::listxattr(const char *, char *, size_t)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::mkdir(const char *, mode_t)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::mknod(const char *, mode_t, dev_t)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::open(const char *, struct fuse_file_info *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::opendir(const char *, struct fuse_file_info *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::read(const char *, char *, size_t, off_t, struct fuse_file_info *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::readdir(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::readlink(const char *, char *, size_t)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::release(const char *, struct fuse_file_info *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::releasedir(const char *, struct fuse_file_info *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::removexattr(const char *, const char *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::rename(const char *, const char *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::rmdir(const char *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::setxattr(const char *, const char *, const char *, size_t, int)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::statfs(const char *, struct statvfs *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::symlink(const char *, const char *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::truncate(const char *, off_t)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::unlink(const char *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::write(const char *, const char *, size_t, off_t, struct fuse_file_info *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::lock(const char *, struct fuse_file_info *, int, struct flock *)
+{
+ return -ENOSYS;
+}
+int FuseAppBase::utimens(const char *, const struct timespec[2])
+{
+ return -ENOSYS;
+}
+int FuseAppBase::bmap(const char *, size_t, uint64_t *)
+{
+ return -ENOSYS;
+}
+
diff --git a/netfs/fuseapp.h b/netfs/fuseapp.h
new file mode 100644
index 0000000..965ef0d
--- /dev/null
+++ b/netfs/fuseapp.h
@@ -0,0 +1,50 @@
+#ifndef FUSEAPP_H
+#define FUSEAPP_H
+
+#define FUSE_USE_VERSION 26
+#include <fuse.h>
+
+class FuseAppBase {
+ public:
+ FuseAppBase();
+ virtual ~FuseAppBase() = 0;
+ virtual void * init (struct fuse_conn_info * info);
+ virtual int opt_parse(void *, const char * arg, int key, struct fuse_args *);
+ virtual int access(const char *, int);
+ virtual int chmod(const char *, mode_t);
+ virtual int chown(const char *, uid_t, gid_t);
+ virtual int create(const char *, mode_t, struct fuse_file_info *);
+ virtual int fgetattr(const char *, struct stat *, struct fuse_file_info *);
+ virtual int flush(const char *, struct fuse_file_info *);
+ virtual int fsync(const char *, int, struct fuse_file_info *);
+ virtual int fsyncdir(const char *, int, struct fuse_file_info *);
+ virtual int ftruncate(const char *, off_t, struct fuse_file_info *);
+ virtual int getattr(const char *, struct stat *);
+ virtual int getxattr(const char *, const char *, char *, size_t);
+ virtual int link(const char *, const char *);
+ virtual int listxattr(const char *, char *, size_t);
+ virtual int mkdir(const char *, mode_t);
+ virtual int mknod(const char *, mode_t, dev_t);
+ virtual int open(const char *, struct fuse_file_info *);
+ virtual int opendir(const char *, struct fuse_file_info *);
+ virtual int read(const char *, char *, size_t, off_t, struct fuse_file_info *);
+ virtual int readdir(const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *);
+ virtual int readlink(const char *, char *, size_t);
+ virtual int release(const char *, struct fuse_file_info *);
+ virtual int releasedir(const char *, struct fuse_file_info *);
+ virtual int removexattr(const char *, const char *);
+ virtual int rename(const char *, const char *);
+ virtual int rmdir(const char *);
+ virtual int setxattr(const char *, const char *, const char *, size_t, int);
+ virtual int statfs(const char *, struct statvfs *);
+ virtual int symlink(const char *, const char *);
+ virtual int truncate(const char *, off_t);
+ virtual int unlink(const char *);
+ virtual int write(const char *, const char *, size_t, off_t, struct fuse_file_info *);
+ virtual int lock(const char *, struct fuse_file_info *, int cmd, struct flock *);
+ virtual int utimens(const char *, const struct timespec tv[2]);
+ virtual int bmap(const char *, size_t blocksize, uint64_t *idx);
+};
+
+#endif
+
diff --git a/netfs/fusecallers.h b/netfs/fusecallers.h
new file mode 100644
index 0000000..cb521df
--- /dev/null
+++ b/netfs/fusecallers.h
@@ -0,0 +1,4 @@
+#ifndef FUSECALLERS_H
+#define FUSECALLERS_H
+#endif
+
diff --git a/netfs/makefile.in b/netfs/makefile.in
new file mode 100644
index 0000000..f7901ba
--- /dev/null
+++ b/netfs/makefile.in
@@ -0,0 +1,63 @@
+FUSEFLAGS=-D_FILE_OFFSET_BITS=64
+COMPFLAGS=-DTHREADSAFE -I ../libmisc -I /usr/include/libxml2
+myCXXFLAGS=${CXXFLAGS} ${COMPFLAGS} ${FUSEFLAGS}
+LDFLAGS=-lfuse -Xlinker --warn-once -L ../libmisc -l misc -l xml2
+CXX=g++
+CC=${CXX}
+default : makefile all
+
+-include makedeps.mk
+
+all : makedeps.mk tags netfs netfsd
+makefile : makefile.in
+ @echo "Makefile out of date. Please re-run configure."
+ @false
+
+makedeps.mk : $(patsubst %.cpp, %.d, $(wildcard *.cpp)) $(wildcard *.h)
+ cat *.d > $@
+
+%.o : %.d
+
+%.o : %.cpp
+ g++ -c ${myCXXFLAGS} $< -o $@
+%.d : %.cpp $(wildcard *.h)
+ g++ -M ${myCXXFLAGS} $< -o $@
+
+tags : $(wildcard *.c*) $(wildcard *.h)
+ @exuberant-ctags -R
+
+NETFSOBJS= \
+ comms.o \
+ fuse.o \
+ fuseapp.o \
+ msgtypes.o \
+
+NETFSDOBJS= \
+ comms.o \
+ daemon.o \
+ daemonConfig.o \
+ msgtypes.o \
+
+netfs : ${NETFSOBJS}
+ g++ -o $@ ${NETFSOBJS} ${LDFLAGS}
+
+netfsd : ${NETFSDOBJS}
+ g++ -o $@ ${NETFSDOBJS} ${LDFLAGS}
+
+.SUFFIXES: .cpp .o
+.PHONY: clean test mount umount install uninstall
+
+clean :
+ rm -f *.o *.d
+ rm -f netfsd netfs
+ rm -f makedeps.mk
+
+install : netfs netfsd
+ cp -f netfs /usr/bin/netfs
+ ln -sf /usr/bin/netfs /sbin/mount.netfs
+ cp -f netfsd /sbin/netfsd
+
+uninstall :
+ rm -f /usr/bin/netfs
+ rm -f /sbin/mount.netfs
+ rm -f /sbin/netfsd
diff --git a/netfs/msgtypes.cpp b/netfs/msgtypes.cpp
new file mode 100644
index 0000000..e7a814e
--- /dev/null
+++ b/netfs/msgtypes.cpp
@@ -0,0 +1,152 @@
+#include "msgtypes.h"
+#include "xfers.h"
+
+MessageFactories & MsgFacs()
+{
+ static MessageFactories m;
+ return m;
+}
+
+MSGTYPE(0, SimpleInt)
+MSGTYPE(1, AccessRequest)
+MSGTYPE(2, GetAttrRequest)
+MSGTYPE(3, GetAttrReply)
+MSGTYPE(4, OpenDirRequest)
+MSGTYPE(5, OpenDirReply)
+MSGTYPE(6, CloseDirRequest)
+MSGTYPE(7, CloseDirReply)
+MSGTYPE(8, ReadDirRequest)
+MSGTYPE(9, ReadDirReply)
+MSGTYPE(10, ReadDirContent)
+MSGTYPE(11, OpenRequest)
+MSGTYPE(12, OpenReply)
+MSGTYPE(13, CloseRequest)
+MSGTYPE(14, CloseReply)
+MSGTYPE(15, ReadRequest)
+MSGTYPE(16, ReadReply)
+MSGTYPE(17, WriteRequest)
+MSGTYPE(18, WriteReply)
+
+template<class Type> void operator<<(FILE &, const Type & t);
+template<class Type> void operator>>(FILE &, Type & t);
+
+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) {
+ }
+}
+template<> void operator>>(FILE & f, std::string & t)
+{
+ std::string::size_type len;
+ f >> len;
+ t.clear();
+ t.reserve(len);
+ while (len --) {
+ t.push_back(fgetc(&f));
+ }
+}
+template<class Type> void operator<<(FILE & f, const Type & t)
+{
+ if (fwrite(&t, sizeof(t), 1, &f) < sizeof(t)) {
+ }
+}
+template<class Type> void operator>>(FILE & f, Type & t)
+{
+ if (fread(&t, sizeof(t), 1, &f) < sizeof(t)) {
+ }
+}
+
+void
+ContentBase::Send(FILE*) const
+{
+}
+void
+ContentBase::Read(FILE*)
+{
+}
+Xfer2(RequestBase, ContentBase, uid, gid)
+Xfer1(OpenDirRequest, RequestBase, path)
+Xfer2(OpenDirReply, ContentBase, error, handle)
+Xfer2(ReadDirReply, ContentBase, error, count)
+Xfer1(ReadDirRequest, RequestBase, handle)
+Xfer2(AccessRequest, RequestBase, path, access)
+Xfer1(SimpleInt, ContentBase, value)
+Xfer1(GetAttrRequest, RequestBase, path)
+Xfer2(GetAttrReply, ContentBase, res, val)
+Xfer1(CloseDirRequest, RequestBase, handle)
+Xfer1(CloseDirReply, ContentBase, error)
+Xfer2(ReadDirContent, ContentBase, path, val)
+Xfer1(OpenRequest, RequestBase, path)
+Xfer2(OpenReply, ContentBase, error, handle)
+Xfer1(CloseRequest, RequestBase, handle)
+Xfer1(CloseReply, ContentBase, error)
+Xfer3(ReadRequest, RequestBase, handle, offset, size)
+Xfer1(WriteReply, ContentBase, error)
+
+ReadReply::~ReadReply()
+{
+ if (data) {
+ delete[] data;
+ }
+}
+void
+ReadReply::Send(FILE*f) const
+{
+ ContentBase::Send(f);
+ *f << error;
+ *f << size;
+ if (size) {
+ if (fwrite(data, size, 1, f) < size) {
+ }
+ }
+}
+void
+ReadReply::Read(FILE*f)
+{
+ ContentBase::Read(f);
+ *f >> error;
+ *f >> size;
+ if (size) {
+ data = new char[size]();
+ if (fread(data, size, 1, f) < size) {
+ }
+ }
+ else {
+ data = NULL;
+ }
+}
+
+WriteRequest::~WriteRequest()
+{
+ if (data) {
+ delete[] data;
+ }
+}
+void
+WriteRequest::Send(FILE*f) const
+{
+ RequestBase::Send(f);
+ *f << handle;
+ *f << size;
+ *f << offset;
+ if (fwrite(data, size, 1, f) < size) {
+ }
+}
+void
+WriteRequest::Read(FILE*f)
+{
+ RequestBase::Read(f);
+ *f >> handle;
+ *f >> size;
+ *f >> offset;
+ if (size) {
+ data = new char[size]();
+ if (fread(data, size, 1, f) < size) {
+ }
+ }
+ else {
+ data = NULL;
+ }
+}
diff --git a/netfs/msgtypes.h b/netfs/msgtypes.h
new file mode 100644
index 0000000..c29dc93
--- /dev/null
+++ b/netfs/msgtypes.h
@@ -0,0 +1,212 @@
+#ifndef MSGTYPES_H
+#define MSGTYPES_H
+
+#include "smartpointer.h"
+#include "comms.h"
+#include <map>
+#include <string>
+#include <sys/stat.h>
+#include <stdint.h>
+
+typedef DataPayloadPtr (*MessageFactory)();
+typedef std::map<uint16_t, MessageFactory> MessageFactories;
+MessageFactories & MsgFacs();
+
+template <class Cls>
+class Factory {
+ public:
+ Factory() { MsgFacs()[Cls::TypeID] = &create; }
+ static DataPayloadPtr create() {
+ return new TypedPayload<Cls>();
+ }
+};
+
+class ContentBase : public IsRefCounted {
+ public:
+ ~ContentBase() = 0;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+
+class RequestBase : public ContentBase {
+ public:
+ uid_t uid;
+ gid_t gid;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+
+class SimpleInt : public ContentBase {
+ public:
+ int value;
+ const static uint16_t TypeID;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+
+class AccessRequest : public RequestBase {
+ public:
+ std::string path;
+ int access;
+ const static uint16_t TypeID;
+ typedef SimpleInt Reply;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+
+class GetAttrReply : public ContentBase {
+ public:
+ int res;
+ struct stat val;
+ const static uint16_t TypeID;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+
+class GetAttrRequest : public RequestBase {
+ public:
+ std::string path;
+ const static uint16_t TypeID;
+ typedef GetAttrReply Reply;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+
+class OpenDirReply : public ContentBase {
+ public:
+ int handle;
+ int error;
+ const static uint16_t TypeID;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+
+class OpenDirRequest : public RequestBase {
+ public:
+ std::string path;
+ const static uint16_t TypeID;
+ typedef OpenDirReply Reply;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+
+class CloseDirReply : public ContentBase {
+ public:
+ int error;
+ const static uint16_t TypeID;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+
+class CloseDirRequest : public RequestBase {
+ public:
+ int handle;
+ const static uint16_t TypeID;
+ typedef CloseDirReply Reply;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+
+class ReadDirReply : public ContentBase {
+ public:
+ int error;
+ size_t count;
+ const static uint16_t TypeID;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+
+class ReadDirRequest : public RequestBase {
+ public:
+ int handle;
+ const static uint16_t TypeID;
+ typedef ReadDirReply Reply;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+
+class ReadDirContent : public ContentBase {
+ public:
+ std::string path;
+ struct stat val;
+ const static uint16_t TypeID;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+
+class OpenReply : public ContentBase {
+ public:
+ int error;
+ int handle;
+ const static uint16_t TypeID;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+class OpenRequest : public RequestBase {
+ public:
+ std::string path;
+ const static uint16_t TypeID;
+ typedef OpenReply Reply;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+class CloseReply : public ContentBase {
+ public:
+ int error;
+ const static uint16_t TypeID;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+class CloseRequest : public RequestBase {
+ public:
+ int handle;
+ const static uint16_t TypeID;
+ typedef CloseReply Reply;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+class ReadReply : public ContentBase {
+ public:
+ ~ReadReply();
+ int error;
+ char * data;
+ size_t size;
+ const static uint16_t TypeID;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+class ReadRequest : public RequestBase {
+ public:
+ int handle;
+ size_t size;
+ off_t offset;
+ const static uint16_t TypeID;
+ typedef ReadReply Reply;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+class WriteReply : public ContentBase {
+ public:
+ int error;
+ const static uint16_t TypeID;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+class WriteRequest : public RequestBase {
+ public:
+ ~WriteRequest();
+ int handle;
+ size_t size;
+ char * data;
+ off_t offset;
+ const static uint16_t TypeID;
+ typedef WriteReply Reply;
+ virtual void Send(FILE*) const;
+ virtual void Read(FILE*);
+};
+
+#define MSGTYPE(id, cls) \
+ const uint16_t cls::TypeID = id; \
+ namespace FactoryFor##cls { Factory<cls> fac; }
+#endif
+
diff --git a/netfs/xfers.h b/netfs/xfers.h
new file mode 100644
index 0000000..c1369eb
--- /dev/null
+++ b/netfs/xfers.h
@@ -0,0 +1,53 @@
+#ifndef XFERS_H
+#define XFERS_H
+
+#define Xfer1(Ptype, Pbase, v1) \
+void \
+Ptype::Send(FILE*f) const \
+{ \
+ Pbase::Send(f); \
+ *f << v1; \
+} \
+void \
+Ptype::Read(FILE*f) \
+{ \
+ Pbase::Read(f); \
+ *f >> v1; \
+}
+
+#define Xfer2(Ptype, Pbase, v1, v2) \
+void \
+Ptype::Send(FILE*f) const \
+{ \
+ Pbase::Send(f); \
+ *f << v1; \
+ *f << v2; \
+} \
+void \
+Ptype::Read(FILE*f) \
+{ \
+ Pbase::Read(f); \
+ *f >> v1; \
+ *f >> v2; \
+}
+
+#define Xfer3(Ptype, Pbase, v1, v2, v3) \
+void \
+Ptype::Send(FILE*f) const \
+{ \
+ Pbase::Send(f); \
+ *f << v1; \
+ *f << v2; \
+ *f << v3; \
+} \
+void \
+Ptype::Read(FILE*f) \
+{ \
+ Pbase::Read(f); \
+ *f >> v1; \
+ *f >> v2; \
+ *f >> v3; \
+}
+
+#endif
+