1 // Written in the D programming language. 2 3 /** 4 This module contains a set of various useful functions for more convenient work with 9P / Styx messages. 5 Note: This module contains helpers for building all types of messages (and some other stuff), except stat (R-message) and wstat (T-message), since these messages have a complex structure and must be processed directly in the code that forms them. 6 7 Copyright: LightHouse Software, 2021 8 License: $(HTTP https://github.com/aquaratixc/ESL-License, Experimental Software License 1.0). 9 Authors: Oleg Bakharev, 10 Ilya Pertsev 11 */ 12 module styx2000.extrautil.styxmessage; 13 14 private { 15 import styx2000.extrautil.casts; 16 17 import styx2000.protoconst; 18 19 import styx2000.protobj; 20 } 21 22 /// Convenient aliases 23 alias StyxMessage = StyxObject[]; 24 25 /// Convenient alias for array of styx messages (not described in 9P / Styx protocol) 26 alias StyxMail = StyxMessage[]; 27 28 /// StyxMessage is client message ? 29 bool isTmessage(StyxMessage msg) 30 { 31 bool isMessage = false; 32 Type type = cast(Type) msg[1]; 33 34 if (type !is null) 35 { 36 with (STYX_MESSAGE_TYPE) 37 { 38 switch(type.getType) 39 { 40 // version 41 case T_VERSION: 42 isMessage = true; 43 break; 44 // auth 45 case T_AUTH: 46 isMessage = true; 47 break; 48 // flush 49 case T_FLUSH: 50 isMessage = true; 51 break; 52 // attach 53 case T_ATTACH: 54 isMessage = true; 55 break; 56 // walk 57 case T_WALK: 58 isMessage = true; 59 break; 60 case T_OPEN: 61 isMessage = true; 62 break; 63 // create 64 case T_CREATE: 65 isMessage = true; 66 break; 67 // read 68 case T_READ: 69 isMessage = true; 70 break; 71 // write 72 case T_WRITE: 73 isMessage = true; 74 break; 75 // clunk 76 case T_CLUNK: 77 isMessage = true; 78 break; 79 // remove 80 case T_REMOVE: 81 isMessage = true; 82 break; 83 // stat 84 case T_STAT: 85 isMessage = true; 86 break; 87 // wstat 88 case T_WSTAT: 89 isMessage = true; 90 break; 91 default: 92 break; 93 } 94 } 95 } 96 97 return isMessage; 98 } 99 100 /// StyxMessage is server message ? 101 bool isRmessage(StyxMessage msg) 102 { 103 bool isMessage = false; 104 Type type = cast(Type) msg[1]; 105 106 if (type !is null) 107 { 108 with (STYX_MESSAGE_TYPE) 109 { 110 switch(type.getType) 111 { 112 // version 113 case R_VERSION: 114 isMessage = true; 115 break; 116 // auth 117 case R_AUTH: 118 isMessage = true; 119 break; 120 // flush 121 case R_FLUSH: 122 isMessage = true; 123 break; 124 // attach 125 case R_ATTACH: 126 isMessage = true; 127 break; 128 // walk 129 case R_WALK: 130 isMessage = true; 131 break; 132 case R_OPEN: 133 isMessage = true; 134 break; 135 // create 136 case R_CREATE: 137 isMessage = true; 138 break; 139 // read 140 case R_READ: 141 isMessage = true; 142 break; 143 // write 144 case R_WRITE: 145 isMessage = true; 146 break; 147 // clunk 148 case R_CLUNK: 149 isMessage = true; 150 break; 151 // remove 152 case R_REMOVE: 153 isMessage = true; 154 break; 155 // stat 156 case R_STAT: 157 isMessage = true; 158 break; 159 // wstat 160 case R_WSTAT: 161 isMessage = true; 162 break; 163 // error 164 case R_ERROR: 165 isMessage = true; 166 break; 167 default: 168 break; 169 } 170 } 171 } 172 173 return isMessage; 174 } 175 176 /// StyxMessage is error message ? 177 bool isErrorMessage(StyxMessage msg) 178 { 179 bool isMessage = false; 180 Type type = cast(Type) msg[1]; 181 182 if (type !is null) 183 { 184 if (type.getType == STYX_MESSAGE_TYPE.R_ERROR) 185 { 186 isMessage = true; 187 } 188 } 189 190 return isMessage; 191 } 192 193 /// Create base header for 9P / Styx messages 194 auto createHeader(uint size = 0, STYX_MESSAGE_TYPE type = STYX_MESSAGE_TYPE.R_ERROR, ushort tag = STYX_NOTAG) 195 { 196 return cast(StyxMessage) [ 197 new Size(size), 198 new Type(type), 199 new Tag(tag) 200 ]; 201 } 202 203 /// Parse header of Styx message. Returns anonymous struct that contains size, type and tag fields. 204 auto extractHeader(StyxMessage msg) 205 { 206 struct StyxHeader { 207 /// message size 208 uint size; 209 /// message type 210 STYX_MESSAGE_TYPE type; 211 /// tag 212 ushort tag; 213 } 214 215 StyxHeader header = StyxHeader(0, STYX_MESSAGE_TYPE.R_ERROR, STYX_NOTAG); 216 217 if (msg.length >= 3) 218 { 219 auto size = msg[0].toSize; 220 221 if (size !is null) 222 { 223 header.size = size.getSize; 224 } 225 226 auto type = msg[1].toType; 227 228 if (type !is null) 229 { 230 header.type = type.getType; 231 } 232 233 auto tag = msg[2].toTag; 234 235 if (tag !is null) 236 { 237 header.tag = tag.getTag; 238 } 239 } 240 241 return header; 242 } 243 244 /// Create version message from client 245 auto createTmsgVersion(ushort tag = STYX_NOTAG, uint maximalSize = 8192, string vers = STYX_VERSION) 246 { 247 return createHeader(0, STYX_MESSAGE_TYPE.T_VERSION, tag) ~ cast(StyxMessage) [ 248 new Msize(maximalSize), 249 new Version(vers) 250 ]; 251 } 252 253 /// Create version message from server 254 auto createRmsgVersion(ushort tag = STYX_NOTAG, uint maximalSize = 8192, string vers = STYX_VERSION) 255 { 256 return createHeader(0, STYX_MESSAGE_TYPE.R_VERSION, tag) ~ cast(StyxMessage) [ 257 new Msize(maximalSize), 258 new Version(vers) 259 ]; 260 } 261 262 /// Create auth message from client 263 auto createTmsgAuth(ushort tag = STYX_NOTAG, uint afid = STYX_NOFID, string uname = "", string aname = "") 264 { 265 return createHeader(0, STYX_MESSAGE_TYPE.T_AUTH, tag) ~ cast(StyxMessage) [ 266 new Afid(afid), 267 new Uname(uname), 268 new Aname(aname) 269 ]; 270 } 271 272 /// Create auth message from server 273 auto createRmsgAuth(ushort tag = STYX_NOTAG, STYX_QID_TYPE type = STYX_QID_TYPE.QTFILE, uint vers = 0, ulong path = 0) 274 { 275 return createHeader(0, STYX_MESSAGE_TYPE.R_AUTH, tag) ~ cast(StyxMessage) [ 276 new Aqid(type, vers, path) 277 ]; 278 } 279 280 /// Create attach message from client 281 auto createTmsgAttach(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID, uint afid = STYX_NOFID, string uname = "", string aname = "") 282 { 283 return createHeader(0, STYX_MESSAGE_TYPE.T_ATTACH, tag) ~ cast(StyxMessage) [ 284 new Fid(fid), 285 new Afid(afid), 286 new Uname(uname), 287 new Aname(aname) 288 ]; 289 } 290 291 /// Create attach message from server 292 auto createRmsgAttach(ushort tag = STYX_NOTAG, STYX_QID_TYPE type = STYX_QID_TYPE.QTFILE, uint vers = 0, ulong path = 0) 293 { 294 return createHeader(0, STYX_MESSAGE_TYPE.R_ATTACH, tag) ~ cast(StyxMessage) [ 295 new Qid(type, vers, path) 296 ]; 297 } 298 299 /// Create error message from server (for client this type of message does not exists) 300 auto createRmsgError(ushort tag = STYX_NOTAG, string ename = "") 301 { 302 return createHeader(0, STYX_MESSAGE_TYPE.R_ERROR, tag) ~ cast(StyxMessage) [ 303 new Ename(ename) 304 ]; 305 } 306 307 /// Create clunk message from client 308 auto createTmsgClunk(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID) 309 { 310 return createHeader(0, STYX_MESSAGE_TYPE.T_CLUNK, tag) ~ cast(StyxMessage) [ 311 new Fid(fid) 312 ]; 313 } 314 315 /// Create clunk message from server 316 auto createRmsgClunk(ushort tag = STYX_NOTAG) 317 { 318 return createHeader(0, STYX_MESSAGE_TYPE.R_CLUNK, tag); 319 } 320 321 /// Create flush message from client 322 auto createTmsgFlush(ushort tag = STYX_NOTAG, ushort oldTag = STYX_NOTAG) 323 { 324 return createHeader(0, STYX_MESSAGE_TYPE.T_FLUSH, tag) ~ cast(StyxMessage) [ 325 new OldTag(oldTag) 326 ]; 327 } 328 329 /// Create flush message from server 330 auto createRmsgFlush(ushort tag = STYX_NOTAG) 331 { 332 return createHeader(0, STYX_MESSAGE_TYPE.R_FLUSH, tag); 333 } 334 335 /// Create remove message from client 336 auto createTmsgRemove(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID) 337 { 338 return createHeader(0, STYX_MESSAGE_TYPE.T_REMOVE, tag) ~ cast(StyxMessage) [ 339 new Fid(fid) 340 ]; 341 } 342 343 /// Create remove message from server 344 auto createRmsgRemove(ushort tag = STYX_NOTAG) 345 { 346 return createHeader(0, STYX_MESSAGE_TYPE.R_REMOVE, tag); 347 } 348 349 /// Create open message from client 350 auto createTmsgOpen(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID, STYX_FILE_MODE mode = STYX_FILE_MODE.OREAD) 351 { 352 return createHeader(0, STYX_MESSAGE_TYPE.T_OPEN, tag) ~ cast(StyxMessage) [ 353 new Fid(fid), 354 new Mode(mode) 355 ]; 356 } 357 358 /// Create open message from server 359 auto createRmsgOpen(ushort tag = STYX_NOTAG, STYX_QID_TYPE type = STYX_QID_TYPE.QTFILE, uint vers = 0, ulong path = 0, uint iounit = 8164) 360 { 361 return createHeader(0, STYX_MESSAGE_TYPE.R_OPEN, tag) ~ cast(StyxMessage) [ 362 new Qid(type, vers, path), 363 new Iounit(iounit) 364 ]; 365 } 366 367 /// Create create message from client 368 auto createTmsgCreate(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID, STYX_FILE_MODE mode = STYX_FILE_MODE.OREAD, STYX_FILE_PERMISSION[] perms = [STYX_FILE_PERMISSION.OWNER_READ, STYX_FILE_PERMISSION.OWNER_WRITE, STYX_FILE_PERMISSION.OWNER_EXEC]) 369 { 370 auto perm = new Perm; 371 perm.setPerm(perms); 372 return createHeader(0, STYX_MESSAGE_TYPE.T_CREATE, tag) ~ cast(StyxMessage) [ 373 new Fid(fid), 374 perm, 375 new Mode(mode) 376 ]; 377 } 378 379 /// Create create message from server 380 auto createRmsgCreate(ushort tag = STYX_NOTAG, STYX_QID_TYPE type = STYX_QID_TYPE.QTFILE, uint vers = 0, ulong path = 0, uint iounit = 8164) 381 { 382 return createHeader(0, STYX_MESSAGE_TYPE.R_CREATE, tag) ~ cast(StyxMessage) [ 383 new Qid(type, vers, path), 384 new Iounit(iounit) 385 ]; 386 } 387 388 /// Create read message from client 389 auto createTmsgRead(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID, ulong offset = 0, uint count = 0) 390 { 391 return createHeader(0, STYX_MESSAGE_TYPE.T_READ, tag) ~ cast(StyxMessage) [ 392 new Fid(fid), 393 new Offset(offset), 394 new Count(count) 395 ]; 396 } 397 398 /// Create read message from server 399 auto createRmsgRead(ushort tag = STYX_NOTAG, uint count = 0, ubyte[] data = []) 400 { 401 return createHeader(0, STYX_MESSAGE_TYPE.R_READ, tag) ~ cast(StyxMessage) [ 402 new Count(count), 403 new Data(data) 404 ]; 405 } 406 407 /// Create write message from client 408 auto createTmsgWrite(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID, ulong offset = 0, uint count = 0, ubyte[] data = []) 409 { 410 return createHeader(0, STYX_MESSAGE_TYPE.T_WRITE, tag) ~ cast(StyxMessage) [ 411 new Fid(fid), 412 new Offset(offset), 413 new Count(count), 414 new Data(data) 415 ]; 416 } 417 418 /// Create write message from server 419 auto createRmsgWrite(ushort tag = STYX_NOTAG, uint count = 0) 420 { 421 return createHeader(0, STYX_MESSAGE_TYPE.R_WRITE, tag) ~ cast(StyxMessage) [ 422 new Count(count) 423 ]; 424 } 425 426 427 /// Create walk message from client 428 auto createTmsgWalk(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID, uint newFid = STYX_NOFID, string[] nwname = []) 429 { 430 return createHeader(0, STYX_MESSAGE_TYPE.T_WALK, tag) ~ cast(StyxMessage) [ 431 new Fid(fid), 432 new NewFid(newFid), 433 new Nwname(nwname) 434 ]; 435 } 436 437 438 /// Create walk message from server 439 auto createRmsgWalk(ushort tag = STYX_NOTAG, Qid[] nwqid = []) 440 { 441 return createHeader(0, STYX_MESSAGE_TYPE.R_WALK, tag) ~ cast(StyxMessage) [ 442 new Nwqid(nwqid) 443 ]; 444 } 445 446 447 /// Create stat message from client 448 auto createTmsgStat(ushort tag = STYX_NOTAG, uint fid = STYX_NOFID) 449 { 450 return createHeader(0, STYX_MESSAGE_TYPE.T_STAT, tag) ~ cast(StyxMessage) [ 451 new Fid(fid) 452 ]; 453 } 454 455 456 /// Create wstat message from server 457 auto createRmsgWstat(ushort tag = STYX_NOTAG) 458 { 459 return createHeader(0, STYX_MESSAGE_TYPE.R_WSTAT, tag); 460 } 461 462 /// Calculate real (!) message size (in bytes) 463 auto calculateMessageSize(StyxMessage msg) 464 { 465 uint realSize = 0; 466 467 foreach (e; msg) 468 { 469 realSize += cast(uint) e.pack.length; 470 } 471 472 return realSize; 473 } 474 475 476 /// Fix incorrect size in some messages which generated by helpers (fix zero sizes) 477 auto fixMessageSize(StyxMessage msg) 478 { 479 StyxMessage newMsg = msg[1..$]; 480 uint realSize = 4; // Size field has length = 4 bytes 481 482 foreach (e; newMsg) 483 { 484 realSize += cast(uint) e.pack.length; 485 } 486 487 return cast(StyxMessage) [new Size(realSize)] ~ newMsg; 488 }