1 /******************************************************************************
2 * Copyright (c) 2011 IBM Corporation
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
10 * IBM Corporation - initial implementation
11 *****************************************************************************/
16 #include <byteorder.h>
20 /* Protocol stack marshaling. */
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)]))
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)
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
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));\
43 #define GET_SIZE (sp - tx)
46 /* General defines. */
47 #define MIN(a,b) ((a)>(b)?(b):(a))
49 #define NOTAG ((uint16_t)~0)
50 #define NOFID ((uint32_t)~0)
52 #define BUF_SIZE (8*1024)
54 #define VERSION "9P2000.u"
55 #define UNKNOWN_VER "unknown"
61 #define MSG_ERR_STR_LEN 7
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
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
80 #define R_VERSION (T_VERSION + 1)
82 #define R_ATTACH (T_ATTACH + 1)
84 #define R_ERROR (T_ERROR + 1)
86 #define R_WALK (T_WALK + 1)
88 #define R_OPEN (T_OPEN + 1)
90 #define R_READ (T_READ + 1)
92 #define R_CLUNK (T_CLUNK + 1)
94 #define R_STAT (T_STAT + 1)
96 static p9_transact_t transact;
97 static void *transact_opaque;
105 * Registers a transport function for use by the P9 protocol. The transport
106 * connects the P9 Client (this library) to a server instance.
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.
112 void p9_reg_transport(p9_transact_t transact_func, void *opaque,
113 uint8_t *tx_buffer, uint8_t *rx_buffer)
115 transact = transact_func;
116 transact_opaque = opaque;
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.
127 void reset_buffers(void)
129 memset(tx, 0, BUF_SIZE);
130 memset(rx, 0, BUF_SIZE);
137 * Perform a transaction (send/recv) over the registered transport.
139 * @param connection[in|out] Connection object.
140 * @return 0 = success, -ve = error.
142 int p9_transaction(p9_connection_t *connection)
145 int tx_size = GET_SIZE;
146 int rx_size = connection->message_size;
148 if (transact == NULL) {
149 return P9_NO_TRANSPORT;
151 if (tx == NULL || rx == NULL) {
154 if (connection->message_size > BUF_SIZE) {
155 return P9_MSG_SIZE_TOO_BIG;
157 if (tx_size > connection->message_size) {
158 return P9_MSG_TOO_LONG;
161 SET_32(tx, MSG_SIZE, tx_size);
162 rc = transact(transact_opaque, tx, tx_size, rx, &rx_size);
165 return P9_TRANSPORT_ERROR;
167 if (GET_16(tx, MSG_TAG) != GET_16(rx, MSG_TAG)) {
168 return P9_UNEXPECTED_TAG;
170 if (GET_08(rx, MSG_ID) == MSG_ERR) {
171 char error_string[200];
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)));
177 printf("\nError: %s\n", error_string);
181 if ((GET_08(tx, MSG_ID) + 1) != GET_08(rx, MSG_ID)) {
182 return P9_UNEXPECTED_MSG;
191 * Called to start a session. Negotiates the maximum message size for the
194 * @param connection[in|out] Connection object, contains message_size.
195 * @return 0 = success, -ve = error.
198 * size[4] Tversion tag[2] msize[4] version[s]
199 * size[4] Rversion tag[2] msize[4] version[s]
201 int p9_version(p9_connection_t *connection)
210 PUT_HD(T_VERSION, NOTAG);
211 PUT_32(connection->message_size);
215 rc = p9_transaction(connection);
220 /* Handle response. */
221 connection->message_size = MIN(connection->message_size,
222 GET_32(rx, MSG_VER_MSIZE));
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;
237 * Called to open a connection for a user to a file tree on the server. There
238 * is no authorisation undertaken (NOFID).
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.
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]
248 int p9_attach(p9_connection_t *connection)
251 int length = 19 + strlen(connection->uname) + strlen(connection->aname);
253 if (length > connection->message_size) {
254 return P9_MSG_TOO_LONG;
260 PUT_HD(T_ATTACH, TAG);
261 PUT_32(connection->fid);
263 PUT_ST(connection->uname);
264 PUT_ST(connection->aname);
265 PUT_32(~0); /* ??? */
268 rc = p9_transaction(connection);
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.
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.
288 * size[4] Tclunk tag[2] fid[4]
289 * size[4] Rclunk tag[2]
291 int p9_clunk(p9_connection_t *connection, uint32_t fid)
298 PUT_HD(T_CLUNK, TAG);
302 rc = p9_transaction(connection);
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).
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.
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])
334 int p9_walk(p9_connection_t *connection, uint32_t fid, uint32_t new_fid,
338 const char *path = (const char *)*pos;
341 int element_count = 0;
351 PUT_HD(T_WALK, TAG); /* Length to 0, set later. */
354 PUT_16(0); /* Element count to 0, set later. */
356 /* Get elements from path, and append to message. */
357 s_tok = (uint8_t *)path;
360 while (*s_tok != 0) {
361 while (*s_tok == '/') {
365 while ((*e_tok != '/') && (*e_tok != 0)) {
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. */
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)) {
381 * Element makes TX msg too long OR expected RX msg
382 * too long. Move pos to previous element and do
386 if (*(e_tok - 1) == '/') {
392 /* Add the element to the message. */
393 PUT_SN(s_tok, e_tok - s_tok);
396 /* Server supports no more than 16 elements, partial walk. */
397 if (element_count == MSG_WALK_MAX_ELMT) {
401 /* Ready to find the next element. */
405 if ((element_count == 0) && (strlen(path) > 0)) {
406 return P9_PATH_ELEMENT_TOO_LONG;
411 /* Update counts and then send message. */
412 SET_16(tx, MSG_WALK_TX_ELMT, element_count);
413 rc = p9_transaction(connection);
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;
423 element_count = GET_16(rx, MSG_WALK_RX_ELMT);
425 while (element_count--) {
426 while (*s_tok == '/') {
432 while ((*e_tok != '/') && (*e_tok != 0)) {
442 rc = P9_PARTIAL_WALK;
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.
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.
461 * size[4] Topen tag[2] fid[4] mode[1]
462 * size[4] Ropen tag[2] qid[13] iounit[4]
464 int p9_open(p9_file_t *file, uint8_t mode)
467 p9_connection_t *connection = file->connection;
475 PUT_08(mode & MSG_OPEN_MODE_MASK);
478 rc = p9_transaction(connection);
483 /* Handle response. */
484 file->iounit = GET_32(rx, MSG_OPEN_IOUNIT);
493 * Reads the file in to buffer.
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
499 * @param offset[in] Offset in file to read from.
500 * @return Bytes read, -ve = error.
503 * size[4] Tread tag[2] fid[4] offset[8] count[4]
504 * size[4] Rread tag[2] count[4] data[count]
506 int p9_read(p9_file_t *file, uint8_t *buffer,
507 uint32_t count, uint64_t offset)
510 p9_connection_t *connection = file->connection;
514 count = MIN((connection->message_size - MSG_READ_DATA), count);
523 rc = p9_transaction(connection);
527 got = GET_32(rx, MSG_READ_COUNT);
529 return P9_READ_UNEXPECTED_DATA;
532 /* Handle response. */
533 memcpy(buffer, &rx[MSG_READ_DATA], got);
541 * Stat's the fid and writes the type and length to the file object.
543 * @param file[in|out] File object, contains fid for file.
544 * @return 0 = success, -ve = error.
547 * size[4] Tstat tag[2] fid[4]
548 * size[4] Rstat tag[2] size[2] stat[n]
550 int p9_stat(p9_file_t *file)
553 p9_connection_t *connection = file->connection;
564 rc = p9_transaction(connection);
569 /* Handle response. */
570 file->length = GET_64(rx, MSG_STAT_LEN);
571 file->type = GET_08(rx, MSG_STAT_TYPE);