Unified · Split
| @@ -69,7 +69,7 @@ class StyxFSServer: · 68 unmodified lines | |||
| 69 | qids = [] | 69 | qids = [] |
| 70 | try: | 70 | try: |
| 71 | for element in msg.wname: | 71 | for element in msg.wname: |
| 72 | path += element | 72 | path += "/" + element |
| 73 | qid = store.make_qid(path) | 73 | qid = store.make_qid(path) |
| 74 | qids.append(qid) | 74 | qids.append(qid) |
| 75 | 75 | ||
| @@ -90,8 +90,29 @@ class StyxFSServer: · 14 unmodified lines | |||
| 90 | # The fid must have an existing qid. | 90 | # The fid must have an existing qid. |
| 91 | qid, path = store.get_qid_path(msg.fid) | 91 | qid, path = store.get_qid_path(msg.fid) |
| 92 | 92 | ||
| 93 | if not store.open(msg.fid, msg.mode): | ||
| 94 | return styx.Rerror(msg.tag, "Cannot open file.") | ||
| 95 | |||
| 93 | return styx.Ropen(msg.tag, qid, self.MAX_MSG_SIZE) | 96 | return styx.Ropen(msg.tag, qid, self.MAX_MSG_SIZE) |
| 94 | 97 | ||
| 98 | def Tcreate(self, conn, client, msg): | ||
| 99 | |||
| 100 | store = self.clients[client] | ||
| 101 | |||
| 102 | # The fid must have an existing qid that corresponds to a directory | ||
| 103 | # and cannot be itself in use. | ||
| 104 | qid, path = store.get_qid_path(msg.fid) | ||
| 105 | |||
| 106 | new_qid = store.create(msg.fid, msg.name, msg.perm) | ||
| 107 | if new_qid == False: | ||
| 108 | return styx.Rerror(msg.tag, "Cannot create file.") | ||
| 109 | |||
| 110 | # The fid now refers to the newly open file or directory. | ||
| 111 | if not store.open(msg.fid, msg.mode): | ||
| 112 | return styx.Rerror(msg.tag, "Cannot open file.") | ||
| 113 | |||
| 114 | return styx.Rcreate(msg.tag, new_qid, self.MAX_MSG_SIZE) | ||
| 115 | |||
| 95 | def Tread(self, conn, client, msg): | 116 | def Tread(self, conn, client, msg): |
| 96 | 117 | ||
| 97 | store = self.clients[client] | 118 | store = self.clients[client] |
| @@ -101,6 +122,15 @@ class StyxFSServer: · 3 unmodified lines | |||
| 101 | 122 | ||
| 102 | return styx.Rread(msg.tag, data) | 123 | return styx.Rread(msg.tag, data) |
| 103 | 124 | ||
| 125 | def Twrite(self, conn, client, msg): | ||
| 126 | |||
| 127 | store = self.clients[client] | ||
| 128 | |||
| 129 | # The fid must have an existing qid. | ||
| 130 | count = store.write(msg.fid, msg.offset, msg.data) | ||
| 131 | |||
| 132 | return styx.Rwrite(msg.tag, count) | ||
| 133 | |||
| 104 | def Tclunk(self, conn, client, msg): | 134 | def Tclunk(self, conn, client, msg): |
| 105 | 135 | ||
| 106 | store = self.clients[client] | 136 | store = self.clients[client] |
| @@ -120,7 +150,9 @@ class StyxFSServer: · 13 unmodified lines | |||
| 120 | styx.Tstat.code: Tstat, | 150 | styx.Tstat.code: Tstat, |
| 121 | styx.Twalk.code: Twalk, | 151 | styx.Twalk.code: Twalk, |
| 122 | styx.Topen.code: Topen, | 152 | styx.Topen.code: Topen, |
| 153 | styx.Tcreate.code: Tcreate, | ||
| 123 | styx.Tread.code: Tread, | 154 | styx.Tread.code: Tread, |
| 155 | styx.Twrite.code: Twrite, | ||
| 124 | styx.Tclunk.code: Tclunk | 156 | styx.Tclunk.code: Tclunk |
| 125 | } | 157 | } |
| 126 | 158 | ||
| @@ -137,6 +169,7 @@ class FileStore: · 10 unmodified lines | |||
| 137 | self.dir = os.path.abspath(directory) | 169 | self.dir = os.path.abspath(directory) |
| 138 | self.qids = {} | 170 | self.qids = {} |
| 139 | self.paths = {} | 171 | self.paths = {} |
| 172 | self.opened = {} | ||
| 140 | 173 | ||
| 141 | def get_root_qid(self, fid, afid, uname, aname): | 174 | def get_root_qid(self, fid, afid, uname, aname): |
| 142 | 175 | ||
| @@ -150,6 +183,8 @@ class FileStore: · 7 unmodified lines | |||
| 150 | 183 | ||
| 151 | def set_qid_path(self, fid, qid, path): | 184 | def set_qid_path(self, fid, qid, path): |
| 152 | 185 | ||
| 186 | path = path.lstrip("/") | ||
| 187 | |||
| 153 | self.qids[fid] = qid | 188 | self.qids[fid] = qid |
| 154 | self.paths[fid] = path | 189 | self.paths[fid] = path |
| 155 | 190 | ||
| @@ -157,14 +192,16 @@ class FileStore: · 1 unmodified line | |||
| 157 | 192 | ||
| 158 | path = path.lstrip("/") | 193 | path = path.lstrip("/") |
| 159 | 194 | ||
| 160 | file_path = os.path.join(self.dir, path) | 195 | real_path = os.path.join(self.dir, path) |
| 161 | s = os.stat(file_path) | 196 | s = os.stat(real_path) |
| 162 | if os.path.isdir(file_path): | 197 | if os.path.isdir(real_path): |
| 163 | qtype = 0x80 | 198 | qtype = 0x80 |
| 164 | else: | 199 | else: |
| 165 | qtype = 0 | 200 | qtype = 0 |
| 166 | 201 | ||
| 167 | qversion = 0 | 202 | qversion = 0 |
| 203 | |||
| 204 | # Use the inode number to uniquely refer to the object at this location. | ||
| 168 | qpath = s.st_ino | 205 | qpath = s.st_ino |
| 169 | 206 | ||
| 170 | return (qtype, qversion, qpath) | 207 | return (qtype, qversion, qpath) |
| @@ -173,6 +210,9 @@ class FileStore: · 2 unmodified lines | |||
| 173 | 210 | ||
| 174 | del self.qids[fid] | 211 | del self.qids[fid] |
| 175 | del self.paths[fid] | 212 | del self.paths[fid] |
| 213 | |||
| 214 | if fid in self.opened: | ||
| 215 | del self.opened[fid] | ||
| 176 | 216 | ||
| 177 | def stat(self, fid): | 217 | def stat(self, fid): |
| 178 | 218 | ||
| @@ -182,6 +222,8 @@ class FileStore: · 3 unmodified lines | |||
| 182 | 222 | ||
| 183 | def _stat(self, qid, path): | 223 | def _stat(self, qid, path): |
| 184 | 224 | ||
| 225 | path = path.lstrip("/") | ||
| 226 | |||
| 185 | name = os.path.split(path)[1] | 227 | name = os.path.split(path)[1] |
| 186 | real_path = os.path.join(self.dir, path) | 228 | real_path = os.path.join(self.dir, path) |
| 187 | s = os.stat(real_path) | 229 | s = os.stat(real_path) |
| @@ -198,6 +240,64 @@ class FileStore: · 10 unmodified lines | |||
| 198 | return styx.Stat(0, 0, qid, mode, s.st_atime, s.st_mtime, size, | 240 | return styx.Stat(0, 0, qid, mode, s.st_atime, s.st_mtime, size, |
| 199 | name, "styxfs", "styxfs", "") | 241 | name, "styxfs", "styxfs", "") |
| 200 | 242 | ||
| 243 | def create(self, fid, name, perm): | ||
| 244 | |||
| 245 | if name in (".", ".."): | ||
| 246 | return False | ||
| 247 | |||
| 248 | elif fid in self.opened: | ||
| 249 | return False | ||
| 250 | else: | ||
| 251 | # Obtain the real path of the directory. | ||
| 252 | path = self.paths[fid] | ||
| 253 | real_path = os.path.join(self.dir, path) | ||
| 254 | |||
| 255 | if not os.path.isdir(real_path): | ||
| 256 | return False | ||
| 257 | |||
| 258 | # Read the directory permissions. | ||
| 259 | dir_perm = os.stat(real_path).st_mode | ||
| 260 | |||
| 261 | new_real_path = os.path.join(real_path, name) | ||
| 262 | |||
| 263 | if os.path.exists(new_real_path): | ||
| 264 | return False | ||
| 265 | |||
| 266 | # Create the file or directory with suitable permissions. | ||
| 267 | try: | ||
| 268 | if perm & styx.Stat.DMDIR: | ||
| 269 | # Only pass the lowest permission bits through to the | ||
| 270 | # underlying filing system. | ||
| 271 | mode = dir_perm & 0777 | ||
| 272 | os.mkdir(new_real_path, mode) | ||
| 273 | else: | ||
| 274 | mode = dir_perm & 0666 | ||
| 275 | os.mknod(new_real_path, mode, stat.S_IFREG) | ||
| 276 | |||
| 277 | except OSError: | ||
| 278 | return False | ||
| 279 | |||
| 280 | # Update the fid to refer to the new object. | ||
| 281 | qid = self.make_qid(path + "/" + name) | ||
| 282 | self.set_qid_path(fid, qid, path + "/" + name) | ||
| 283 | |||
| 284 | return qid | ||
| 285 | |||
| 286 | def open(self, fid, mode): | ||
| 287 | |||
| 288 | if fid in self.opened: | ||
| 289 | self.opened[fid] = mode | ||
| 290 | if mode & styx.Stat.DMEXCL: | ||
| 291 | return False | ||
| 292 | else: | ||
| 293 | self.opened[fid] = mode | ||
| 294 | |||
| 295 | return True | ||
| 296 | |||
| 297 | def is_opened(self, fid): | ||
| 298 | |||
| 299 | return fid in self.opened | ||
| 300 | |||
| 201 | def read(self, fid, offset, count): | 301 | def read(self, fid, offset, count): |
| 202 | 302 | ||
| 203 | path = self.paths[fid] | 303 | path = self.paths[fid] |
| @@ -205,12 +305,15 @@ class FileStore: · 1 unmodified line | |||
| 205 | data = "" | 305 | data = "" |
| 206 | 306 | ||
| 207 | if os.path.isdir(real_path): | 307 | if os.path.isdir(real_path): |
| 308 | |||
| 309 | # Iterate over a sorted list of files in the directory, constructing | ||
| 310 | # a byte string of information about them that can be sent in chunks. | ||
| 208 | files = os.listdir(real_path) | 311 | files = os.listdir(real_path) |
| 209 | files.sort() | 312 | files.sort() |
| 210 | 313 | ||
| 211 | for file_name in files: | 314 | for file_name in files: |
| 212 | qid = self.make_qid(path + "/" + file_name) | 315 | qid = self.make_qid(path + "/" + file_name) |
| 213 | data += self._stat(qid, file_name).encode() | 316 | data += self._stat(qid, path + "/" + file_name).encode() |
| 214 | 317 | ||
| 215 | return data[offset:offset + count] | 318 | return data[offset:offset + count] |
| 216 | else: | 319 | else: |
| @@ -220,6 +323,21 @@ class FileStore: · 3 unmodified lines | |||
| 220 | f.close() | 323 | f.close() |
| 221 | 324 | ||
| 222 | return data | 325 | return data |
| 326 | |||
| 327 | def write(self, fid, offset, data): | ||
| 328 | |||
| 329 | path = self.paths[fid] | ||
| 330 | real_path = os.path.join(self.dir, path) | ||
| 331 | |||
| 332 | if os.path.isdir(real_path): | ||
| 333 | return styx.Rerror(msg.tag, "Not a file.") | ||
| 334 | |||
| 335 | f = open(real_path, "r+wb") | ||
| 336 | f.seek(offset) | ||
| 337 | f.write(data) | ||
| 338 | f.close() | ||
| 339 | |||
| 340 | return len(data) | ||
| 223 | 341 | ||
| 224 | 342 | ||
| 225 | if __name__ == "__main__": | 343 | if __name__ == "__main__": |