|
|
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.