NFS: Don't use GFP_KERNEL in rpcsec_gss downcalls
[safe/jmp/linux-2.6] / net / 9p / protocol.c
index 43e9822..e7541d5 100644 (file)
 
 #include <linux/module.h>
 #include <linux/errno.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/types.h>
 #include <net/9p/9p.h>
 #include <net/9p/client.h>
 #include "protocol.h"
 #endif
 
 static int
-p9pdu_writef(struct p9_fcall *pdu, int optional, const char *fmt, ...);
+p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
+
+#ifdef CONFIG_NET_9P_DEBUG
+void
+p9pdu_dump(int way, struct p9_fcall *pdu)
+{
+       int i, n;
+       u8 *data = pdu->sdata;
+       int datalen = pdu->size;
+       char buf[255];
+       int buflen = 255;
+
+       i = n = 0;
+       if (datalen > (buflen-16))
+               datalen = buflen-16;
+       while (i < datalen) {
+               n += scnprintf(buf + n, buflen - n, "%02x ", data[i]);
+               if (i%4 == 3)
+                       n += scnprintf(buf + n, buflen - n, " ");
+               if (i%32 == 31)
+                       n += scnprintf(buf + n, buflen - n, "\n");
+
+               i++;
+       }
+       n += scnprintf(buf + n, buflen - n, "\n");
+
+       if (way)
+               P9_DPRINTK(P9_DEBUG_PKT, "[[[(%d) %s\n", datalen, buf);
+       else
+               P9_DPRINTK(P9_DEBUG_PKT, "]]](%d) %s\n", datalen, buf);
+}
+#else
+void
+p9pdu_dump(int way, struct p9_fcall *pdu)
+{
+}
+#endif
+EXPORT_SYMBOL(p9pdu_dump);
 
 void p9stat_free(struct p9_wstat *stbuf)
 {
@@ -77,6 +118,18 @@ static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size)
        return size - len;
 }
 
+static size_t
+pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size)
+{
+       size_t len = MIN(pdu->capacity - pdu->size, size);
+       int err = copy_from_user(&pdu->sdata[pdu->size], udata, len);
+       if (err)
+               printk(KERN_WARNING "pdu_write_u returning: %d\n", err);
+
+       pdu->size += len;
+       return size - len;
+}
+
 /*
        b - int8_t
        w - int16_t
@@ -92,7 +145,8 @@ static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size)
 */
 
 static int
