Unified · Split
| @@ -69,7 +69,7 @@ class StyxFSServer: · 68 unmodified lines | ||
| 69 | 69 | qids = [] |
| 70 | 70 | try: |
| 71 | 71 | for element in msg.wname: |
| 72 | − path += element | |
| 72 | + path += "/" + element | |
| 73 | 73 | qid = store.make_qid(path) |
| 74 | 74 | qids.append(qid) |
| 75 | 75 | |
| @@ -90,8 +90,29 @@ class StyxFSServer: · 14 unmodified lines | ||
| 90 | 90 | # The fid must have an existing qid. |
| 91 | 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 | 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 | 116 | def Tread(self, conn, client, msg): |
| 96 | 117 | |
| 97 | 118 | store = self.clients[client] |
| @@ -101,6 +122,15 @@ class StyxFSServer: · 3 unmodified lines | ||
| 101 | 122 | |
| 102 | 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 | 134 | def Tclunk(self, conn, client, msg): |
| 105 | 135 | |
| 106 | 136 | store = self.clients[client] |
| @@ -120,7 +150,9 @@ class StyxFSServer: · 13 unmodified lines | ||
| 120 | 150 | styx.Tstat.code: Tstat, |
| 121 | 151 | styx.Twalk.code: Twalk, |
| 122 | 152 | styx.Topen.code: Topen, |
| 153 | + styx.Tcreate.code: Tcreate, | |
| 123 | 154 | styx.Tread.code: Tread, |
| 155 | + styx.Twrite.code: Twrite, | |
| 124 | 156 | styx.Tclunk.code: Tclunk |
| 125 | 157 | } |
| 126 | 158 | |
| @@ -137,6 +169,7 @@ class FileStore: · 10 unmodified lines | ||
| 137 | 169 | self.dir = os.path.abspath(directory) |
| 138 | 170 | self.qids = {} |
| 139 | 171 | self.paths = {} |
| 172 | + self.opened = {} | |
| 140 | 173 | |
| 141 | 174 | def get_root_qid(self, fid, afid, uname, aname): |
| 142 | 175 | |
| @@ -150,6 +183,8 @@ class FileStore: · 7 unmodified lines | ||
| 150 | 183 | |
| 151 | 184 | def set_qid_path(self, fid, qid, path): |
| 152 | 185 | |
| 186 | + path = path.lstrip("/") | |
| 187 | + | |
| 153 | 188 | self.qids[fid] = qid |
| 154 | 189 | self.paths[fid] = path |
| 155 | 190 | |
| @@ -157,14 +192,16 @@ class FileStore: · 1 unmodified line | ||
| 157 | 192 | |
| 158 | 193 | path = path.lstrip("/") |
| 159 | 194 | |
| 160 | − file_path = os.path.join(self.dir, path) | |
| 161 | − s = os.stat(file_path) | |
| 162 | − if os.path.isdir(file_path): | |
| 195 | + real_path = os.path.join(self.dir, path) | |
| 196 | + s = os.stat(real_path) | |
| 197 | + if os.path.isdir(real_path): | |
| 163 | 198 | qtype = 0x80 |
| 164 | 199 | else: |
| 165 | 200 | qtype = 0 |
| 166 | 201 | |
| 167 | 202 | qversion = 0 |
| 203 | + | |
| 204 | + # Use the inode number to uniquely refer to the object at this location. | |
| 168 | 205 | qpath = s.st_ino |
| 169 | 206 | |
| 170 | 207 | return (qtype, qversion, qpath) |
| @@ -173,6 +210,9 @@ class FileStore: · 2 unmodified lines | ||
| 173 | 210 | |
| 174 | 211 | del self.qids[fid] |
| 175 | 212 | del self.paths[fid] |
| 213 | + | |
| 214 | + if fid in self.opened: | |
| 215 | + del self.opened[fid] | |
| 176 | 216 | |
| 177 | 217 | def stat(self, fid): |
| 178 | 218 | |
| @@ -182,6 +222,8 @@ class FileStore: · 3 unmodified lines | ||
| 182 | 222 | |
| 183 | 223 | def _stat(self, qid, path): |
| 184 | 224 | |
| 225 | + path = path.lstrip("/") | |
| 226 | + | |
| 185 | 227 | name = os.path.split(path)[1] |
| 186 | 228 | real_path = os.path.join(self.dir, path) |
| 187 | 229 | s = os.stat(real_path) |
| @@ -198,6 +240,64 @@ class FileStore: · 10 unmodified lines | ||
| 198 | 240 | return styx.Stat(0, 0, qid, mode, s.st_atime, s.st_mtime, size, |
| 199 | 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 | 301 | def read(self, fid, offset, count): |
| 202 | 302 | |
| 203 | 303 | path = self.paths[fid] |
| @@ -205,12 +305,15 @@ class FileStore: · 1 unmodified line | ||
| 205 | 305 | data = "" |
| 206 | 306 | |
| 207 | 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 | 311 | files = os.listdir(real_path) |
| 209 | 312 | files.sort() |
| 210 | 313 | |
| 211 | 314 | for file_name in files: |
| 212 | 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 | 318 | return data[offset:offset + count] |
| 216 | 319 | else: |
| @@ -220,6 +323,21 @@ class FileStore: · 3 unmodified lines | ||
| 220 | 323 | f.close() |
| 221 | 324 | |
| 222 | 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 | 343 | if __name__ == "__main__": |