|
|
1.1 ! root 1: /****************************************************************************** ! 2: * Copyright (c) 2011 IBM Corporation ! 3: * All rights reserved. ! 4: * This program and the accompanying materials ! 5: * are made available under the terms of the BSD License ! 6: * which accompanies this distribution, and is available at ! 7: * http://www.opensource.org/licenses/bsd-license.php ! 8: * ! 9: * Contributors: ! 10: * IBM Corporation - initial implementation ! 11: *****************************************************************************/ ! 12: ! 13: #include <stdio.h> ! 14: #include <stdint.h> ! 15: #include <string.h> ! 16: #include <byteorder.h> ! 17: #include "p9.h" ! 18: ! 19: ! 20: /* Protocol stack marshaling. */ ! 21: uint8_t *sp; ! 22: ! 23: #define GET_08(s,i) (s)[(i)] ! 24: #define GET_16(s,i) le16_to_cpu(*(uint16_t*)(&(s)[(i)])) ! 25: #define GET_32(s,i) le32_to_cpu(*(uint32_t*)(&(s)[(i)])) ! 26: #define GET_64(s,i) le64_to_cpu(*(uint64_t*)(&(s)[(i)])) ! 27: ! 28: #define SET_08(s,i,v) (s)[(i)] = (v) ! 29: #define SET_16(s,i,v) *(uint16_t*)(&(s)[(i)]) = cpu_to_le16(v) ! 30: #define SET_32(s,i,v) *(uint32_t*)(&(s)[(i)]) = cpu_to_le32(v) ! 31: #define SET_64(s,i,v) *(uint64_t*)(&(s)[(i)]) = cpu_to_le64(v) ! 32: ! 33: #define PUT_08(v) sp[0] = (v);sp+=1 ! 34: #define PUT_16(v) *(uint16_t*)(&sp[0]) = cpu_to_le16(v);sp+=2 ! 35: #define PUT_32(v) *(uint32_t*)(&sp[0]) = cpu_to_le32(v);sp+=4 ! 36: #define PUT_64(v) *(uint64_t*)(&sp[0]) = cpu_to_le64(v);sp+=8 ! 37: ! 38: #define PUT_HD(m,t) PUT_32(0);PUT_08(m);PUT_16(t) ! 39: #define PUT_SN(v,n) PUT_16(n);memcpy(sp,(v),(n));sp+=n ! 40: #define PUT_ST(v) PUT_16(strlen(v));memcpy(sp,(v),strlen(v));\ ! 41: sp+=strlen(v) ! 42: ! 43: #define GET_SIZE (sp - tx) ! 44: ! 45: ! 46: /* General defines. */ ! 47: #define MIN(a,b) ((a)>(b)?(b):(a)) ! 48: ! 49: #define NOTAG ((uint16_t)~0) ! 50: #define NOFID ((uint32_t)~0) ! 51: #define TAG 1 ! 52: #define BUF_SIZE (8*1024) ! 53: ! 54: #define VERSION "9P2000.u" ! 55: #define UNKNOWN_VER "unknown" ! 56: ! 57: #define MSG_SIZE 0 ! 58: #define MSG_ID 4 ! 59: #define MSG_ERR 0x6b ! 60: #define MSG_ERR_STR 9 ! 61: #define MSG_ERR_STR_LEN 7 ! 62: #define MSG_TAG 5 ! 63: #define MSG_VER_MSIZE 7 ! 64: #define MSG_VER_STR_LEN 11 ! 65: #define MSG_VER_STR 13 ! 66: #define MSG_WALK_TX_ELMT 15 ! 67: #define MSG_WALK_RX_ELMT 7 ! 68: #define MSG_SIZE 0 ! 69: #define MSG_WALK_MAX_ELMT 16 ! 70: #define MSG_QID_SIZE 13 ! 71: #define MSG_WALK_RX_HDR_SIZE 9 ! 72: #define MSG_OPEN_IOUNIT 20 ! 73: #define MSG_OPEN_MODE_MASK 0x5f ! 74: #define MSG_READ_COUNT 7 ! 75: #define MSG_READ_DATA 11 ! 76: #define MSG_STAT_LEN 42 ! 77: #define MSG_STAT_TYPE 17 ! 78: ! 79: #define T_VERSION 100 ! 80: #define R_VERSION (T_VERSION + 1) ! 81: #define T_ATTACH 104 ! 82: #define R_ATTACH (T_ATTACH + 1) ! 83: #define T_ERROR 106 ! 84: #define R_ERROR (T_ERROR + 1) ! 85: #define T_WALK 110 ! 86: #define R_WALK (T_WALK + 1) ! 87: #define T_OPEN 112 ! 88: #define R_OPEN (T_OPEN + 1) ! 89: #define T_READ 116 ! 90: #define R_READ (T_READ + 1) ! 91: #define T_CLUNK 120 ! 92: #define R_CLUNK (T_CLUNK + 1) ! 93: #define T_STAT 124 ! 94: #define R_STAT (T_STAT + 1) ! 95: ! 96: static p9_transact_t transact; ! 97: static void *transact_opaque; ! 98: static uint8_t *tx; ! 99: static uint8_t *rx; ! 100: ! 101: ! 102: /** ! 103: * p9_reg_transport ! 104: * ! 105: * Registers a transport function for use by the P9 protocol. The transport ! 106: * connects the P9 Client (this library) to a server instance. ! 107: * ! 108: * @param transact_func[in] Function pointer to type p9_transact_t. ! 109: * @param tx_buffer[in] TX buffer, must be 8k in size. ! 110: * @param rx_buffer[in] RX buffer, must be 8k in size. ! 111: */ ! 112: void p9_reg_transport(p9_transact_t transact_func, void *opaque, ! 113: uint8_t *tx_buffer, uint8_t *rx_buffer) ! 114: { ! 115: transact = transact_func; ! 116: transact_opaque = opaque; ! 117: tx = tx_buffer; ! 118: rx = rx_buffer; ! 119: } ! 120: ! 121: /** ! 122: * reset_buffers ! 123: * ! 124: * Reset the RX and TX buffers to BUF_SIZE (8k) and reset the Stack Pointer ! 125: * for the TX buffer, which is referenced by the PUT_* macro's. ! 126: */ ! 127: void reset_buffers(void) ! 128: { ! 129: memset(tx, 0, BUF_SIZE); ! 130: memset(rx, 0, BUF_SIZE); ! 131: sp = tx; ! 132: } ! 133: ! 134: /** ! 135: * p9_transaction ! 136: * ! 137: * Perform a transaction (send/recv) over the registered transport. ! 138: * ! 139: * @param connection[in|out] Connection object. ! 140: * @return 0 = success, -ve = error. ! 141: */ ! 142: int p9_transaction(p9_connection_t *connection) ! 143: { ! 144: int rc; ! 145: int tx_size = GET_SIZE; ! 146: int rx_size = connection->message_size; ! 147: ! 148: if (transact == NULL) { ! 149: return P9_NO_TRANSPORT; ! 150: } ! 151: if (tx == NULL || rx == NULL) { ! 152: return P9_NO_BUFFER; ! 153: } ! 154: if (connection->message_size > BUF_SIZE) { ! 155: return P9_MSG_SIZE_TOO_BIG; ! 156: } ! 157: if (tx_size > connection->message_size) { ! 158: return P9_MSG_TOO_LONG; ! 159: } ! 160: ! 161: SET_32(tx, MSG_SIZE, tx_size); ! 162: rc = transact(transact_opaque, tx, tx_size, rx, &rx_size); ! 163: ! 164: if (rc != 0) { ! 165: return P9_TRANSPORT_ERROR; ! 166: } ! 167: if (GET_16(tx, MSG_TAG) != GET_16(rx, MSG_TAG)) { ! 168: return P9_UNEXPECTED_TAG; ! 169: } ! 170: if (GET_08(rx, MSG_ID) == MSG_ERR) { ! 171: char error_string[200]; ! 172: ! 173: memset(error_string, 0, 200); ! 174: strncpy(error_string, (char *)&rx[MSG_ERR_STR], ! 175: MIN(200 - 1, GET_16(rx, MSG_ERR_STR_LEN))); ! 176: #ifndef TEST ! 177: printf("\nError: %s\n", error_string); ! 178: #endif ! 179: return P9_R_ERROR; ! 180: } ! 181: if ((GET_08(tx, MSG_ID) + 1) != GET_08(rx, MSG_ID)) { ! 182: return P9_UNEXPECTED_MSG; ! 183: } ! 184: ! 185: return 0; ! 186: } ! 187: ! 188: /** ! 189: * p9_version ! 190: * ! 191: * Called to start a session. Negotiates the maximum message size for the ! 192: * P9 protocol. ! 193: * ! 194: * @param connection[in|out] Connection object, contains message_size. ! 195: * @return 0 = success, -ve = error. ! 196: * ! 197: * @remark ! 198: * size[4] Tversion tag[2] msize[4] version[s] ! 199: * size[4] Rversion tag[2] msize[4] version[s] ! 200: */ ! 201: int p9_version(p9_connection_t *connection) ! 202: { ! 203: int rc; ! 204: char *ver_str; ! 205: int ver_len; ! 206: ! 207: reset_buffers(); ! 208: ! 209: /* Build message. */ ! 210: PUT_HD(T_VERSION, NOTAG); ! 211: PUT_32(connection->message_size); ! 212: PUT_ST(VERSION); ! 213: ! 214: /* Send message. */ ! 215: rc = p9_transaction(connection); ! 216: if (rc != 0) { ! 217: return rc; ! 218: } ! 219: ! 220: /* Handle response. */ ! 221: connection->message_size = MIN(connection->message_size, ! 222: GET_32(rx, MSG_VER_MSIZE)); ! 223: ! 224: ver_str = (char *)&rx[MSG_VER_STR]; ! 225: ver_len = GET_16(rx, MSG_VER_STR_LEN); ! 226: if (strncmp(UNKNOWN_VER, ver_str, ver_len) == 0) { ! 227: return P9_UNKNOWN_VERSION; ! 228: } ! 229: ! 230: ! 231: return 0; ! 232: } ! 233: ! 234: /** ! 235: * p9_attach ! 236: * ! 237: * Called to open a connection for a user to a file tree on the server. There ! 238: * is no authorisation undertaken (NOFID). ! 239: * ! 240: * @param connection[in|out] Connection object, contains uname and aname as ! 241: * well as the connection fid and returned qid. ! 242: * @return 0 = success, -ve = error. ! 243: * ! 244: * @remark ! 245: * size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] ! 246: * size[4] Rattach tag[2] qid[13] ! 247: */ ! 248: int p9_attach(p9_connection_t *connection) ! 249: { ! 250: int rc; ! 251: int length = 19 + strlen(connection->uname) + strlen(connection->aname); ! 252: ! 253: if (length > connection->message_size) { ! 254: return P9_MSG_TOO_LONG; ! 255: } ! 256: ! 257: reset_buffers(); ! 258: ! 259: /* Build message. */ ! 260: PUT_HD(T_ATTACH, TAG); ! 261: PUT_32(connection->fid); ! 262: PUT_32(NOFID); ! 263: PUT_ST(connection->uname); ! 264: PUT_ST(connection->aname); ! 265: PUT_32(~0); /* ??? */ ! 266: ! 267: /* Send message. */ ! 268: rc = p9_transaction(connection); ! 269: if (rc != 0) { ! 270: return rc; ! 271: } ! 272: ! 273: ! 274: return 0; ! 275: } ! 276: ! 277: /** ! 278: * p9_clunk ! 279: * ! 280: * Called when closing a file or connection (or after failed opens). Tells the ! 281: * server that the supplied fid is no longer needed by this client. ! 282: * ! 283: * @param connection[in|out] Connection object. ! 284: * @param fid[in] Fid to be clunked (released) on the server. ! 285: * @return 0 = success, -ve = error. ! 286: * ! 287: * @remark ! 288: * size[4] Tclunk tag[2] fid[4] ! 289: * size[4] Rclunk tag[2] ! 290: */ ! 291: int p9_clunk(p9_connection_t *connection, uint32_t fid) ! 292: { ! 293: int rc; ! 294: ! 295: reset_buffers(); ! 296: ! 297: /* Build message. */ ! 298: PUT_HD(T_CLUNK, TAG); ! 299: PUT_32(fid); ! 300: ! 301: /* Send message. */ ! 302: rc = p9_transaction(connection); ! 303: if (rc != 0) { ! 304: return rc; ! 305: } ! 306: ! 307: ! 308: return 0; ! 309: } ! 310: ! 311: /** ! 312: * p9_walk ! 313: * ! 314: * Walk the provided path to a file (or directory) starting at the directory ! 315: * indicated by fid and assigning new_fid to the last successfully walked ! 316: * element. If not all elements of the path can be walked then the pos ! 317: * pointer is set to the part of the path following the last successful ! 318: * walked element. The function can be called again to walk the remainder ! 319: * of the path (or produce an error). ! 320: * ! 321: * @param connection[in] Connection object. ! 322: * @param fid[in] Fid to start walk from, must be directory or root (from ! 323: * call to p9_attach). ! 324: * @param new_fid[in] Fid to be used for the last walked element. ! 325: * @param pos[in|out] Position in path that remains to be walked. If the ! 326: * path was completely walked without error this will point to the NULL ! 327: * at the end of path. ! 328: * @return 1 = partial walk, 0 = success, -ve = error. ! 329: * ! 330: * @remark ! 331: * size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) ! 332: * size[4] Rwalk tag[2] nwqid[2] nwqid*(qid[13]) ! 333: */ ! 334: int p9_walk(p9_connection_t *connection, uint32_t fid, uint32_t new_fid, ! 335: uint8_t **pos) ! 336: { ! 337: int rc; ! 338: const char *path = (const char *)*pos; ! 339: uint8_t *s_tok; ! 340: uint8_t *e_tok; ! 341: int element_count = 0; ! 342: ! 343: if (path == NULL) { ! 344: *pos = NULL; ! 345: return P9_NULL_PATH; ! 346: } ! 347: ! 348: reset_buffers(); ! 349: ! 350: /* Build message. */ ! 351: PUT_HD(T_WALK, TAG); /* Length to 0, set later. */ ! 352: PUT_32(fid); ! 353: PUT_32(new_fid); ! 354: PUT_16(0); /* Element count to 0, set later. */ ! 355: ! 356: /* Get elements from path, and append to message. */ ! 357: s_tok = (uint8_t *)path; ! 358: e_tok = s_tok; ! 359: ! 360: while (*s_tok != 0) { ! 361: while (*s_tok == '/') { ! 362: s_tok++; ! 363: } ! 364: e_tok = s_tok; ! 365: while ((*e_tok != '/') && (*e_tok != 0)) { ! 366: e_tok++; ! 367: } ! 368: ! 369: /* Check the element is OK. */ ! 370: if (strncmp(".", (const char *)s_tok, (e_tok - s_tok)) == 0) { ! 371: /* Don't send ".", continue to next. */ ! 372: s_tok = e_tok; ! 373: continue; ! 374: } ! 375: int tx_size = (e_tok - s_tok + 2 + GET_SIZE); ! 376: int rx_size = ((element_count + 1) * MSG_QID_SIZE ! 377: + MSG_WALK_RX_HDR_SIZE); ! 378: if ((tx_size > connection->message_size) ! 379: || (rx_size > connection->message_size)) { ! 380: /* ! 381: * Element makes TX msg too long OR expected RX msg ! 382: * too long. Move pos to previous element and do ! 383: * partial walk. ! 384: */ ! 385: e_tok = s_tok; ! 386: if (*(e_tok - 1) == '/') { ! 387: e_tok--; ! 388: } ! 389: break; ! 390: } ! 391: ! 392: /* Add the element to the message. */ ! 393: PUT_SN(s_tok, e_tok - s_tok); ! 394: element_count++; ! 395: ! 396: /* Server supports no more than 16 elements, partial walk. */ ! 397: if (element_count == MSG_WALK_MAX_ELMT) { ! 398: break; ! 399: } ! 400: ! 401: /* Ready to find the next element. */ ! 402: s_tok = e_tok; ! 403: } ! 404: ! 405: if ((element_count == 0) && (strlen(path) > 0)) { ! 406: return P9_PATH_ELEMENT_TOO_LONG; ! 407: } ! 408: ! 409: *pos = e_tok; ! 410: ! 411: /* Update counts and then send message. */ ! 412: SET_16(tx, MSG_WALK_TX_ELMT, element_count); ! 413: rc = p9_transaction(connection); ! 414: if (rc != 0) { ! 415: return rc; ! 416: } ! 417: ! 418: /* Check for special return conditions. */ ! 419: if (element_count != GET_16(rx, MSG_WALK_RX_ELMT)) { ! 420: /* Find the last element successfully walked */ ! 421: s_tok = (uint8_t *)path; ! 422: e_tok = s_tok; ! 423: element_count = GET_16(rx, MSG_WALK_RX_ELMT); ! 424: ! 425: while (element_count--) { ! 426: while (*s_tok == '/') { ! 427: s_tok++; ! 428: } ! 429: ! 430: e_tok = s_tok; ! 431: ! 432: while ((*e_tok != '/') && (*e_tok != 0)) { ! 433: e_tok++; ! 434: } ! 435: ! 436: s_tok = e_tok; ! 437: } ! 438: ! 439: *pos = e_tok; ! 440: } ! 441: if (**pos != 0) { ! 442: rc = P9_PARTIAL_WALK; ! 443: } ! 444: ! 445: ! 446: return rc; ! 447: } ! 448: ! 449: /** ! 450: * p9_open ! 451: * ! 452: * Opens the file represented by fid with associated mode bit mask. The iounit ! 453: * size returned from the server is written to the connection object. ! 454: * ! 455: * @param file[in|out] File object, contains fid for file. ! 456: * @param mode[in] Mode to open with. Bit's 0=R, 1=W, 2=RW, 3=EX, 4=Trunc ! 457: * and 6=Delete on Close. ! 458: * @return 0 = success, -ve = error. ! 459: * ! 460: * @remark ! 461: * size[4] Topen tag[2] fid[4] mode[1] ! 462: * size[4] Ropen tag[2] qid[13] iounit[4] ! 463: */ ! 464: int p9_open(p9_file_t *file, uint8_t mode) ! 465: { ! 466: int rc; ! 467: p9_connection_t *connection = file->connection; ! 468: ! 469: reset_buffers(); ! 470: file->iounit = 0; ! 471: ! 472: /* Build message. */ ! 473: PUT_HD(T_OPEN, TAG); ! 474: PUT_32(file->fid); ! 475: PUT_08(mode & MSG_OPEN_MODE_MASK); ! 476: ! 477: /* Send message. */ ! 478: rc = p9_transaction(connection); ! 479: if (rc != 0) { ! 480: return rc; ! 481: } ! 482: ! 483: /* Handle response. */ ! 484: file->iounit = GET_32(rx, MSG_OPEN_IOUNIT); ! 485: ! 486: ! 487: return 0; ! 488: } ! 489: ! 490: /** ! 491: * p9_read ! 492: * ! 493: * Reads the file in to buffer. ! 494: * ! 495: * @param file[in] File object, contains fid for file. ! 496: * @param buffer[out] Buffer for data. ! 497: * @param count[in] Number of bytes to read (less bytes than requested ! 498: * may be read). ! 499: * @param offset[in] Offset in file to read from. ! 500: * @return Bytes read, -ve = error. ! 501: * ! 502: * @remark ! 503: * size[4] Tread tag[2] fid[4] offset[8] count[4] ! 504: * size[4] Rread tag[2] count[4] data[count] ! 505: */ ! 506: int p9_read(p9_file_t *file, uint8_t *buffer, ! 507: uint32_t count, uint64_t offset) ! 508: { ! 509: int rc; ! 510: p9_connection_t *connection = file->connection; ! 511: uint32_t got; ! 512: ! 513: reset_buffers(); ! 514: count = MIN((connection->message_size - MSG_READ_DATA), count); ! 515: ! 516: /* Build message. */ ! 517: PUT_HD(T_READ, TAG); ! 518: PUT_32(file->fid); ! 519: PUT_64(offset); ! 520: PUT_32(count); ! 521: ! 522: /* Send message. */ ! 523: rc = p9_transaction(connection); ! 524: if (rc != 0) { ! 525: return rc; ! 526: } ! 527: got = GET_32(rx, MSG_READ_COUNT); ! 528: if (got > count) { ! 529: return P9_READ_UNEXPECTED_DATA; ! 530: } ! 531: ! 532: /* Handle response. */ ! 533: memcpy(buffer, &rx[MSG_READ_DATA], got); ! 534: ! 535: return got; ! 536: } ! 537: ! 538: /** ! 539: * p9_stat ! 540: * ! 541: * Stat's the fid and writes the type and length to the file object. ! 542: * ! 543: * @param file[in|out] File object, contains fid for file. ! 544: * @return 0 = success, -ve = error. ! 545: * ! 546: * @remark ! 547: * size[4] Tstat tag[2] fid[4] ! 548: * size[4] Rstat tag[2] size[2] stat[n] ! 549: */ ! 550: int p9_stat(p9_file_t *file) ! 551: { ! 552: int rc; ! 553: p9_connection_t *connection = file->connection; ! 554: ! 555: reset_buffers(); ! 556: file->length = 0; ! 557: file->type = 0; ! 558: ! 559: /* Build message. */ ! 560: PUT_HD(T_STAT, TAG); ! 561: PUT_32(file->fid); ! 562: ! 563: /* Send message. */ ! 564: rc = p9_transaction(connection); ! 565: if (rc != 0) { ! 566: return rc; ! 567: } ! 568: ! 569: /* Handle response. */ ! 570: file->length = GET_64(rx, MSG_STAT_LEN); ! 571: file->type = GET_08(rx, MSG_STAT_TYPE); ! 572: ! 573: ! 574: return 0; ! 575: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.