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
  40#include <trace/events/9p.h>
  41
  42static int
  43p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
  44
  45void p9stat_free(struct p9_wstat *stbuf)
  46{
  47        kfree(stbuf->name);
  48        kfree(stbuf->uid);
  49        kfree(stbuf->gid);
  50        kfree(stbuf->muid);
  51        kfree(stbuf->extension);
  52}
  53EXPORT_SYMBOL(p9stat_free);
  54
  55size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size)
  56{
  57        size_t len = min(pdu->size - pdu->offset, size);
  58        memcpy(data, &pdu->sdata[pdu->offset], len);
  59        pdu->offset += len;
  60        return size - len;
  61}
  62
  63static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size)
  64{
  65        size_t len = min(pdu->capacity - pdu->size, size);
  66        memcpy(&pdu->sdata[pdu->size], data, len);
  67        pdu->size += len;
  68        return size - len;
  69}
  70
  71static size_t
  72pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size)
  73{
  74        size_t len = min(pdu->capacity - pdu->size, size);
  75        if (copy_from_user(&pdu->sdata[pdu->size], udata, len))
  76                len = 0;
  77
  78        pdu->size += len;
  79        return size - len;
  80}
  81
  82/*
  83        b - int8_t
  84        w - int16_t
  85        d - int32_t
  86        q - int64_t
  87        s - string
  88        S - stat
  89        Q - qid
  90        D - data blob (int32_t size followed by void *, results are not freed)
  91        T - array of strings (int16_t count, followed by strings)
  92        R - array of qids (int16_t count, followed by qids)
  93        A - stat for 9p2000.L (p9_stat_dotl)
  94        ? - if optional = 1, continue parsing
  95*/
  96
  97static int
  98p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
  99        va_list ap)
 100{
 101        const char *ptr;
 102        int errcode = 0;
 103
 104        for (ptr = fmt; *ptr; ptr++) {
 105                switch (*ptr) {
 106                case 'b':{
 107                                int8_t *val = va_arg(ap, int8_t *);
 108                                if (pdu_read(pdu, val, sizeof(*val))) {
 109                                        errcode = -EFAULT;
 110                                        break;
 111                                }
 112                        }
 113                        break;
 114                case 'w':{
 115                                int16_t *val = va_arg(ap, int16_t *);
 116                                __le16 le_val;
 117                                if (pdu_read(pdu, &le_val, sizeof(le_val))) {
 118                                        errcode = -EFAULT;
 119                                        break;
 120                                }
 121                                *val = le16_to_cpu(le_val);
 122                        }
 123                        break;
 124                case 'd':{
 125                                int32_t *val = va_arg(ap, int32_t *);
 126                                __le32 le_val;
 127                                if (pdu_read(pdu, &le_val, sizeof(le_val))) {
 128                                        errcode = -EFAULT;
 129                                        break;
 130                                }
 131                                *val = le32_to_cpu(le_val);
 132                        }
 133                        break;
 134                case 'q':{
 135                                int64_t *val = va_arg(ap, int64_t *);
 136                                __le64 le_val;
 137                                if (pdu_read(pdu, &le_val, sizeof(le_val))) {
 138                                        errcode = -EFAULT;
 139                                        break;
 140                                }
 141                                *val = le64_to_cpu(le_val);
 142                        }
 143                        break;
 144                case 's':{
 145                                char **sptr = va_arg(ap, char **);
 146                                uint16_t len;
 147
 148                                errcode = p9pdu_readf(pdu, proto_version,
 149                                                                "w", &len);
 150                                if (errcode)
 151                                        break;
 152
 153                                *sptr = kmalloc(len + 1, GFP_NOFS);
 154                                if (*sptr == NULL) {
 155                                        errcode = -EFAULT;
 156                                        break;
 157                                }
 158                                if (pdu_read(pdu, *sptr, len)) {
 159                                        errcode = -EFAULT;
 160                                        kfree(*sptr);
 161                                        *sptr = NULL;
 162                                } else
 163                                        (*sptr)[len] = 0;
 164                        }
 165                        break;
 166                case 'Q':{
 167                                struct p9_qid *qid =
 168                                    va_arg(ap, struct p9_qid *);
 169
 170                                errcode = p9pdu_readf(pdu, proto_version, "bdq",
 171                                                      &qid->type, &qid->version,
 172                                                      &qid->path);
 173                        }
 174                        break;
 175                case 'S':{
 176                                struct p9_wstat *stbuf =
 177                                    va_arg(ap, struct p9_wstat *);
 178
 179                                memset(stbuf, 0, sizeof(struct p9_wstat));
 180                                stbuf->n_uid = stbuf->n_gid = stbuf->n_muid =
 181                                                                        -1;
 182                                errcode =
 183                                    p9pdu_readf(pdu, proto_version,
 184                                                "wwdQdddqssss?sddd",
 185                                                &stbuf->size, &stbuf->type,
 186                                                &stbuf->dev, &stbuf->qid,
 187                                                &stbuf->mode, &stbuf->atime,
 188                                                &stbuf->mtime, &stbuf->length,
 189                                                &stbuf->name, &stbuf->uid,
 190                                                &stbuf->gid, &stbuf->muid,
 191                                                &stbuf->extension,
 192                                                &stbuf->n_uid, &stbuf->n_gid,
 193                                                &stbuf->n_muid);
 194                                if (errcode)
 195                                        p9stat_free(stbuf);
 196                        }
 197                        break;
 198                case 'D':{
 199                                uint32_t *count = va_arg(ap, uint32_t *);
 200                                void **data = va_arg(ap, void **);
 201
 202                                errcode =
 203                                    p9pdu_readf(pdu, proto_version, "d", count);
 204                                if (!errcode) {
 205                                        *count =
 206                                            min_t(uint32_t, *count,
 207                                                  pdu->size - pdu->offset);
 208                                        *data = &pdu->sdata[pdu->offset];
 209                                }
 210                        }
 211                        break;
 212                case 'T':{
 213                                uint16_t *nwname = va_arg(ap, uint16_t *);
 214                                char ***wnames = va_arg(ap, char ***);
 215
 216                                errcode = p9pdu_readf(pdu, proto_version,
 217                                                                "w", nwname);
 218                                if (!errcode) {
 219                                        *wnames =
 220                                            kmalloc(sizeof(char *) * *nwname,
 221                                                    GFP_NOFS);
 222                                        if (!*wnames)
 223                                                errcode = -ENOMEM;
 224                                }
 225
 226                                if (!errcode) {
 227                                        int i;
 228
 229                                        for (i = 0; i < *nwname; i++) {
 230                                                errcode =
 231                                                    p9pdu_readf(pdu,
 232                                                                proto_version,
 233                                                                "s",
 234                                                                &(*wnames)[i]);
 235                                                if (errcode)
 236                                                        break;
 237                                        }
 238                                }
 239
 240                                if (errcode) {
 241                                        if (*wnames) {
 242                                                int i;
 243
 244                                                for (i = 0; i < *nwname; i++)
 245                                                        kfree((*wnames)[i]);
 246                                        }
 247                                        kfree(*wnames);
 248                                        *wnames = NULL;
 249                                }
 250                        }
 251                        break;
 252                case 'R':{
 253                                int16_t *nwqid = va_arg(ap, int16_t *);
 254                                struct p9_qid **wqids =
 255                                    va_arg(ap, struct p9_qid **);
 256
 257                                *wqids = NULL;
 258
 259                                errcode =
 260                                    p9pdu_readf(pdu, proto_version, "w", nwqid);
 261                                if (!errcode) {
 262                                        *wqids =
 263                                            kmalloc(*nwqid *
 264                                                    sizeof(struct p9_qid),
 265                                                    GFP_NOFS);
 266                                        if (*wqids == NULL)
 267                                                errcode = -ENOMEM;
 268                                }
 269
 270                                if (!errcode) {
 271                                        int i;
 272
 273                                        for (i = 0; i < *nwqid; i++) {
 274                                                errcode =
 275                                                    p9pdu_readf(pdu,
 276                                                                proto_version,
 277                                                                "Q",
 278                                                                &(*wqids)[i]);
 279                                                if (errcode)
 280                                                        break;
 281                                        }
 282                                }
 283
 284                                if (errcode) {
 285                                        kfree(*wqids);
 286                                        *wqids = NULL;
 287                                }
 288                        }
 289                        break;
 290                case 'A': {
 291                                struct p9_stat_dotl *stbuf =
 292                                    va_arg(ap, struct p9_stat_dotl *);
 293
 294                                memset(stbuf, 0, sizeof(struct p9_stat_dotl));
 295                                errcode =
 296                                    p9pdu_readf(pdu, proto_version,
 297                                        "qQdddqqqqqqqqqqqqqqq",
 298                                        &stbuf->st_result_mask,
 299                                        &stbuf->qid,
 300                                        &stbuf->st_mode,
 301                                        &stbuf->st_uid, &stbuf->st_gid,
 302                                        &stbuf->st_nlink,
 303                                        &stbuf->st_rdev, &stbuf->st_size,
 304                                        &stbuf->st_blksize, &stbuf->st_blocks,
 305                                        &stbuf->st_atime_sec,
 306                                        &stbuf->st_atime_nsec,
 307                                        &stbuf->st_mtime_sec,
 308                                        &stbuf->st_mtime_nsec,
 309                                        &stbuf->st_ctime_sec,
 310                                        &stbuf->st_ctime_nsec,
 311                                        &stbuf->st_btime_sec,
 312                                        &stbuf->st_btime_nsec,
 313                                        &stbuf->st_gen,
 314                                        &stbuf->st_data_version);
 315                        }
 316                        break;
 317                case '?':
 318                        if ((proto_version != p9_proto_2000u) &&
 319                                (proto_version != p9_proto_2000L))
 320                                return 0;
 321                        break;
 322                default:
 323                        BUG();
 324                        break;
 325                }
 326
 327                if (errcode)
 328                        break;
 329        }
 330
 331        return errcode;
 332}
 333
 334int
 335p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
 336        va_list ap)
 337{
 338        const char *ptr;
 339        int errcode = 0;
 340
 341        for (ptr = fmt; *ptr; ptr++) {
 342                switch (*ptr) {
 343                case 'b':{
 344                                int8_t val = va_arg(ap, int);
 345                                if (pdu_write(pdu, &val, sizeof(val)))
 346                                        errcode = -EFAULT;
 347                        }
 348                        break;
 349                case 'w':{
 350                                __le16 val = cpu_to_le16(va_arg(ap, int));
 351                                if (pdu_write(pdu, &val, sizeof(val)))
 352                                        errcode = -EFAULT;
 353                        }
 354                        break;
 355                case 'd':{
 356                                __le32 val = cpu_to_le32(va_arg(ap, int32_t));
 357                                if (pdu_write(pdu, &val, sizeof(val)))
 358                                        errcode = -EFAULT;
 359                        }
 360                        break;
 361                case 'q':{
 362                                __le64 val = cpu_to_le64(va_arg(ap, int64_t));
 363                                if (pdu_write(pdu, &val, sizeof(val)))
 364                                        errcode = -EFAULT;
 365                        }
 366                        break;
 367                case 's':{
 368                                const char *sptr = va_arg(ap, const char *);
 369                                uint16_t len = 0;
 370                                if (sptr)
 371                                        len = min_t(size_t, strlen(sptr),
 372                                                                USHRT_MAX);
 373
 374                                errcode = p9pdu_writef(pdu, proto_version,
 375                                                                "w", len);
 376                                if (!errcode && pdu_write(pdu, sptr, len))
 377                                        errcode = -EFAULT;
 378                        }
 379                        break;
 380                case 'Q':{
 381                                const struct p9_qid *qid =
 382                                    va_arg(ap, const struct p9_qid *);
 383                                errcode =
 384                                    p9pdu_writef(pdu, proto_version, "bdq",
 385                                                 qid->type, qid->version,
 386                                                 qid->path);
 387                        } break;
 388                case 'S':{
 389                                const struct p9_wstat *stbuf =
 390                                    va_arg(ap, const struct p9_wstat *);
 391                                errcode =
 392                                    p9pdu_writef(pdu, proto_version,
 393                                                 "wwdQdddqssss?sddd",
 394                                                 stbuf->size, stbuf->type,
 395                                                 stbuf->dev, &stbuf->qid,
 396                                                 stbuf->mode, stbuf->atime,
 397                                                 stbuf->mtime, stbuf->length,
 398                                                 stbuf->name, stbuf->uid,
 399                                                 stbuf->gid, stbuf->muid,
 400                                                 stbuf->extension, stbuf->n_uid,
 401                                                 stbuf->n_gid, stbuf->n_muid);
 402                        } break;
 403                case 'D':{
 404                                uint32_t count = va_arg(ap, uint32_t);
 405                                const void *data = va_arg(ap, const void *);
 406
 407                                errcode = p9pdu_writef(pdu, proto_version, "d",
 408                                                                        count);
 409                                if (!errcode && pdu_write(pdu, data, count))
 410                                        errcode = -EFAULT;
 411                        }
 412                        break;
 413                case 'U':{
 414                                int32_t count = va_arg(ap, int32_t);
 415                                const char __user *udata =
 416                                                va_arg(ap, const void __user *);
 417                                errcode = p9pdu_writef(pdu, proto_version, "d",
 418                                                                        count);
 419                                if (!errcode && pdu_write_u(pdu, udata, count))
 420                                        errcode = -EFAULT;
 421                        }
 422                        break;
 423                case 'T':{
 424                                uint16_t nwname = va_arg(ap, int);
 425                                const char **wnames = va_arg(ap, const char **);
 426
 427                                errcode = p9pdu_writef(pdu, proto_version, "w",
 428                                                                        nwname);
 429                                if (!errcode) {
 430                                        int i;
 431
 432                                        for (i = 0; i < nwname; i++) {
 433                                                errcode =
 434                                                    p9pdu_writef(pdu,
 435                                                                proto_version,
 436                                                                 "s",
 437                                                                 wnames[i]);
 438                                                if (errcode)
 439                                                        break;
 440                                        }
 441                                }
 442                        }
 443                        break;
 444                case 'R':{
 445                                int16_t nwqid = va_arg(ap, int);
 446                                struct p9_qid *wqids =
 447                                    va_arg(ap, struct p9_qid *);
 448
 449                                errcode = p9pdu_writef(pdu, proto_version, "w",
 450                                                                        nwqid);
 451                                if (!errcode) {
 452                                        int i;
 453
 454                                        for (i = 0; i < nwqid; i++) {
 455                                                errcode =
 456                                                    p9pdu_writef(pdu,
 457                                                                proto_version,
 458                                                                 "Q",
 459                                                                 &wqids[i]);
 460                                                if (errcode)
 461                                                        break;
 462                                        }
 463                                }
 464                        }
 465                        break;
 466                case 'I':{
 467                                struct p9_iattr_dotl *p9attr = va_arg(ap,
 468                                                        struct p9_iattr_dotl *);
 469
 470                                errcode = p9pdu_writef(pdu, proto_version,
 471                                                        "ddddqqqqq",
 472                                                        p9attr->valid,
 473                                                        p9attr->mode,
 474                                                        p9attr->uid,
 475                                                        p9attr->gid,
 476                                                        p9attr->size,
 477                                                        p9attr->atime_sec,
 478                                                        p9attr->atime_nsec,
 479                                                        p9attr->mtime_sec,
 480                                                        p9attr->mtime_nsec);
 481                        }
 482                        break;
 483                case '?':
 484                        if ((proto_version != p9_proto_2000u) &&
 485                                (proto_version != p9_proto_2000L))
 486                                return 0;
 487                        break;
 488                default:
 489                        BUG();
 490                        break;
 491                }
 492
 493                if (errcode)
 494                        break;
 495        }
 496
 497        return errcode;
 498}
 499
 500int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
 501{
 502        va_list ap;
 503        int ret;
 504
 505        va_start(ap, fmt);
 506        ret = p9pdu_vreadf(pdu, proto_version, fmt, ap);
 507        va_end(ap);
 508
 509        return ret;
 510}
 511
 512static int
 513p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
 514{
 515        va_list ap;
 516        int ret;
 517
 518        va_start(ap, fmt);
 519        ret = p9pdu_vwritef(pdu, proto_version, fmt, ap);
 520        va_end(ap);
 521
 522        return ret;
 523}
 524
 525int p9stat_read(struct p9_client *clnt, char *buf, int len, struct p9_wstat *st)
 526{
 527        struct p9_fcall fake_pdu;
 528        int ret;
 529
 530        fake_pdu.size = len;
 531        fake_pdu.capacity = len;
 532        fake_pdu.sdata = buf;
 533        fake_pdu.offset = 0;
 534
 535        ret = p9pdu_readf(&fake_pdu, clnt->proto_version, "S", st);
 536        if (ret) {
 537                p9_debug(P9_DEBUG_9P, "<<< p9stat_read failed: %d\n", ret);
 538                trace_9p_protocol_dump(clnt, &fake_pdu);
 539        }
 540
 541        return ret;
 542}
 543EXPORT_SYMBOL(p9stat_read);
 544
 545int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type)
 546{
 547        pdu->id = type;
 548        return p9pdu_writef(pdu, 0, "dbw", 0, type, tag);
 549}
 550
 551int p9pdu_finalize(struct p9_client *clnt, struct p9_fcall *pdu)
 552{
 553        int size = pdu->size;
 554        int err;
 555
 556        pdu->size = 0;
 557        err = p9pdu_writef(pdu, 0, "d", size);
 558        pdu->size = size;
 559
 560        trace_9p_protocol_dump(clnt, pdu);
 561        p9_debug(P9_DEBUG_9P, ">>> size=%d type: %d tag: %d\n",
 562                 pdu->size, pdu->id, pdu->tag);
 563
 564        return err;
 565}
 566
 567void p9pdu_reset(struct p9_fcall *pdu)
 568{
 569        pdu->offset = 0;
 570        pdu->size = 0;
 571}
 572
 573int p9dirent_read(struct p9_client *clnt, char *buf, int len,
 574                  struct p9_dirent *dirent)
 575{
 576        struct p9_fcall fake_pdu;
 577        int ret;
 578        char *nameptr;
 579
 580        fake_pdu.size = len;
 581        fake_pdu.capacity = len;
 582        fake_pdu.sdata = buf;
 583        fake_pdu.offset = 0;
 584
 585        ret = p9pdu_readf(&fake_pdu, clnt->proto_version, "Qqbs", &dirent->qid,
 586                          &dirent->d_off, &dirent->d_type, &nameptr);
 587        if (ret) {
 588                p9_debug(P9_DEBUG_9P, "<<< p9dirent_read failed: %d\n", ret);
 589                trace_9p_protocol_dump(clnt, &fake_pdu);
 590                goto out;
 591        }
 592
 593        strcpy(dirent->d_name, nameptr);
 594        kfree(nameptr);
 595
 596out:
 597        return fake_pdu.offset;
 598}
 599EXPORT_SYMBOL(p9dirent_read);
 600
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.