-p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
+p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
+       va_list ap)
 {
        const char *ptr;
        int errcode = 0;
@@ -109,60 +163,64 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                        break;
                case 'w':{
                                int16_t *val = va_arg(ap, int16_t *);
-                               if (pdu_read(pdu, val, sizeof(*val))) {
+                               __le16 le_val;
+                               if (pdu_read(pdu, &le_val, sizeof(le_val))) {
                                        errcode = -EFAULT;
                                        break;
                                }
-                               *val = cpu_to_le16(*val);
+                               *val = le16_to_cpu(le_val);
                        }
                        break;
                case 'd':{
                                int32_t *val = va_arg(ap, int32_t *);
-                               if (pdu_read(pdu, val, sizeof(*val))) {
+                               __le32 le_val;
+                               if (pdu_read(pdu, &le_val, sizeof(le_val))) {
                                        errcode = -EFAULT;
                                        break;
                                }
-                               *val = cpu_to_le32(*val);
+                               *val = le32_to_cpu(le_val);
                        }
                        break;
                case 'q':{
                                int64_t *val = va_arg(ap, int64_t *);
-                               if (pdu_read(pdu, val, sizeof(*val))) {
+                               __le64 le_val;
+                               if (pdu_read(pdu, &le_val, sizeof(le_val))) {
                                        errcode = -EFAULT;
                                        break;
                                }
-                               *val = cpu_to_le64(*val);
+                               *val = le64_to_cpu(le_val);
                        }
                        break;
                case 's':{
-                               char **ptr = va_arg(ap, char **);
+                               char **sptr = va_arg(ap, char **);
                                int16_t len;
                                int size;
 
-                               errcode = p9pdu_readf(pdu, optional, "w", &len);
+                               errcode = p9pdu_readf(pdu, proto_version,
+                                                               "w", &len);
                                if (errcode)
                                        break;
 
                                size = MAX(len, 0);
 
-                               *ptr = kmalloc(size + 1, GFP_KERNEL);
-                               if (*ptr == NULL) {
+                               *sptr = kmalloc(size + 1, GFP_KERNEL);
+                               if (*sptr == NULL) {
                                        errcode = -EFAULT;
                                        break;
                                }
-                               if (pdu_read(pdu, *ptr, size)) {
+                               if (pdu_read(pdu, *sptr, size)) {
                                        errcode = -EFAULT;
-                                       kfree(*ptr);
-                                       *ptr = NULL;
+                                       kfree(*sptr);
+                                       *sptr = NULL;
                                } else
-                                       (*ptr)[size] = 0;
+                                       (*sptr)[size] = 0;
                        }
                        break;
                case 'Q':{
                                struct p9_qid *qid =
                                    va_arg(ap, struct p9_qid *);
 
-                               errcode = p9pdu_readf(pdu, optional, "bdq",
+                               errcode = p9pdu_readf(pdu, proto_version, "bdq",
                                                      &qid->type, &qid->version,
                                                      &qid->path);
                        }
@@ -171,12 +229,11 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                struct p9_wstat *stbuf =
                                    va_arg(ap, struct p9_wstat *);
 
-                               stbuf->extension = NULL;
+                               memset(stbuf, 0, sizeof(struct p9_wstat));
                                stbuf->n_uid = stbuf->n_gid = stbuf->n_muid =
-                                   -1;
-
+                                                                       -1;
                                errcode =
-                                   p9pdu_readf(pdu, optional,
+                                   p9pdu_readf(pdu, proto_version,
                                                "wwdQdddqssss?sddd",
                                                &stbuf->size, &stbuf->type,
                                                &stbuf->dev, &stbuf->qid,
@@ -196,7 +253,7 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                void **data = va_arg(ap, void **);
 
                                errcode =
-                                   p9pdu_readf(pdu, optional, "d", count);
+                                   p9pdu_readf(pdu, proto_version, "d", count);
                                if (!errcode) {
                                        *count =
                                            MIN(*count,
@@ -209,8 +266,8 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                int16_t *nwname = va_arg(ap, int16_t *);
                                char ***wnames = va_arg(ap, char ***);
 
-                               errcode =
-                                   p9pdu_readf(pdu, optional, "w", nwname);
+                               errcode = p9pdu_readf(pdu, proto_version,
+                                                               "w", nwname);
                                if (!errcode) {
                                        *wnames =
                                            kmalloc(sizeof(char *) * *nwname,
@@ -224,7 +281,8 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
 
                                        for (i = 0; i < *nwname; i++) {
                                                errcode =
-                                                   p9pdu_readf(pdu, optional,
+                                                   p9pdu_readf(pdu,
+                                                               proto_version,
                                                                "s",
                                                                &(*wnames)[i]);
                                                if (errcode)
@@ -252,7 +310,7 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                *wqids = NULL;
 
                                errcode =
-                                   p9pdu_readf(pdu, optional, "w", nwqid);
+                                   p9pdu_readf(pdu, proto_version, "w", nwqid);
                                if (!errcode) {
                                        *wqids =
                                            kmalloc(*nwqid *
@@ -267,7 +325,8 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
 
                                        for (i = 0; i < *nwqid; i++) {
                                                errcode =
-                                                   p9pdu_readf(pdu, optional,
+                                                   p9pdu_readf(pdu,
+                                                               proto_version,
                                                                "Q",
                                                                &(*wqids)[i]);
                                                if (errcode)
@@ -282,7 +341,7 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                        }
                        break;
                case '?':
-                       if (!optional)
+                       if (proto_version != p9_proto_2000u)
                                return 0;
                        break;
                default:
@@ -298,7 +357,8 @@ p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
 }
 
 int
-p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
+p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
+       va_list ap)
 {
        const char *ptr;
        int errcode = 0;
@@ -312,32 +372,32 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                        }
                        break;
                case 'w':{
-                               int16_t val = va_arg(ap, int);
+                               __le16 val = cpu_to_le16(va_arg(ap, int));
                                if (pdu_write(pdu, &val, sizeof(val)))
                                        errcode = -EFAULT;
                        }
                        break;
                case 'd':{
-                               int32_t val = va_arg(ap, int32_t);
+                               __le32 val = cpu_to_le32(va_arg(ap, int32_t));
                                if (pdu_write(pdu, &val, sizeof(val)))
                                        errcode = -EFAULT;
                        }
                        break;
                case 'q':{
-                               int64_t val = va_arg(ap, int64_t);
+                               __le64 val = cpu_to_le64(va_arg(ap, int64_t));
                                if (pdu_write(pdu, &val, sizeof(val)))
                                        errcode = -EFAULT;
                        }
                        break;
                case 's':{
-                               const char *ptr = va_arg(ap, const char *);
+                               const char *sptr = va_arg(ap, const char *);
                                int16_t len = 0;
+                               if (sptr)
+                                       len = MIN(strlen(sptr), USHORT_MAX);
 
-                               if (ptr)
-                                       len = MIN(strlen(ptr), USHORT_MAX);
-
-                               errcode = p9pdu_writef(pdu, optional, "w", len);
-                               if (!errcode && pdu_write(pdu, ptr, len))
+                               errcode = p9pdu_writef(pdu, proto_version,
+                                                               "w", len);
+                               if (!errcode && pdu_write(pdu, sptr, len))
                                        errcode = -EFAULT;
                        }
                        break;
@@ -345,7 +405,7 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                const struct p9_qid *qid =
                                    va_arg(ap, const struct p9_qid *);
                                errcode =
-                                   p9pdu_writef(pdu, optional, "bdq",
+                                   p9pdu_writef(pdu, proto_version, "bdq",
                                                 qid->type, qid->version,
                                                 qid->path);
                        } break;
@@ -353,10 +413,10 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                const struct p9_wstat *stbuf =
                                    va_arg(ap, const struct p9_wstat *);
                                errcode =
-                                   p9pdu_writef(pdu, optional,
+                                   p9pdu_writef(pdu, proto_version,
                                                 "wwdQdddqssss?sddd",
                                                 stbuf->size, stbuf->type,
-                                                stbuf->dev, stbuf->qid,
+                                                stbuf->dev, &stbuf->qid,
                                                 stbuf->mode, stbuf->atime,
                                                 stbuf->mtime, stbuf->length,
                                                 stbuf->name, stbuf->uid,
@@ -368,24 +428,35 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                int32_t count = va_arg(ap, int32_t);
                                const void *data = va_arg(ap, const void *);
 
-                               errcode =
-                                   p9pdu_writef(pdu, optional, "d", count);
+                               errcode = p9pdu_writef(pdu, proto_version, "d",
+                                                                       count);
                                if (!errcode && pdu_write(pdu, data, count))
                                        errcode = -EFAULT;
                        }
                        break;
+               case 'U':{
+                               int32_t count = va_arg(ap, int32_t);
+                               const char __user *udata =
+                                               va_arg(ap, const void __user *);
+                               errcode = p9pdu_writef(pdu, proto_version, "d",
+                                                                       count);
+                               if (!errcode && pdu_write_u(pdu, udata, count))
+                                       errcode = -EFAULT;
+                       }
+                       break;
                case 'T':{
                                int16_t nwname = va_arg(ap, int);
                                const char **wnames = va_arg(ap, const char **);
 
-                               errcode =
-                                   p9pdu_writef(pdu, optional, "w", nwname);
+                               errcode = p9pdu_writef(pdu, proto_version, "w",
+                                                                       nwname);
                                if (!errcode) {
                                        int i;
 
                                        for (i = 0; i < nwname; i++) {
                                                errcode =
-                                                   p9pdu_writef(pdu, optional,
+                                                   p9pdu_writef(pdu,
+                                                               proto_version,
                                                                 "s",
                                                                 wnames[i]);
                                                if (errcode)
@@ -399,14 +470,15 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                                struct p9_qid *wqids =
                                    va_arg(ap, struct p9_qid *);
 
-                               errcode =
-                                   p9pdu_writef(pdu, optional, "w", nwqid);
+                               errcode = p9pdu_writef(pdu, proto_version, "w",
+                                                                       nwqid);
                                if (!errcode) {
                                        int i;
 
                                        for (i = 0; i < nwqid; i++) {
                                                errcode =
-                                                   p9pdu_writef(pdu, optional,
+                                                   p9pdu_writef(pdu,
+                                                               proto_version,
                                                                 "Q",
                                                                 &wqids[i]);
                                                if (errcode)
@@ -416,7 +488,7 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
                        }
                        break;
                case '?':
-                       if (!optional)
+                       if (proto_version != p9_proto_2000u)
                                return 0;
                        break;
                default:
@@ -431,27 +503,78 @@ p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
        return errcode;
 }
 
-int p9pdu_readf(struct p9_fcall *pdu, int optional, const char *fmt, ...)
+int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
 {
        va_list ap;
        int ret;
 
        va_start(ap, fmt);
-       ret = p9pdu_vreadf(pdu, optional, fmt, ap);
+       ret = p9pdu_vreadf(pdu, proto_version, fmt, ap);
        va_end(ap);
 
        return ret;
 }
 
 static int
-p9pdu_writef(struct p9_fcall *pdu, int optional, const char *fmt, ...)
+p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
 {
        va_list ap;
        int ret;
 
        va_start(ap, fmt);
-       ret = p9pdu_vwritef(pdu, optional, fmt, ap);
+       ret = p9pdu_vwritef(pdu, proto_version, fmt, ap);
        va_end(ap);
 
        return ret;
 }
+
+int p9stat_read(char *buf, int len, struct p9_wstat *st, int proto_version)
+{
+       struct p9_fcall fake_pdu;
+       int ret;
+
+       fake_pdu.size = len;
+       fake_pdu.capacity = len;
+       fake_pdu.sdata = buf;
+       fake_pdu.offset = 0;
+
+       ret = p9pdu_readf(&fake_pdu, proto_version, "S", st);
+       if (ret) {
+               P9_DPRINTK(P9_DEBUG_9P, "<<< p9stat_read failed: %d\n", ret);
+               p9pdu_dump(1, &fake_pdu);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(p9stat_read);
+
+int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type)
+{
+       return p9pdu_writef(pdu, 0, "dbw", 0, type, tag);
+}
+
+int p9pdu_finalize(struct p9_fcall *pdu)
+{
+       int size = pdu->size;
+       int err;
+
+       pdu->size = 0;
+       err = p9pdu_writef(pdu, 0, "d", size);
+       pdu->size = size;
+
+#ifdef CONFIG_NET_9P_DEBUG
+       if ((p9_debug_level & P9_DEBUG_PKT) == P9_DEBUG_PKT)
+               p9pdu_dump(0, pdu);
+#endif
+
+       P9_DPRINTK(P9_DEBUG_9P, ">>> size=%d type: %d tag: %d\n", pdu->size,
+                                                       pdu->id, pdu->tag);
+
+       return err;
+}
+
+void p9pdu_reset(struct p9_fcall *pdu)
+{
+       pdu->offset = 0;
+       pdu->size = 0;
+}