History

Separated the server code from the host filing system code, creating specific modules for each.

David Boddie committed 8 years ago

Commit 1ed27611 parent b6957db2 files · +223 −206Browse files

Unified · Split

2 files
  1. styxfs.py +4 −206
  2. styxserver.py +219 −0
styxfs.py+4−206
@@ -17,211 +17,8 @@ · 16 unmodified lines
1717 # You should have received a copy of the GNU General Public License
1818 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1919
20−import os, shutil, socket, stat, sys
21−import styx
22−
23−class StyxFSError(Exception):
24− pass
25−
26−class StyxFSServer:
27−
28− MAX_MSG_SIZE = 0
29−
30− def __init__(self, directory):
31−
32− self.dir = directory
33− self.clients = {}
34−
35− def serve(self, host, port):
36−
37− s = socket.socket()
38− s.bind((host, port))
39− s.listen(1)
40−
41− while True:
42−
43− conn, client = s.accept()
44− self.clients[client] = FileStore(self.dir)
45−
46− while client in self.clients:
47−
48− message = styx.decode(sock=conn)
49− print message
50−
51− try:
52− handler = self.handlers[message.code]
53− reply = handler(self, conn, client, message)
54− except KeyError:
55− reply = styx.Rerror(message.tag, "Unsupported message.")
56− except StyxFSError, e:
57− reply = styx.Rerror(message.tag, e.message)
58− except socket.error:
59− # The connection was probably closed by the client.
60− break
61−
62− print reply
63− reply.encode(conn)
64−
65− def Tversion(self, conn, client, msg):
66−
67− return styx.Rversion(msg.tag, msg.msize, msg.version)
68−
69− def Tattach(self, conn, client, msg):
70−
71− store = self.clients[client]
72−
73− qid = store.get_root_qid(msg.fid, msg.afid, msg.uname, msg.aname)
74− return styx.Rattach(msg.tag, qid)
75−
76− def Tstat(self, conn, client, msg):
77−
78− store = self.clients[client]
79−
80− s = store.stat(msg.fid)
81− return styx.Rstat(msg.tag, s)
82−
83− def Twalk(self, conn, client, msg):
84−
85− store = self.clients[client]
86−
87− # The fid must have an existing qid but not be in use for open or create.
88− qid, path = store.get_qid_path(msg.fid)
89−
90− # Generate qids for each of the elements in the path, starting from the
91− # path corresponding to the current fid.
92− qids = []
93− new_path = path.split("/")
94− try:
95− for element in msg.wname:
96−
97− if element == "..":
98− # Handle parent directories as path elements.
99− if new_path: new_path.pop()
100− else:
101− new_path.append(element)
102−
103− # Update the qid variable, create qids for each intermediate
104− # path, and record the qids created.
105− qid = store.make_qid("/".join(new_path))
106− qids.append(qid)
107−
108− except OSError:
109− if len(qids) > 0:
110− return styx.Rwalk(msg.tag, qids)
111− else:
112− return styx.Rerror(msg.tag, "Not found.")
113−
114− # Set the qid and path for the newfid passed by the caller.
115− store.set_qid_path(msg.newfid, qid, "/".join(new_path))
116−
117− return styx.Rwalk(msg.tag, qids)
118−
119− def Topen(self, conn, client, msg):
120−
121− store = self.clients[client]
122−
123− # The fid must have an existing qid.
124− qid, path = store.get_qid_path(msg.fid)
125−
126− if not store.open(msg.fid, msg.mode):
127− return styx.Rerror(msg.tag, "Cannot open file.")
128−
129− return styx.Ropen(msg.tag, qid, self.MAX_MSG_SIZE)
130−
131− def Tcreate(self, conn, client, msg):
132−
133− store = self.clients[client]
134−
135− # The fid must have an existing qid that corresponds to a directory
136− # and cannot be itself in use.
137− qid, path = store.get_qid_path(msg.fid)
138−
139− new_qid = store.create(msg.fid, msg.name, msg.perm)
140− if new_qid == False:
141− return styx.Rerror(msg.tag, "Cannot create file.")
142−
143− # The fid now refers to the newly open file or directory.
144− if not store.open(msg.fid, msg.mode):
145− return styx.Rerror(msg.tag, "Cannot open file.")
146−
147− return styx.Rcreate(msg.tag, new_qid, self.MAX_MSG_SIZE)
148−
149− def Tread(self, conn, client, msg):
150−
151− store = self.clients[client]
152−
153− # The fid must have an existing qid.
154− data = store.read(msg.fid, msg.offset, msg.count)
155−
156− return styx.Rread(msg.tag, data)
157−
158− def Twrite(self, conn, client, msg):
159−
160− store = self.clients[client]
161−
162− # The fid must have an existing qid.
163− count = store.write(msg.fid, msg.offset, msg.data)
164−
165− return styx.Rwrite(msg.tag, count)
166−
167− def Tclunk(self, conn, client, msg):
168−
169− store = self.clients[client]
170−
171− # Release/free the qid.
172− store.free_qid_path(msg.fid)
173−
174− # Additionally, if the fid refers to the root of the file system then
175− # remove the client from the clients dictionary to disconnect it.
176− if msg.fid == store.root_fid:
177− del self.clients[client]
178−
179− return styx.Rclunk(msg.tag)
180−
181− def Tremove(self, conn, client, msg):
182−
183− store = self.clients[client]
184−
185− # Remove the file and clunk it whether the remove was successful or not.
186− result = store.remove(msg.fid)
187−
188− if result == True:
189− store.free_qid_path(msg.fid)
190− return styx.Rremove(msg.tag)
191− else:
192− store.free_qid_path(msg.fid)
193− return styx.Rerror(msg.tag, result)
194−
195− def Tflush(self, conn, client, msg):
196−
197− store = self.clients[client]
198−
199− # If we supported a queue of commands then we would remove any pending
200− # commands with tags matching msg.oldtag.
201−
202− return styx.Rflush(msg.tag)
203−
204− def Twstat(self, conn, client, msg):
205−
206− store = self.clients[client]
207−
208− s = store.wstat(msg.fid, msg.stat)
209− return styx.Rwstat(msg.tag)
210−
211− handlers = {
212− styx.Tversion.code: Tversion,
213− styx.Tattach.code: Tattach,
214− styx.Tstat.code: Tstat,
215− styx.Twalk.code: Twalk,
216− styx.Topen.code: Topen,
217− styx.Tcreate.code: Tcreate,
218− styx.Tread.code: Tread,
219− styx.Twrite.code: Twrite,
220− styx.Tclunk.code: Tclunk,
221− styx.Tremove.code: Tremove,
222− styx.Twstat.code: Twstat
223− }
224−
20+import os, stat, sys
21+import styx, styxserver
22522
22623 class FileStore:
22724
@@ -462,5 +259,6 @@ if __name__ == "__main__": · 234 unmodified lines
462259 directory = sys.argv[1]
463260 port = int(sys.argv[2])
464261
465− server = StyxFSServer(directory)
262+ store = FileStore(directory)
263+ server = styxserver.StyxServer(store)
466264 server.serve("", port)
styxserver.py+219−0
@@ -0,0 +1,219 @@
1+# styx.py - Classes to decode and encode Styx (9P2000) messages.
2+#
3+# Copyright (C) 2018 David Boddie <david@boddie.org.uk>
4+#
5+# This program is free software: you can redistribute it and/or modify
6+# it under the terms of the GNU General Public License as published by
7+# the Free Software Foundation, either version 3 of the License, or
8+# (at your option) any later version.
9+#
10+# This program is distributed in the hope that it will be useful,
11+# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+# GNU General Public License for more details.
14+#
15+# You should have received a copy of the GNU General Public License
16+# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
18+import socket
19+import styx
20+
21+class StyxServerError(Exception):
22+ pass
23+
24+class StyxServer:
25+
26+ MAX_MSG_SIZE = 0
27+
28+ def __init__(self, store):
29+
30+ self.store = store
31+ self.clients = {}
32+
33+ def serve(self, host, port):
34+
35+ s = socket.socket()
36+ s.bind((host, port))
37+ s.listen(1)
38+
39+ while True:
40+
41+ conn, client = s.accept()
42+ self.clients[client] = self.store
43+
44+ while client in self.clients:
45+
46+ message = styx.decode(sock=conn)
47+
48+ try:
49+ handler = self.handlers[message.code]
50+ reply = handler(self, conn, client, message)
51+ except KeyError:
52+ reply = styx.Rerror(message.tag, "Unsupported message.")
53+ except StyxServerError, e:
54+ reply = styx.Rerror(message.tag, e.message)
55+ except socket.error:
56+ # The connection was probably closed by the client.
57+ break
58+
59+ reply.encode(conn)
60+
61+ def Tversion(self, conn, client, msg):
62+
63+ return styx.Rversion(msg.tag, msg.msize, msg.version)
64+
65+ def Tattach(self, conn, client, msg):
66+
67+ store = self.clients[client]
68+
69+ qid = store.get_root_qid(msg.fid, msg.afid, msg.uname, msg.aname)
70+ return styx.Rattach(msg.tag, qid)
71+
72+ def Tstat(self, conn, client, msg):
73+
74+ store = self.clients[client]
75+
76+ s = store.stat(msg.fid)
77+ return styx.Rstat(msg.tag, s)
78+
79+ def Twalk(self, conn, client, msg):
80+
81+ store = self.clients[client]
82+
83+ # The fid must have an existing qid but not be in use for open or create.
84+ qid, path = store.get_qid_path(msg.fid)
85+
86+ # Generate qids for each of the elements in the path, starting from the
87+ # path corresponding to the current fid.
88+ qids = []
89+ new_path = path.split("/")
90+ try:
91+ for element in msg.wname:
92+
93+ if element == "..":
94+ # Handle parent directories as path elements.
95+ if new_path: new_path.pop()
96+ else:
97+ new_path.append(element)
98+
99+ # Update the qid variable, create qids for each intermediate
100+ # path, and record the qids created.
101+ qid = store.make_qid("/".join(new_path))
102+ qids.append(qid)
103+
104+ except OSError:
105+ if len(qids) > 0:
106+ return styx.Rwalk(msg.tag, qids)
107+ else:
108+ return styx.Rerror(msg.tag, "Not found.")
109+
110+ # Set the qid and path for the newfid passed by the caller.
111+ store.set_qid_path(msg.newfid, qid, "/".join(new_path))
112+
113+ return styx.Rwalk(msg.tag, qids)
114+
115+ def Topen(self, conn, client, msg):
116+
117+ store = self.clients[client]
118+
119+ # The fid must have an existing qid.
120+ qid, path = store.get_qid_path(msg.fid)
121+
122+ if not store.open(msg.fid, msg.mode):
123+ return styx.Rerror(msg.tag, "Cannot open file.")
124+
125+ return styx.Ropen(msg.tag, qid, self.MAX_MSG_SIZE)
126+
127+ def Tcreate(self, conn, client, msg):
128+
129+ store = self.clients[client]
130+
131+ # The fid must have an existing qid that corresponds to a directory
132+ # and cannot be itself in use.
133+ qid, path = store.get_qid_path(msg.fid)
134+
135+ new_qid = store.create(msg.fid, msg.name, msg.perm)
136+ if new_qid == False:
137+ return styx.Rerror(msg.tag, "Cannot create file.")
138+
139+ # The fid now refers to the newly open file or directory.
140+ if not store.open(msg.fid, msg.mode):
141+ return styx.Rerror(msg.tag, "Cannot open file.")
142+
143+ return styx.Rcreate(msg.tag, new_qid, self.MAX_MSG_SIZE)
144+
145+ def Tread(self, conn, client, msg):
146+
147+ store = self.clients[client]
148+
149+ # The fid must have an existing qid.
150+ data = store.read(msg.fid, msg.offset, msg.count)
151+
152+ return styx.Rread(msg.tag, data)
153+
154+ def Twrite(self, conn, client, msg):
155+
156+ store = self.clients[client]
157+
158+ # The fid must have an existing qid.
159+ count = store.write(msg.fid, msg.offset, msg.data)
160+
161+ return styx.Rwrite(msg.tag, count)
162+
163+ def Tclunk(self, conn, client, msg):
164+
165+ store = self.clients[client]
166+
167+ # Release/free the qid.
168+ store.free_qid_path(msg.fid)
169+
170+ # Additionally, if the fid refers to the root of the file system then
171+ # remove the client from the clients dictionary to disconnect it.
172+ if msg.fid == store.root_fid:
173+ del self.clients[client]
174+
175+ return styx.Rclunk(msg.tag)
176+
177+ def Tremove(self, conn, client, msg):
178+
179+ store = self.clients[client]
180+
181+ # Remove the file and clunk it whether the remove was successful or not.
182+ result = store.remove(msg.fid)
183+
184+ if result == True:
185+ store.free_qid_path(msg.fid)
186+ return styx.Rremove(msg.tag)
187+ else:
188+ store.free_qid_path(msg.fid)
189+ return styx.Rerror(msg.tag, result)
190+
191+ def Tflush(self, conn, client, msg):
192+
193+ store = self.clients[client]
194+
195+ # If we supported a queue of commands then we would remove any pending
196+ # commands with tags matching msg.oldtag.
197+
198+ return styx.Rflush(msg.tag)
199+
200+ def Twstat(self, conn, client, msg):
201+
202+ store = self.clients[client]
203+
204+ s = store.wstat(msg.fid, msg.stat)
205+ return styx.Rwstat(msg.tag)
206+
207+ handlers = {
208+ styx.Tversion.code: Tversion,
209+ styx.Tattach.code: Tattach,
210+ styx.Tstat.code: Tstat,
211+ styx.Twalk.code: Twalk,
212+ styx.Topen.code: Topen,
213+ styx.Tcreate.code: Tcreate,
214+ styx.Tread.code: Tread,
215+ styx.Twrite.code: Twrite,
216+ styx.Tclunk.code: Tclunk,
217+ styx.Tremove.code: Tremove,
218+ styx.Twstat.code: Twstat
219+ }