linux/net/9p/protocol.c
<<
>>
Prefs
   1/*
   2 * net/9p/protocol.c
   3 *
   4 * 9P Protocol Support Code
   5 *
   6 *  Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
   7 *
   8 *  Base on code from Anthony Liguori <aliguori@us.ibm.com>
   9 *  Copyright (C) 2008 by IBM, Corp.
  10 *
  11 *  This program is free software; you can redistribute it and/or modify
  12 *  it under the terms of the GNU General Public License version 2
  13 *  as published by the Free Software Foundation.
  14 *
  15 *  This program is distributed in the hope that it will be useful,
  16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 *  GNU General Public License for more details.
  19 *
  20 *  You should have received a copy of the GNU General Public License
  21 *  along with this program; if not, write to:
  22 *  Free Software Foundation
  23 *  51 Franklin Street, Fifth Floor
  24 *  Boston, MA  02111-1301  USA
  25 *
  26 */
  27
  28#include <linux/module.h>
  29#include <linux/errno.h>
  30#include <linux/kernel.h>
  31#include <linux/uaccess.h>
  32#include <linux/slab.h>
  33#include <linux/sched.h>
  34#include <linux/stddef.h>
  35#include <linux/types.h>
  36#include <net/9p/9p.h>
  37#include <net/9p/client.h>
  38#include "protocol.h"
  39
  40static int
  41p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
  42
  43#ifdef CONFIG_NET_9P_DEBUG
  44void
  45p9pdu_dump(int way, struct p9_fcall *pdu)
  46{
  47        int i, n;
  48        u8 *data = pdu->sdata;
  49        int datalen = pdu->size;
  50        char buf[255];
  51        int buflen = 255;
  52
  53        i = n = 0;
  54        if (datalen > (buflen-16))
  55                datalen = buflen-16;
  56        while (i < datalen) {
  57                n += scnprintf(buf + n, buflen - n, "%02x ", data[i]);
  58                if (i%4 == 3)
  59                        n += scnprintf(buf + n, buflen - n, " ");
  60                if (i%32 == 31)
  61                        n += scnprintf(buf + n, buflen - n, "\n");
  62
  63                i++;
  64        }
  65        n += scnprintf(buf + n, buflen - n, "\n");
  66
  67        if (way)
  68                P9_DPRINTK(P9_DEBUG_PKT, "[[[(%d) %s\n", datalen, buf);
  69        else
  70                P9_DPRINTK(P9_DEBUG_PKT, "]]](%d) %s\n", datalen, buf);
  71}
  72#else
  73void
  74p9pdu_dump(int way, struct p9_fcall *pdu)
  75{
  76}
  77#endif
  78EXPORT_SYMBOL(p9pdu_dump);
  79
  80void p9stat_free(struct p9_wstat *stbuf)
  81{
  82        kfree(stbuf->name);
  83        kfree(stbuf->uid);
  84        kfree(stbuf->gid);
  85        kfree(stbuf->muid);
  86        kfree(stbuf->extension);
  87}
  88EXPORT_SYMBOL(p9stat_free);
  89
  90static size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size)
  91{
  92        size_t len = min(pdu->size - pdu->offset, size);
  93        memcpy(data, &pdu->sdata[pdu->offset], len);
  94        pdu->offset += len;
  95        return size - len;
  96}
  97
  98static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size)
  99{
 100        size_t len = min(pdu->capacity - pdu->size, size);
 101        memcpy(&pdu->sdata[pdu->size], data, len);
 102        pdu->size += len;
 103        return size - len;
 104}
 105
 106static size_t
 107pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size)
 108{
 109        size_t len = min(pdu->capacity - pdu->size, size);
 110        if (copy_from_user(&pdu->sdata[pdu->size], udata, len))
 111                len = 0;
 112
 113        pdu->size += len;
 114        return size - len;
 115}
 116
 117/*
 118        b - int8_t
 119        w - int16_t
 120        d - int32_t
 121        q - int64_t
 122        s - string
 123        S - stat
 124        Q - qid
 125        D - data blob (int32_t size followed by void *, results are not freed)
 126        T - array of strings (int16_t count, followed by strings)
 127        R - array of qids (int16_t count, followed by qids)
 128        A - stat for 9p2000.L (p9_stat_dotl)
 129        ? - if optional = 1, continue parsing
 130*/
 131
 132static int
 133p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
 134        va_list ap)
 135{
 136        const char *ptr;
 137        int errcode = 0;
 138
 139        for (ptr = fmt; *ptr; ptr++) {
 140                switch (*ptr) {
 141                case 'b':{
 142                                int8_t *val = va_arg(ap, int8_t *);
 143                                if (pdu_read(pdu, val, sizeof(*val))) {
 144                                        errcode = -EFAULT;
 145                                        break;
 146                                }
 147                        }
 148                        break;
 149                case 'w':{
 150                                int16_t *val = va_arg(ap, int16_t *);
 151                                __le16 le_val;
 152                                if (pdu_read(pdu, &le_val, sizeof(le_val))) {
 153                                        errcode = -EFAULT;
 154                                        break;
 155                                }
 156                                *val = le16_to_cpu(le_val);
 157                        }
 158                        break;
 159                case 'd':{
 160                                int32_t *val = va_arg(ap, int32_t *);
 161                                __le32 le_val;
 162                                if (pdu_read(pdu, &le_val, sizeof(le_val))) {
 163                                        errcode = -EFAULT;
 164                                        break;
 165                                }
 166                                *val = le32_to_cpu(le_val);
 167                        }
 168                        break;
 169                case 'q':{
 170                                int64_t *val = va_arg(ap, int64_t *);
 171                                __le64 le_val;
 172                                if (pdu_read(pdu, &le_val, sizeof(le_val))) {
 173                                        errcode = -EFAULT;
 174                                        break;
 175                                }
 176                                *val = le64_to_cpu(le_val);
 177                        }
 178                        break;
 179                case 's':{
 180                                char **sptr = va_arg(ap, char **);
 181                                uint16_t len;
 182
 183                                errcode = p9pdu_readf(pdu, proto_version,
 184                                                                "w", &len);
 185                                if (errcode)
 186                                        break;
 187
 188                                *sptr = kmalloc(len + 1, GFP_KERNEL);
 189                                if (*sptr == NULL) {
 190                                        errcode = -EFAULT;
 191                                        break;
 192                                }
 193                                if (pdu_read(pdu, *sptr, len)) {
 194                                        errcode = -EFAULT;
 195                                        kfree(*sptr);
 196                                        *sptr = NULL;
 197                                } else
 198                                        (*sptr)[len] = 0;
 199                        }
 200                        break;
 201                case 'Q':{
 202                                struct p9_qid *qid =
 203                                    va_arg(ap, struct p9_qid *);
 204
 205                                errcode = p9pdu_readf(pdu, proto_version, "bdq",
 206                                                      &qid->type, &qid->version,
 207                                                      &qid->path);
 208                        }
 209                        break;
 210                case 'S':{
 211                                struct p9_wstat *stbuf =
 212                                    va_arg(ap, struct p9_wstat *);
 213
 214                                memset(stbuf, 0, sizeof(struct p9_wstat));
 215                                stbuf->n_uid = stbuf->n_gid = stbuf->n_muid =
 216                                                                        -1;
 217                                errcode =
 218                                    p9pdu_readf(pdu, proto_version,
 219                                                "wwdQdddqssss?sddd",
 220                                                &stbuf->size, &stbuf->type,
 221                                                &stbuf->dev, &stbuf->qid,
 222                                                &stbuf->mode, &stbuf->atime,
 223                                                &stbuf->mtime, &stbuf->length,
 224                                                &stbuf->name, &stbuf->uid,
 225                                                &stbuf->gid, &stbuf->muid,
 226                                                &stbuf->extension,
 227                                                &stbuf->n_uid, &stbuf->n_gid,
 228                                                &stbuf->n_muid);
 229                                if (errcode)
 230                                        p9stat_free(stbuf);
 231                        }
 232                        break;
 233                case 'D':{
 234                                uint32_t *count = va_arg(ap, uint32_t *);
 235                                void **data = va_arg(ap, void **);
 236
 237                                errcode =
 238                                    p9pdu_readf(pdu, proto_version, "d", count);
 239                                if (!errcode) {
 240                                        *count =
 241                                            min_t(uint32_t, *count,
 242                                                  pdu->size - pdu->offset);
 243                                        *data = &pdu->sdata[pdu->offset];
 244                                }
 245                        }
 246                        break;
 247                case 'T':{
 248                                int16_t *nwname = va_arg(ap, int16_t *);
 249                                char ***wnames = va_arg(ap, char ***);
 250
 251                                errcode = p9pdu_readf(pdu, proto_version,
 252                                                                "w", nwname);
 253                                if (!errcode) {
 254                                        *wnames =
 255                                            kmalloc(sizeof(char *) * *nwname,
 256                                                    GFP_KERNEL);
 257                                        if (!*wnames)
 258                                                errcode = -ENOMEM;
 259                                }
 260
 261                                if (!errcode) {
 262                                        int i;
 263
 264                                        for (i = 0; i < *nwname; i++) {
 265                                                errcode =
 266                                                    p9pdu_readf(pdu,
 267                                                                proto_version,
 268                                                                "s",
 269                                                                &(*wnames)[i]);
 270                                                if (errcode)
 271                                                        break;
 272                                        }
 273                                }
 274
 275                                if (errcode) {
 276                                        if (*wnames) {
 277                                                int i;
 278
 279                                                for (i = 0; i < *nwname; i++)
 280                                                        kfree((*wnames)[i]);
 281                                        }
 282                                        kfree(*wnames);
 283                                        *wnames = NULL;
 284                                }
 285                        }
 286                        break;
 287                case 'R':{
 288                                int16_t *nwqid = va_arg(ap, int16_t *);
 289                                struct p9_qid **wqids =
 290                                    va_arg(ap, struct p9_qid **);
 291
 292                                *wqids = NULL;
 293
 294                                errcode =
 295                                    p9pdu_readf(pdu, proto_version, "w", nwqid);
 296                                if (!errcode) {
 297                                        *wqids =
 298                                            kmalloc(*nwqid *
 299                                                    sizeof(struct p9_qid),
 300                                                    GFP_KERNEL);
 301                                        if (*wqids == NULL)
 302                                                errcode = -ENOMEM;
 303                                }
 304
 305                                if (!errcode) {
 306                                        int i;
 307
 308                                        for (i = 0; i < *nwqid; i++) {
 309                                                errcode =
 310                                                    p9pdu_readf(pdu,
 311                                                                proto_version,
 312                                                                "Q",
 313                                                                &(*wqids)[i]);
 314                                                if (errcode)
 315                                                        break;
 316                                        }
 317                                }
 318
 319                                if (errcode) {
 320                                        kfree(*wqids);
 321                                        *wqids = NULL;
 322                                }
 323                        }
 324                        break;
 325                case 'A': {
 326                                struct p9_stat_dotl *stbuf =
 327                                    va_arg(ap, struct p9_stat_dotl *);
 328
 329                                memset(stbuf, 0, sizeof(struct p9_stat_dotl));
 330                                errcode =
 331                                    p9pdu_readf(pdu, proto_version,
 332                                        "qQdddqqqqqqqqqqqqqqq",
 333                                        &stbuf->st_result_mask,
 334                                        &stbuf->qid,
 335                                        &stbuf->st_mode,
 336                                        &stbuf->st_uid, &stbuf->st_gid,
 337                                        &stbuf->st_nlink,
 338                                        &stbuf->st_rdev, &stbuf->st_size,
 339                                        &stbuf->st_blksize, &stbuf->st_blocks,
 340                                        &stbuf->st_atime_sec,
 341                                        &stbuf->st_atime_nsec,
 342                                        &stbuf->st_mtime_sec,
 343                                        &stbuf->st_mtime_nsec,
 344                                        &stbuf->st_ctime_sec,
 345                                        &stbuf->st_ctime_nsec,
 346                                        &stbuf->st_btime_sec,
 347                                        &stbuf->st_btime_nsec,
 348                                        &stbuf->st_gen,
 349                                        &stbuf->st_data_version);
 350                        }
 351                        break;
 352                case '?':
 353                        if ((proto_version != p9_proto_2000u) &&
 354                                (proto_version != p9_proto_2000L))
 355                                return 0;
 356                        break;
 357                default:
 358                        BUG();
 359                        break;
 360                }
 361
 362                if (errcode)
 363                        break;
 364        }
 365
 366        return errcode;
 367}
 368
 369int
 370p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
 371        va_list ap)
 372{
 373        const char *ptr;
 374        int errcode = 0;
 375
 376        for (ptr = fmt; *ptr; ptr++) {
 377                switch (*ptr) {
 378                case 'b':{
 379                                int8_t val = va_arg(ap, int);
 380                                if (pdu_write(pdu, &val, sizeof(val)))
 381                                        errcode = -EFAULT;
 382                        }
 383                        break;
 384                case 'w':{
 385                                __le16 val = cpu_to_le16(va_arg(ap, int));
 386                                if (pdu_write(pdu, &val, sizeof(val)))
 387                                        errcode = -EFAULT;
 388                        }
 389                        break;
 390                case 'd':{
 391                                __le32 val = cpu_to_le32(va_arg(ap, int32_t));
 392                                if (pdu_write(pdu, &val, sizeof(val)))
 393                                        errcode = -EFAULT;
 394                        }
 395                        break;
 396                case 'q':{
 397                                __le64 val = cpu_to_le64(va_arg(ap, int64_t));
 398                                if (pdu_write(pdu, &val, sizeof(val)))
 399                                        errcode = -EFAULT;
 400                        }
 401                        break;
 402                case 's':{
 403                                const char *sptr = va_arg(ap, const char *);
 404                                uint16_t len = 0;
 405                                if (sptr)
 406                                        len = min_t(uint16_t, strlen(sptr),
 407                                                                USHRT_MAX);
 408
 409                                errcode = p9pdu_writef(pdu, proto_version,
 410                                                                "w", len);
 411                                if (!errcode && pdu_write(pdu, sptr, len))
 412                                        errcode = -EFAULT;
 413                        }
 414                        break;
 415                case 'Q':{
 416                                const struct p9_qid *qid =
 417                                    va_arg(ap, const struct p9_qid *);
 418                                errcode =
 419                                    p9pdu_writef(pdu, proto_version, "bdq",
 420                                                 qid->type, qid->version,
 421                                                 qid->path);
 422                        } break;
 423                case 'S':{
 424                                const struct p9_wstat *stbuf =
 425                                    va_arg(ap, const struct p9_wstat *);
 426                                errcode =
 427                                    p9pdu_writef(pdu, proto_version,
 428                                                 "wwdQdddqssss?sddd",
 429                                                 stbuf->size, stbuf->type,
 430                                                 stbuf->dev, &stbuf->qid,
 431                                                 stbuf->mode, stbuf->atime,
 432                                                 stbuf->mtime, stbuf->length,
 433                                                 stbuf->name, stbuf->uid,
 434                                                 stbuf->gid, stbuf->muid,
 435                                                 stbuf->extension, stbuf->n_uid,
 436                                                 stbuf->n_gid, stbuf->n_muid);
 437                        } break;
 438                case 'D':{
 439                                uint32_t count = va_arg(ap, uint32_t);
 440                                const void *data = va_arg(ap, const void *);
 441
 442                                errcode = p9pdu_writef(pdu, proto_version, "d",
 443                                                                        count);
 444                                if (!errcode && pdu_write(pdu, data, count))
 445                                        errcode = -EFAULT;
 446                        }
 447                        break;
 448                case 'U':{
 449                                int32_t count = va_arg(ap, int32_t);
 450                                const char __user *udata =
 451                                                va_arg(ap, const void __user *);
 452                                errcode = p9pdu_writef(pdu, proto_version, "d",
 453                                                                        count);
 454                                if (!errcode && pdu_write_u(pdu, udata, count))
 455                                        errcode = -EFAULT;
 456                        }
 457                        break;
 458                case 'T':{
 459                                int16_t nwname = va_arg(ap, int);
 460                                const char **wnames = va_arg(ap, const char **);
 461
 462                                errcode = p9pdu_writef(pdu, proto_version, "w",
 463                                                                        nwname);
 464                                if (!errcode) {
 465                                        int i;
 466
 467                                        for (i = 0; i < nwname; i++) {
 468                                                errcode =
 469                                                    p9pdu_writef(pdu,
 470                                                                proto_version,
 471                                                                 "s",
 472                                                                 wnames[i]);
 473                                                if (errcode)
 474                                                        break;
 475                                        }
 476                                }
 477                        }
 478                        break;
 479                case 'R':{
 480                                int16_t nwqid = va_arg(ap, int);
 481                                struct p9_qid *wqids =
 482                                    va_arg(ap, struct p9_qid *);
 483
 484                                errcode = p9pdu_writef(pdu, proto_version, "w",
 485                                                                        nwqid);
 486                                if (!errcode) {
 487                                        int i;
 488
 489                                        for (i = 0; i < nwqid; i++) {
 490                                                errcode =
 491                                                    p9pdu_writef(pdu,
 492                                                                proto_version,
 493                                                                 "Q",
 494                                                                 &wqids[i]);
 495                                                if (errcode)
 496                                                        break;
 497                                        }
 498                                }
 499                        }
 500                        break;
 501                case 'I':{
 502                                struct p9_iattr_dotl *p9attr = va_arg(ap,
 503                                                        struct p9_iattr_dotl *);
 504
 505                                errcode = p9pdu_writef(pdu, proto_version,
 506                                                        "ddddqqqqq",
 507                                                        p9attr->valid,
 508                                                        p9attr->mode,
 509                                                        p9attr->uid,
 510                                                        p9attr->gid,
 511                                                        p9attr->size,
 512                                                        p9attr->atime_sec,
 513                                                        p9attr->atime_nsec,
 514                                                        p9attr->mtime_sec,
 515                                                        p9attr->mtime_nsec);
 516                        }
 517                        break;
 518                case '?':
 519                        if ((proto_version != p9_proto_2000u) &&
 520                                (proto_version != p9_proto_2000L))
 521                                return 0;
 522                        break;
 523                default:
 524                        BUG();
 525                        break;
 526                }
 527
 528                if (errcode)
 529                        break;
 530        }
 531
 532        return errcode;
 533}
 534
 535int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
 536{
 537        va_list ap;
 538        int ret;
 539
 540        va_start(ap, fmt);
 541        ret = p9pdu_vreadf(pdu, proto_version, fmt, ap);
 542        va_end(ap);
 543
 544        return ret;
 545}
 546
 547static int
 548p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
 549{
 550        va_list ap;
 551        int ret;
 552
 553        va_start(ap, fmt);
 554        ret = p9pdu_vwritef(pdu, proto_version, fmt, ap);
 555        va_end(ap);
 556
 557        return ret;
 558}
 559
 560int p9stat_read(char *buf, int len, struct p9_wstat *st, int proto_version)
 561{
 562        struct p9_fcall fake_pdu;
 563        int ret;
 564
 565        fake_pdu.size = len;
 566        fake_pdu.capacity = len;
 567        fake_pdu.sdata = buf;
 568        fake_pdu.offset = 0;
 569
 570        ret = p9pdu_readf(&fake_pdu, proto_version, "S", st);
 571        if (ret) {
 572                P9_DPRINTK(P9_DEBUG_9P, "<<< p9stat_read failed: %d\n", ret);
 573                p9pdu_dump(1, &fake_pdu);
 574        }
 575
 576        return ret;
 577}
 578EXPORT_SYMBOL(p9stat_read);
 579
 580int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type)
 581{
 582        return p9pdu_writef(pdu, 0, "dbw", 0, type, tag);
 583}
 584
 585int p9pdu_finalize(struct p9_fcall *pdu)
 586{
 587        int size = pdu->size;
 588        int err;
 589
 590        pdu->size = 0;
 591        err = p9pdu_writef(pdu, 0, "d", size);
 592        pdu->size = size;
 593
 594#ifdef CONFIG_NET_9P_DEBUG
 595        if ((p9_debug_level & P9_DEBUG_PKT) == P9_DEBUG_PKT)
 596                p9pdu_dump(0, pdu);
 597#endif
 598
 599        P9_DPRINTK(P9_DEBUG_9P, ">>> size=%d type: %d tag: %d\n", pdu->size,
 600                                                        pdu->id, pdu->tag);
 601
 602        return err;
 603}
 604
 605void p9pdu_reset(struct p9_fcall *pdu)
 606{
 607        pdu->offset = 0;
 608        pdu->size = 0;
 609}
 610
 611int p9dirent_read(char *buf, int len, struct p9_dirent *dirent,
 612                                                int proto_version)
 613{
 614        struct p9_fcall fake_pdu;
 615        int ret;
 616        char *nameptr;
 617
 618        fake_pdu.size = len;
 619        fake_pdu.capacity = len;
 620        fake_pdu.sdata = buf;
 621        fake_pdu.offset = 0;
 622
 623        ret = p9pdu_readf(&fake_pdu, proto_version, "Qqbs", &dirent->qid,
 624                        &dirent->d_off, &dirent->d_type, &nameptr);
 625        if (ret) {
 626                P9_DPRINTK(P9_DEBUG_9P, "<<< p9dirent_read failed: %d\n", ret);
 627                p9pdu_dump(1, &fake_pdu);
 628                goto out;
 629        }
 630
 631        strcpy(dirent->d_name, nameptr);
 632
 633out:
 634        return fake_pdu.offset;
 635}
 636EXPORT_SYMBOL(p9dirent_read);
 637