From 1ec903cd98e1ab8c4333e5e9de201498f78ff335 Mon Sep 17 00:00:00 2001 From: Oleg Bakharev Date: Sun, 17 Apr 2022 08:33:23 +0300 Subject: [PATCH] Created project structure and added source files --- .gitignore | 15 +++ dub.sdl | 6 + source/app.d | 50 ++++++++ source/io.d | 99 +++++++++++++++ source/server.d | 131 ++++++++++++++++++++ source/styxserver.d | 284 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 585 insertions(+) create mode 100644 .gitignore create mode 100644 dub.sdl create mode 100644 source/app.d create mode 100644 source/io.d create mode 100644 source/server.d create mode 100644 source/styxserver.d diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ce20625 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.dub +docs.json +__dummy.html +docs/ +/serve9 +serve9.so +serve9.dylib +serve9.dll +serve9.a +serve9.lib +serve9-test-* +*.exe +*.o +*.obj +*.lst diff --git a/dub.sdl b/dub.sdl new file mode 100644 index 0000000..ae155da --- /dev/null +++ b/dub.sdl @@ -0,0 +1,6 @@ +name "serve9" +description "Simple tool for sharing folders via 9P/Styx" +authors "aquareji" +copyright "Copyright © 2022, aquareji" +license "ESL 1.0" +dependency "styx2000" version="~>1.0.0" diff --git a/source/app.d b/source/app.d new file mode 100644 index 0000000..28e116d --- /dev/null +++ b/source/app.d @@ -0,0 +1,50 @@ +private { + import std.conv : ConvException; + import std.getopt; + import std.stdio : writeln; + + import serve9.styxserver; +} + +string address; +ushort port; +string path; +string uid = "user"; +string gid = "user"; +bool messages; +bool bytes; + +void main(string[] args) +{ + try + { + getopt( + args, + std.getopt.config.required, + "addr|a", &address, /* server ip */ + std.getopt.config.required, + "port|p", &port, /* server port */ + std.getopt.config.required, + std.getopt.config.caseSensitive, + "path|d", &path, /* path to folder */ + "uid|u", &uid, /* user id */ + "gid|g", &gid, /* group id */ + "messages|M", &messages, /* debug mode */ + "bytes|B", &bytes, /* show raw bytes */ + std.getopt.config.passThrough + ); + + auto server = new StyxShareServer(path,uid, gid); + with (server) + { + messagesMode(messages); + bytesMode(bytes); + setup6(address, port); + run; + } + } + catch (Throwable e) + { + writeln(e.msg); + } +} diff --git a/source/io.d b/source/io.d new file mode 100644 index 0000000..486ea36 --- /dev/null +++ b/source/io.d @@ -0,0 +1,99 @@ +module serve9.io; + +private { + import std.stdio : File; + + import styx2000.protobj : Count, Data, StyxObject; +} + +auto readAt(string filepath, ulong offset, uint count) +{ + File file; + file.open(filepath, `rb`); + auto size = file.size; + + if (offset >= size) + { + return cast(StyxObject[])[ + new Count(0), + new Data + ]; + } + else + { + ubyte[] data = new ubyte[count]; + uint realCount; + + file.seek(offset); + file.rawRead(data); + + + if ((size - offset) < count) + { + realCount = cast(uint) (size - offset); + } + else + { + realCount = count; + } + + return cast(StyxObject[])[ + new Count(cast(uint) realCount), + new Data(data[0..realCount]) + ]; + } +} + +auto readAt(ubyte[] data, ulong offset, uint count) +{ + auto size = data.length; + if (offset >= size) + { + return cast(StyxObject[])[ + new Count(0), + new Data + ]; + } + else + { + ubyte[] bdata; + + if ((offset + count) > data.length) + { + if (offset >= data.length) + { + return cast(StyxObject[])[ + new Count(0), + new Data + ]; + } + else + { + bdata = data[offset..$]; + } + } + else + { + bdata = data[offset..offset+count]; + } + + return cast(StyxObject[])[ + new Count(cast(uint) bdata.length), + new Data(bdata) + ]; + } +} + +auto writeAt(string filepath, ulong offset, uint count, ubyte[] data) +{ + File file; + file.open(filepath, `wb`); + + auto buffer = data[0..count]; + file.seek(offset); + file.rawWrite(buffer); + + return cast(StyxObject[])[ + new Count(cast(uint) buffer.length) + ]; +} diff --git a/source/server.d b/source/server.d new file mode 100644 index 0000000..2539a3a --- /dev/null +++ b/source/server.d @@ -0,0 +1,131 @@ +module serve9.server; + +private { + import std.algorithm : remove; + import std.socket; +} + +class GenericSimpleServer(uint BUFFER_SIZE, uint MAXIMAL_NUMBER_OF_CONNECTIONS) +{ + protected { + ubyte[BUFFER_SIZE] _buffer; + + string _address; + ushort _port; + bool _immediate; + Socket _listener; + Socket[] _readable; + SocketSet _sockets; + } + + abstract ubyte[] handle(ubyte[] request); + + final void run() + { + while (true) + { + serve; + + scope(failure) { + _sockets = null; + _listener.close; + } + } + } + + final void setup4(string address, ushort port, int backlog = 10, bool immediate = false) + { + _address = address; + _port = port; + _listener = new Socket(AddressFamily.INET, SocketType.STREAM); + _immediate = immediate; + + with (_listener) + { + bind(new InternetAddress(_address, _port)); + listen(backlog); + } + + _sockets = new SocketSet(MAXIMAL_NUMBER_OF_CONNECTIONS + 1); + } + + final void setup6(string address, ushort port, int backlog = 10, bool immediate = false) + { + _address = address; + _port = port; + _listener = new Socket(AddressFamily.INET6, SocketType.STREAM); + _immediate = immediate; + + with (_listener) + { + bind(new Internet6Address(_address, _port)); + listen(backlog); + } + + _sockets = new SocketSet(MAXIMAL_NUMBER_OF_CONNECTIONS + 1); + } + + private + { + final void serve() + { + _sockets.add(_listener); + + foreach (socket; _readable) + { + _sockets.add(socket); + } + + Socket.select(_sockets, null, null); + + for (uint i = 0; i < _readable.length; i++) + { + if (_sockets.isSet(_readable[i])) + { + auto realBufferSize = _readable[i].receive(_buffer); + + if (realBufferSize != 0) + { + auto data = _buffer[0..realBufferSize]; + + _readable[i].send( + handle(data) + ); + } + + if (_immediate) + { + _readable[i].close; + _readable = _readable.remove(i); + i--; + } + } + } + + if (_sockets.isSet(_listener)) + { + Socket currentSocket = null; + + scope (failure) + { + if (currentSocket) + { + currentSocket.close; + } + } + + currentSocket = _listener.accept; + + if (_readable.length < MAXIMAL_NUMBER_OF_CONNECTIONS) + { + _readable ~= currentSocket; + } + else + { + currentSocket.close; + } + } + _sockets.reset; + } + } +} diff --git a/source/styxserver.d b/source/styxserver.d new file mode 100644 index 0000000..5b034d9 --- /dev/null +++ b/source/styxserver.d @@ -0,0 +1,284 @@ +module serve9.styxserver; + +private { + enum uint BUFFER_SIZE = 8_192; + enum uint CONNECTIONS_NUMBER = 60; + + import std.file : dirEntries, DirEntry, SpanMode, exists; + import std.path : buildPath, baseName; + import std.stdio : writeln; + + import styx2000.extrautil.casts; + import styx2000.extrautil.dir; + import styx2000.extrautil.dirstat; + + import styx2000.extrautil.mischelpers : createQid, createStat; + import styx2000.extrautil.styxmessage; + + import styx2000.protoconst.modes; + import styx2000.protoconst.messages; + import styx2000.protoconst.qids; + + import styx2000.protomsg : decode, encode; + + import styx2000.protobj; + + import serve9.io; + import serve9.server; +} + +class StyxShareServer : GenericSimpleServer!(BUFFER_SIZE, CONNECTIONS_NUMBER) +{ + private { + string _dir; + string _uid; + string _gid; + + string[uint] _pool; + + bool _messages; + bool _bytes; + } + + this(string dir, string uid, string gid) + { + _dir = dir; + _uid = uid; + _gid = gid; + } + + void messagesMode(bool messages) + { + _messages = messages; + } + + void bytesMode(bool bytes) + { + _bytes = bytes; + } + + override ubyte[] handle(ubyte[] request) + { + auto rmsg = decode(request); + auto tmsg = process(rmsg); + auto response = encode(tmsg); + + if (_messages) + { + writeln(`-> `, rmsg); + writeln(`<- `, tmsg); + } + + if (_bytes) + { + writeln(`-> `, request); + writeln(`<- `, response); + } + return response; + } + + private { + StyxMessage walk(ushort tag, uint fid, uint newfid, string[] nwname) { + StyxMessage msg = createHeader(0, STYX_MESSAGE_TYPE.R_WALK, tag); + + if (nwname.length == 0) + { + msg ~= cast(StyxObject) new Nwqid; + _pool[newfid] = _dir; + } + else + { + auto path = buildPath(_dir ~ nwname); + + if (path.exists) + { + Qid[] qids; + auto nwqid = new Nwqid; + + foreach (e; nwname) + { + auto npath = buildPath(_dir ~ nwname); + qids ~= createQid(npath); + } + + nwqid.setQid(qids); + msg ~= cast(StyxObject) nwqid; + + if (path != _dir) + { + _pool[newfid] = path; + } + } + else + { + msg = createRmsgError(tag, `Walk error`); + } + } + + return msg; + } + + StyxMessage stat(ushort tag, uint fid) { + StyxMessage msg = createHeader(0, STYX_MESSAGE_TYPE.R_STAT, tag); + + if (fid in _pool) + { + auto path = _pool[fid]; + + msg ~= cast(StyxObject) createStat(path, 0, 0, _uid, _gid); + } + else + { + msg = createRmsgError(tag, `Stat error`); + } + + return msg; + } + + StyxMessage open(ushort tag, uint fid, STYX_FILE_MODE mode) { + StyxMessage msg; + + if (fid in _pool) + { + auto path = _pool[fid]; + + auto qid = createQid(_dir); + msg = createRmsgOpen(tag, qid.getType, qid.getVers, qid.getPath); + } + else + { + msg = createRmsgError(tag, `Open error`); + } + + return msg; + } + + StyxMessage read(ushort tag, uint fid, ulong offset, uint count) { + StyxMessage msg = createHeader(0, STYX_MESSAGE_TYPE.R_READ, tag); + + if (fid in _pool) + { + auto path = _pool[fid]; + + if (DirEntry(path).isDir) + { + Dir[] dirs; + + foreach (e; dirEntries(path, SpanMode.shallow)) + { + auto dir = createStat(e.name, 0, 0, _uid, _gid).stat2dir; + dirs ~= dir; + } + + auto ds = new DirStat(dirs); + msg ~= readAt(ds.pack, offset, count); + } + else + { + msg ~= readAt(path, offset, count); + } + } + else + { + msg = createRmsgError(tag, `Read error`); + } + + return msg; + } + + StyxMessage write(ushort tag, uint fid, ulong offset, uint count, ubyte[] data) { + StyxMessage msg = createHeader(0, STYX_MESSAGE_TYPE.R_WRITE, tag); + + if (fid in _pool) + { + auto path = _pool[fid]; + + if (DirEntry(path).isDir) + { + msg = createRmsgError(tag, `Write error`); + } + else + { + msg ~= writeAt(path, offset, count, data); + } + } + else + { + msg = createRmsgError(tag, `Write error`); + } + + return msg; + } + + StyxMessage processFS(STYX_MESSAGE_TYPE type, ushort tag, StyxObject[] args...) { + StyxMessage msg; + + auto fid = args[0].toFid.getFid; + + switch (type) + { + case STYX_MESSAGE_TYPE.T_WALK: + auto newfid = args[1].toNewFid.getFid; + auto nwname = args[2].toNwname.getName; + msg = walk(tag, fid, newfid, nwname); + break; + case STYX_MESSAGE_TYPE.T_STAT: + msg = stat(tag, fid); + break; + case STYX_MESSAGE_TYPE.T_OPEN: + auto mode = args[1].toMode.getMode; + msg = open(tag, fid, mode); + break; + case STYX_MESSAGE_TYPE.T_READ: + auto offset = args[1].toOffset.getOffset; + auto count = args[2].toCount.getCount; + msg = read(tag, fid, offset, count); + break; + case STYX_MESSAGE_TYPE.T_WRITE: + auto offset = args[1].toOffset.getOffset; + auto count = args[2].toCount.getCount; + auto data = args[3].toData.getData; + msg = write(tag, fid, offset, count, data); + break; + default: + msg = createRmsgError(tag, `Wrong message`); + break; + } + + return msg; + } + + StyxMessage process(StyxMessage query) { + StyxMessage reply; + + auto type = query[1].toType.getType; + auto tag = query[2].toTag.getTag; + + switch (type) + { + case STYX_MESSAGE_TYPE.T_VERSION: + reply = createRmsgVersion; + break; + case STYX_MESSAGE_TYPE.T_ATTACH: + auto fid = query[3].toFid.getFid; + _pool[fid] = _dir; + reply = createRmsgAttach(tag, STYX_QID_TYPE.QTDIR); + break; + case STYX_MESSAGE_TYPE.T_CLUNK: + auto fid = query[3].toFid.getFid; + _pool.remove(fid); + reply = createRmsgClunk(tag); + break; + case STYX_MESSAGE_TYPE.T_FLUSH: + reply = createRmsgFlush(tag); + break; + default: + auto args = query[3..$]; + reply = processFS(type, tag, args); + break; + } + + return reply; + } + } +} -- 2.45.2