|
|
1.1 root 1: /*
2: * Virtio 9p backend
3: *
4: * Copyright IBM, Corp. 2010
5: *
6: * Authors:
7: * Anthony Liguori <[email protected]>
8: *
9: * This work is licensed under the terms of the GNU GPL, version 2. See
10: * the COPYING file in the top-level directory.
11: *
12: */
13:
1.1.1.2 ! root 14: #include <glib.h>
! 15: #include <glib/gprintf.h>
! 16:
1.1 root 17: #include "hw/virtio.h"
18: #include "hw/pc.h"
19: #include "qemu_socket.h"
20: #include "hw/virtio-pci.h"
21: #include "virtio-9p.h"
22: #include "fsdev/qemu-fsdev.h"
23: #include "virtio-9p-xattr.h"
1.1.1.2 ! root 24: #include "virtio-9p-coth.h"
! 25: #include "trace.h"
! 26: #include "migration.h"
! 27:
! 28: int open_fd_hw;
! 29: int total_open_fd;
! 30: static int open_fd_rc;
1.1 root 31:
32: enum {
33: Oread = 0x00,
34: Owrite = 0x01,
35: Ordwr = 0x02,
36: Oexec = 0x03,
37: Oexcl = 0x04,
38: Otrunc = 0x10,
39: Orexec = 0x20,
40: Orclose = 0x40,
41: Oappend = 0x80,
42: };
43:
44: static int omode_to_uflags(int8_t mode)
45: {
46: int ret = 0;
47:
48: switch (mode & 3) {
49: case Oread:
50: ret = O_RDONLY;
51: break;
52: case Ordwr:
53: ret = O_RDWR;
54: break;
55: case Owrite:
56: ret = O_WRONLY;
57: break;
58: case Oexec:
59: ret = O_RDONLY;
60: break;
61: }
62:
63: if (mode & Otrunc) {
64: ret |= O_TRUNC;
65: }
66:
67: if (mode & Oappend) {
68: ret |= O_APPEND;
69: }
70:
71: if (mode & Oexcl) {
72: ret |= O_EXCL;
73: }
74:
75: return ret;
76: }
77:
1.1.1.2 ! root 78: struct dotl_openflag_map {
! 79: int dotl_flag;
! 80: int open_flag;
! 81: };
1.1 root 82:
1.1.1.2 ! root 83: static int dotl_to_open_flags(int flags)
1.1 root 84: {
1.1.1.2 ! root 85: int i;
! 86: /*
! 87: * We have same bits for P9_DOTL_READONLY, P9_DOTL_WRONLY
! 88: * and P9_DOTL_NOACCESS
! 89: */
! 90: int oflags = flags & O_ACCMODE;
1.1 root 91:
1.1.1.2 ! root 92: struct dotl_openflag_map dotl_oflag_map[] = {
! 93: { P9_DOTL_CREATE, O_CREAT },
! 94: { P9_DOTL_EXCL, O_EXCL },
! 95: { P9_DOTL_NOCTTY , O_NOCTTY },
! 96: { P9_DOTL_TRUNC, O_TRUNC },
! 97: { P9_DOTL_APPEND, O_APPEND },
! 98: { P9_DOTL_NONBLOCK, O_NONBLOCK } ,
! 99: { P9_DOTL_DSYNC, O_DSYNC },
! 100: { P9_DOTL_FASYNC, FASYNC },
! 101: { P9_DOTL_DIRECT, O_DIRECT },
! 102: { P9_DOTL_LARGEFILE, O_LARGEFILE },
! 103: { P9_DOTL_DIRECTORY, O_DIRECTORY },
! 104: { P9_DOTL_NOFOLLOW, O_NOFOLLOW },
! 105: { P9_DOTL_NOATIME, O_NOATIME },
! 106: { P9_DOTL_SYNC, O_SYNC },
! 107: };
! 108:
! 109: for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) {
! 110: if (flags & dotl_oflag_map[i].dotl_flag) {
! 111: oflags |= dotl_oflag_map[i].open_flag;
! 112: }
1.1 root 113: }
114:
1.1.1.2 ! root 115: return oflags;
1.1 root 116: }
117:
1.1.1.2 ! root 118: void cred_init(FsCred *credp)
1.1 root 119: {
1.1.1.2 ! root 120: credp->fc_uid = -1;
! 121: credp->fc_gid = -1;
! 122: credp->fc_mode = -1;
! 123: credp->fc_rdev = -1;
1.1 root 124: }
125:
1.1.1.2 ! root 126: static int get_dotl_openflags(V9fsState *s, int oflags)
1.1 root 127: {
1.1.1.2 ! root 128: int flags;
! 129: /*
! 130: * Filter the client open flags
! 131: */
! 132: flags = dotl_to_open_flags(oflags);
! 133: flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
! 134: /*
! 135: * Ignore direct disk access hint until the server supports it.
! 136: */
! 137: flags &= ~O_DIRECT;
! 138: return flags;
1.1 root 139: }
140:
1.1.1.2 ! root 141: void v9fs_string_init(V9fsString *str)
1.1 root 142: {
143: str->data = NULL;
144: str->size = 0;
145: }
146:
1.1.1.2 ! root 147: void v9fs_string_free(V9fsString *str)
1.1 root 148: {
1.1.1.2 ! root 149: g_free(str->data);
1.1 root 150: str->data = NULL;
151: str->size = 0;
152: }
153:
1.1.1.2 ! root 154: void v9fs_string_null(V9fsString *str)
1.1 root 155: {
156: v9fs_string_free(str);
157: }
158:
1.1.1.2 ! root 159: void GCC_FMT_ATTR(2, 3)
! 160: v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
1.1 root 161: {
1.1.1.2 ! root 162: va_list ap;
1.1 root 163:
1.1.1.2 ! root 164: v9fs_string_free(str);
1.1 root 165:
1.1.1.2 ! root 166: va_start(ap, fmt);
! 167: str->size = g_vasprintf(&str->data, fmt, ap);
! 168: va_end(ap);
1.1 root 169: }
170:
1.1.1.2 ! root 171: void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs)
1.1 root 172: {
1.1.1.2 ! root 173: v9fs_string_free(lhs);
! 174: v9fs_string_sprintf(lhs, "%s", rhs->data);
1.1 root 175: }
176:
1.1.1.2 ! root 177: void v9fs_path_init(V9fsPath *path)
1.1 root 178: {
1.1.1.2 ! root 179: path->data = NULL;
! 180: path->size = 0;
! 181: }
1.1 root 182:
1.1.1.2 ! root 183: void v9fs_path_free(V9fsPath *path)
! 184: {
! 185: g_free(path->data);
! 186: path->data = NULL;
! 187: path->size = 0;
! 188: }
1.1 root 189:
1.1.1.2 ! root 190: void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs)
! 191: {
! 192: v9fs_path_free(lhs);
! 193: lhs->data = g_malloc(rhs->size);
! 194: memcpy(lhs->data, rhs->data, rhs->size);
! 195: lhs->size = rhs->size;
1.1 root 196: }
197:
1.1.1.2 ! root 198: int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath,
! 199: const char *name, V9fsPath *path)
1.1 root 200: {
1.1.1.2 ! root 201: int err;
! 202: err = s->ops->name_to_path(&s->ctx, dirpath, name, path);
! 203: if (err < 0) {
! 204: err = -errno;
! 205: }
! 206: return err;
1.1 root 207: }
208:
209: /*
210: * Return TRUE if s1 is an ancestor of s2.
211: *
212: * E.g. "a/b" is an ancestor of "a/b/c" but not of "a/bc/d".
213: * As a special case, We treat s1 as ancestor of s2 if they are same!
214: */
1.1.1.2 ! root 215: static int v9fs_path_is_ancestor(V9fsPath *s1, V9fsPath *s2)
1.1 root 216: {
1.1.1.2 ! root 217: if (!strncmp(s1->data, s2->data, s1->size - 1)) {
! 218: if (s2->data[s1->size - 1] == '\0' || s2->data[s1->size - 1] == '/') {
1.1 root 219: return 1;
220: }
221: }
222: return 0;
223: }
224:
225: static size_t v9fs_string_size(V9fsString *str)
226: {
227: return str->size;
228: }
229:
1.1.1.2 ! root 230: /*
! 231: * returns 0 if fid got re-opened, 1 if not, < 0 on error */
! 232: static int v9fs_reopen_fid(V9fsPDU *pdu, V9fsFidState *f)
! 233: {
! 234: int err = 1;
! 235: if (f->fid_type == P9_FID_FILE) {
! 236: if (f->fs.fd == -1) {
! 237: do {
! 238: err = v9fs_co_open(pdu, f, f->open_flags);
! 239: } while (err == -EINTR && !pdu->cancelled);
! 240: }
! 241: } else if (f->fid_type == P9_FID_DIR) {
! 242: if (f->fs.dir == NULL) {
! 243: do {
! 244: err = v9fs_co_opendir(pdu, f);
! 245: } while (err == -EINTR && !pdu->cancelled);
! 246: }
! 247: }
! 248: return err;
! 249: }
! 250:
! 251: static V9fsFidState *get_fid(V9fsPDU *pdu, int32_t fid)
1.1 root 252: {
1.1.1.2 ! root 253: int err;
1.1 root 254: V9fsFidState *f;
1.1.1.2 ! root 255: V9fsState *s = pdu->s;
1.1 root 256:
257: for (f = s->fid_list; f; f = f->next) {
1.1.1.2 ! root 258: BUG_ON(f->clunked);
1.1 root 259: if (f->fid == fid) {
1.1.1.2 ! root 260: /*
! 261: * Update the fid ref upfront so that
! 262: * we don't get reclaimed when we yield
! 263: * in open later.
! 264: */
! 265: f->ref++;
! 266: /*
! 267: * check whether we need to reopen the
! 268: * file. We might have closed the fd
! 269: * while trying to free up some file
! 270: * descriptors.
! 271: */
! 272: err = v9fs_reopen_fid(pdu, f);
! 273: if (err < 0) {
! 274: f->ref--;
! 275: return NULL;
! 276: }
! 277: /*
! 278: * Mark the fid as referenced so that the LRU
! 279: * reclaim won't close the file descriptor
! 280: */
! 281: f->flags |= FID_REFERENCED;
1.1 root 282: return f;
283: }
284: }
285: return NULL;
286: }
287:
288: static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
289: {
290: V9fsFidState *f;
291:
1.1.1.2 ! root 292: for (f = s->fid_list; f; f = f->next) {
! 293: /* If fid is already there return NULL */
! 294: BUG_ON(f->clunked);
! 295: if (f->fid == fid) {
! 296: return NULL;
! 297: }
1.1 root 298: }
1.1.1.2 ! root 299: f = g_malloc0(sizeof(V9fsFidState));
1.1 root 300: f->fid = fid;
301: f->fid_type = P9_FID_NONE;
1.1.1.2 ! root 302: f->ref = 1;
! 303: /*
! 304: * Mark the fid as referenced so that the LRU
! 305: * reclaim won't close the file descriptor
! 306: */
! 307: f->flags |= FID_REFERENCED;
1.1 root 308: f->next = s->fid_list;
309: s->fid_list = f;
310:
311: return f;
312: }
313:
1.1.1.2 ! root 314: static int v9fs_xattr_fid_clunk(V9fsPDU *pdu, V9fsFidState *fidp)
1.1 root 315: {
316: int retval = 0;
317:
318: if (fidp->fs.xattr.copied_len == -1) {
319: /* getxattr/listxattr fid */
320: goto free_value;
321: }
322: /*
323: * if this is fid for setxattr. clunk should
324: * result in setxattr localcall
325: */
326: if (fidp->fs.xattr.len != fidp->fs.xattr.copied_len) {
327: /* clunk after partial write */
328: retval = -EINVAL;
329: goto free_out;
330: }
331: if (fidp->fs.xattr.len) {
1.1.1.2 ! root 332: retval = v9fs_co_lsetxattr(pdu, &fidp->path, &fidp->fs.xattr.name,
1.1 root 333: fidp->fs.xattr.value,
334: fidp->fs.xattr.len,
335: fidp->fs.xattr.flags);
336: } else {
1.1.1.2 ! root 337: retval = v9fs_co_lremovexattr(pdu, &fidp->path, &fidp->fs.xattr.name);
1.1 root 338: }
339: free_out:
340: v9fs_string_free(&fidp->fs.xattr.name);
341: free_value:
342: if (fidp->fs.xattr.value) {
1.1.1.2 ! root 343: g_free(fidp->fs.xattr.value);
1.1 root 344: }
345: return retval;
346: }
347:
1.1.1.2 ! root 348: static int free_fid(V9fsPDU *pdu, V9fsFidState *fidp)
1.1 root 349: {
350: int retval = 0;
1.1.1.2 ! root 351:
! 352: if (fidp->fid_type == P9_FID_FILE) {
! 353: /* If we reclaimed the fd no need to close */
! 354: if (fidp->fs.fd != -1) {
! 355: retval = v9fs_co_close(pdu, &fidp->fs);
! 356: }
! 357: } else if (fidp->fid_type == P9_FID_DIR) {
! 358: if (fidp->fs.dir != NULL) {
! 359: retval = v9fs_co_closedir(pdu, &fidp->fs);
! 360: }
! 361: } else if (fidp->fid_type == P9_FID_XATTR) {
! 362: retval = v9fs_xattr_fid_clunk(pdu, fidp);
! 363: }
! 364: v9fs_path_free(&fidp->path);
! 365: g_free(fidp);
! 366: return retval;
! 367: }
! 368:
! 369: static void put_fid(V9fsPDU *pdu, V9fsFidState *fidp)
! 370: {
! 371: BUG_ON(!fidp->ref);
! 372: fidp->ref--;
! 373: /*
! 374: * Don't free the fid if it is in reclaim list
! 375: */
! 376: if (!fidp->ref && fidp->clunked) {
! 377: if (fidp->fid == pdu->s->root_fid) {
! 378: /*
! 379: * if the clunked fid is root fid then we
! 380: * have unmounted the fs on the client side.
! 381: * delete the migration blocker. Ideally, this
! 382: * should be hooked to transport close notification
! 383: */
! 384: if (pdu->s->migration_blocker) {
! 385: migrate_del_blocker(pdu->s->migration_blocker);
! 386: error_free(pdu->s->migration_blocker);
! 387: pdu->s->migration_blocker = NULL;
! 388: }
! 389: }
! 390: free_fid(pdu, fidp);
! 391: }
! 392: }
! 393:
! 394: static V9fsFidState *clunk_fid(V9fsState *s, int32_t fid)
! 395: {
1.1 root 396: V9fsFidState **fidpp, *fidp;
397:
398: for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) {
399: if ((*fidpp)->fid == fid) {
400: break;
401: }
402: }
403: if (*fidpp == NULL) {
1.1.1.2 ! root 404: return NULL;
1.1 root 405: }
406: fidp = *fidpp;
407: *fidpp = fidp->next;
1.1.1.2 ! root 408: fidp->clunked = 1;
! 409: return fidp;
! 410: }
1.1 root 411:
1.1.1.2 ! root 412: void v9fs_reclaim_fd(V9fsPDU *pdu)
! 413: {
! 414: int reclaim_count = 0;
! 415: V9fsState *s = pdu->s;
! 416: V9fsFidState *f, *reclaim_list = NULL;
1.1 root 417:
1.1.1.2 ! root 418: for (f = s->fid_list; f; f = f->next) {
! 419: /*
! 420: * Unlink fids cannot be reclaimed. Check
! 421: * for them and skip them. Also skip fids
! 422: * currently being operated on.
! 423: */
! 424: if (f->ref || f->flags & FID_NON_RECLAIMABLE) {
! 425: continue;
! 426: }
! 427: /*
! 428: * if it is a recently referenced fid
! 429: * we leave the fid untouched and clear the
! 430: * reference bit. We come back to it later
! 431: * in the next iteration. (a simple LRU without
! 432: * moving list elements around)
! 433: */
! 434: if (f->flags & FID_REFERENCED) {
! 435: f->flags &= ~FID_REFERENCED;
! 436: continue;
! 437: }
! 438: /*
! 439: * Add fids to reclaim list.
! 440: */
! 441: if (f->fid_type == P9_FID_FILE) {
! 442: if (f->fs.fd != -1) {
! 443: /*
! 444: * Up the reference count so that
! 445: * a clunk request won't free this fid
! 446: */
! 447: f->ref++;
! 448: f->rclm_lst = reclaim_list;
! 449: reclaim_list = f;
! 450: f->fs_reclaim.fd = f->fs.fd;
! 451: f->fs.fd = -1;
! 452: reclaim_count++;
! 453: }
! 454: } else if (f->fid_type == P9_FID_DIR) {
! 455: if (f->fs.dir != NULL) {
! 456: /*
! 457: * Up the reference count so that
! 458: * a clunk request won't free this fid
! 459: */
! 460: f->ref++;
! 461: f->rclm_lst = reclaim_list;
! 462: reclaim_list = f;
! 463: f->fs_reclaim.dir = f->fs.dir;
! 464: f->fs.dir = NULL;
! 465: reclaim_count++;
! 466: }
! 467: }
! 468: if (reclaim_count >= open_fd_rc) {
! 469: break;
! 470: }
! 471: }
! 472: /*
! 473: * Now close the fid in reclaim list. Free them if they
! 474: * are already clunked.
! 475: */
! 476: while (reclaim_list) {
! 477: f = reclaim_list;
! 478: reclaim_list = f->rclm_lst;
! 479: if (f->fid_type == P9_FID_FILE) {
! 480: v9fs_co_close(pdu, &f->fs_reclaim);
! 481: } else if (f->fid_type == P9_FID_DIR) {
! 482: v9fs_co_closedir(pdu, &f->fs_reclaim);
! 483: }
! 484: f->rclm_lst = NULL;
! 485: /*
! 486: * Now drop the fid reference, free it
! 487: * if clunked.
! 488: */
! 489: put_fid(pdu, f);
! 490: }
1.1 root 491: }
492:
1.1.1.2 ! root 493: static int v9fs_mark_fids_unreclaim(V9fsPDU *pdu, V9fsPath *path)
! 494: {
! 495: int err;
! 496: V9fsState *s = pdu->s;
! 497: V9fsFidState *fidp, head_fid;
1.1 root 498:
1.1.1.2 ! root 499: head_fid.next = s->fid_list;
! 500: for (fidp = s->fid_list; fidp; fidp = fidp->next) {
! 501: if (fidp->path.size != path->size) {
! 502: continue;
! 503: }
! 504: if (!memcmp(fidp->path.data, path->data, path->size)) {
! 505: /* Mark the fid non reclaimable. */
! 506: fidp->flags |= FID_NON_RECLAIMABLE;
! 507:
! 508: /* reopen the file/dir if already closed */
! 509: err = v9fs_reopen_fid(pdu, fidp);
! 510: if (err < 0) {
! 511: return -1;
! 512: }
! 513: /*
! 514: * Go back to head of fid list because
! 515: * the list could have got updated when
! 516: * switched to the worker thread
! 517: */
! 518: if (err == 0) {
! 519: fidp = &head_fid;
! 520: }
! 521: }
! 522: }
! 523: return 0;
! 524: }
! 525:
! 526: static void virtfs_reset(V9fsPDU *pdu)
! 527: {
! 528: V9fsState *s = pdu->s;
! 529: V9fsFidState *fidp = NULL;
! 530:
! 531: /* Free all fids */
! 532: while (s->fid_list) {
! 533: fidp = s->fid_list;
! 534: s->fid_list = fidp->next;
! 535:
! 536: if (fidp->ref) {
! 537: fidp->clunked = 1;
! 538: } else {
! 539: free_fid(pdu, fidp);
! 540: }
! 541: }
! 542: if (fidp) {
! 543: /* One or more unclunked fids found... */
! 544: error_report("9pfs:%s: One or more uncluncked fids "
! 545: "found during reset", __func__);
! 546: }
! 547: return;
! 548: }
! 549:
! 550: #define P9_QID_TYPE_DIR 0x80
! 551: #define P9_QID_TYPE_SYMLINK 0x02
! 552:
! 553: #define P9_STAT_MODE_DIR 0x80000000
! 554: #define P9_STAT_MODE_APPEND 0x40000000
! 555: #define P9_STAT_MODE_EXCL 0x20000000
! 556: #define P9_STAT_MODE_MOUNT 0x10000000
! 557: #define P9_STAT_MODE_AUTH 0x08000000
! 558: #define P9_STAT_MODE_TMP 0x04000000
1.1 root 559: #define P9_STAT_MODE_SYMLINK 0x02000000
560: #define P9_STAT_MODE_LINK 0x01000000
561: #define P9_STAT_MODE_DEVICE 0x00800000
562: #define P9_STAT_MODE_NAMED_PIPE 0x00200000
563: #define P9_STAT_MODE_SOCKET 0x00100000
564: #define P9_STAT_MODE_SETUID 0x00080000
565: #define P9_STAT_MODE_SETGID 0x00040000
566: #define P9_STAT_MODE_SETVTX 0x00010000
567:
568: #define P9_STAT_MODE_TYPE_BITS (P9_STAT_MODE_DIR | \
569: P9_STAT_MODE_SYMLINK | \
570: P9_STAT_MODE_LINK | \
571: P9_STAT_MODE_DEVICE | \
572: P9_STAT_MODE_NAMED_PIPE | \
573: P9_STAT_MODE_SOCKET)
574:
575: /* This is the algorithm from ufs in spfs */
576: static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
577: {
578: size_t size;
579:
1.1.1.2 ! root 580: memset(&qidp->path, 0, sizeof(qidp->path));
1.1 root 581: size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
582: memcpy(&qidp->path, &stbuf->st_ino, size);
583: qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8);
584: qidp->type = 0;
585: if (S_ISDIR(stbuf->st_mode)) {
586: qidp->type |= P9_QID_TYPE_DIR;
587: }
588: if (S_ISLNK(stbuf->st_mode)) {
589: qidp->type |= P9_QID_TYPE_SYMLINK;
590: }
591: }
592:
1.1.1.2 ! root 593: static int fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp, V9fsQID *qidp)
1.1 root 594: {
595: struct stat stbuf;
596: int err;
597:
1.1.1.2 ! root 598: err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
! 599: if (err < 0) {
1.1 root 600: return err;
601: }
602: stat_to_qid(&stbuf, qidp);
603: return 0;
604: }
605:
606: static V9fsPDU *alloc_pdu(V9fsState *s)
607: {
608: V9fsPDU *pdu = NULL;
609:
610: if (!QLIST_EMPTY(&s->free_list)) {
1.1.1.2 ! root 611: pdu = QLIST_FIRST(&s->free_list);
! 612: QLIST_REMOVE(pdu, next);
! 613: QLIST_INSERT_HEAD(&s->active_list, pdu, next);
1.1 root 614: }
615: return pdu;
616: }
617:
618: static void free_pdu(V9fsState *s, V9fsPDU *pdu)
619: {
620: if (pdu) {
1.1.1.2 ! root 621: /*
! 622: * Cancelled pdu are added back to the freelist
! 623: * by flush request .
! 624: */
! 625: if (!pdu->cancelled) {
! 626: QLIST_REMOVE(pdu, next);
! 627: QLIST_INSERT_HEAD(&s->free_list, pdu, next);
1.1 root 628: }
629: }
630: }
631:
632: size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
633: size_t offset, size_t size, int pack)
634: {
635: int i = 0;
636: size_t copied = 0;
637:
638: for (i = 0; size && i < sg_count; i++) {
639: size_t len;
640: if (offset >= sg[i].iov_len) {
641: /* skip this sg */
642: offset -= sg[i].iov_len;
643: continue;
644: } else {
645: len = MIN(sg[i].iov_len - offset, size);
646: if (pack) {
647: memcpy(sg[i].iov_base + offset, addr, len);
648: } else {
649: memcpy(addr, sg[i].iov_base + offset, len);
650: }
651: size -= len;
652: copied += len;
653: addr += len;
654: if (size) {
655: offset = 0;
656: continue;
657: }
658: }
659: }
660:
661: return copied;
662: }
663:
664: static size_t pdu_unpack(void *dst, V9fsPDU *pdu, size_t offset, size_t size)
665: {
666: return pdu_packunpack(dst, pdu->elem.out_sg, pdu->elem.out_num,
667: offset, size, 0);
668: }
669:
670: static size_t pdu_pack(V9fsPDU *pdu, size_t offset, const void *src,
671: size_t size)
672: {
673: return pdu_packunpack((void *)src, pdu->elem.in_sg, pdu->elem.in_num,
674: offset, size, 1);
675: }
676:
677: static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
678: {
679: size_t old_offset = offset;
680: va_list ap;
681: int i;
682:
683: va_start(ap, fmt);
684: for (i = 0; fmt[i]; i++) {
685: switch (fmt[i]) {
686: case 'b': {
687: uint8_t *valp = va_arg(ap, uint8_t *);
688: offset += pdu_unpack(valp, pdu, offset, sizeof(*valp));
689: break;
690: }
691: case 'w': {
692: uint16_t val, *valp;
693: valp = va_arg(ap, uint16_t *);
694: offset += pdu_unpack(&val, pdu, offset, sizeof(val));
695: *valp = le16_to_cpu(val);
696: break;
697: }
698: case 'd': {
699: uint32_t val, *valp;
700: valp = va_arg(ap, uint32_t *);
701: offset += pdu_unpack(&val, pdu, offset, sizeof(val));
702: *valp = le32_to_cpu(val);
703: break;
704: }
705: case 'q': {
706: uint64_t val, *valp;
707: valp = va_arg(ap, uint64_t *);
708: offset += pdu_unpack(&val, pdu, offset, sizeof(val));
709: *valp = le64_to_cpu(val);
710: break;
711: }
712: case 's': {
713: V9fsString *str = va_arg(ap, V9fsString *);
714: offset += pdu_unmarshal(pdu, offset, "w", &str->size);
715: /* FIXME: sanity check str->size */
1.1.1.2 ! root 716: str->data = g_malloc(str->size + 1);
1.1 root 717: offset += pdu_unpack(str->data, pdu, offset, str->size);
718: str->data[str->size] = 0;
719: break;
720: }
721: case 'Q': {
722: V9fsQID *qidp = va_arg(ap, V9fsQID *);
723: offset += pdu_unmarshal(pdu, offset, "bdq",
724: &qidp->type, &qidp->version, &qidp->path);
725: break;
726: }
727: case 'S': {
728: V9fsStat *statp = va_arg(ap, V9fsStat *);
729: offset += pdu_unmarshal(pdu, offset, "wwdQdddqsssssddd",
730: &statp->size, &statp->type, &statp->dev,
731: &statp->qid, &statp->mode, &statp->atime,
732: &statp->mtime, &statp->length,
733: &statp->name, &statp->uid, &statp->gid,
734: &statp->muid, &statp->extension,
735: &statp->n_uid, &statp->n_gid,
736: &statp->n_muid);
737: break;
738: }
739: case 'I': {
740: V9fsIattr *iattr = va_arg(ap, V9fsIattr *);
741: offset += pdu_unmarshal(pdu, offset, "ddddqqqqq",
742: &iattr->valid, &iattr->mode,
743: &iattr->uid, &iattr->gid, &iattr->size,
744: &iattr->atime_sec, &iattr->atime_nsec,
745: &iattr->mtime_sec, &iattr->mtime_nsec);
746: break;
747: }
748: default:
749: break;
750: }
751: }
752:
753: va_end(ap);
754:
755: return offset - old_offset;
756: }
757:
758: static size_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
759: {
760: size_t old_offset = offset;
761: va_list ap;
762: int i;
763:
764: va_start(ap, fmt);
765: for (i = 0; fmt[i]; i++) {
766: switch (fmt[i]) {
767: case 'b': {
768: uint8_t val = va_arg(ap, int);
769: offset += pdu_pack(pdu, offset, &val, sizeof(val));
770: break;
771: }
772: case 'w': {
773: uint16_t val;
774: cpu_to_le16w(&val, va_arg(ap, int));
775: offset += pdu_pack(pdu, offset, &val, sizeof(val));
776: break;
777: }
778: case 'd': {
779: uint32_t val;
780: cpu_to_le32w(&val, va_arg(ap, uint32_t));
781: offset += pdu_pack(pdu, offset, &val, sizeof(val));
782: break;
783: }
784: case 'q': {
785: uint64_t val;
786: cpu_to_le64w(&val, va_arg(ap, uint64_t));
787: offset += pdu_pack(pdu, offset, &val, sizeof(val));
788: break;
789: }
790: case 's': {
791: V9fsString *str = va_arg(ap, V9fsString *);
792: offset += pdu_marshal(pdu, offset, "w", str->size);
793: offset += pdu_pack(pdu, offset, str->data, str->size);
794: break;
795: }
796: case 'Q': {
797: V9fsQID *qidp = va_arg(ap, V9fsQID *);
798: offset += pdu_marshal(pdu, offset, "bdq",
799: qidp->type, qidp->version, qidp->path);
800: break;
801: }
802: case 'S': {
803: V9fsStat *statp = va_arg(ap, V9fsStat *);
804: offset += pdu_marshal(pdu, offset, "wwdQdddqsssssddd",
805: statp->size, statp->type, statp->dev,
806: &statp->qid, statp->mode, statp->atime,
807: statp->mtime, statp->length, &statp->name,
808: &statp->uid, &statp->gid, &statp->muid,
809: &statp->extension, statp->n_uid,
810: statp->n_gid, statp->n_muid);
811: break;
812: }
813: case 'A': {
814: V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *);
815: offset += pdu_marshal(pdu, offset, "qQdddqqqqqqqqqqqqqqq",
816: statp->st_result_mask,
817: &statp->qid, statp->st_mode,
818: statp->st_uid, statp->st_gid,
819: statp->st_nlink, statp->st_rdev,
820: statp->st_size, statp->st_blksize, statp->st_blocks,
821: statp->st_atime_sec, statp->st_atime_nsec,
822: statp->st_mtime_sec, statp->st_mtime_nsec,
823: statp->st_ctime_sec, statp->st_ctime_nsec,
824: statp->st_btime_sec, statp->st_btime_nsec,
825: statp->st_gen, statp->st_data_version);
826: break;
827: }
828: default:
829: break;
830: }
831: }
832: va_end(ap);
833:
834: return offset - old_offset;
835: }
836:
837: static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
838: {
839: int8_t id = pdu->id + 1; /* Response */
840:
841: if (len < 0) {
842: int err = -len;
843: len = 7;
844:
845: if (s->proto_version != V9FS_PROTO_2000L) {
846: V9fsString str;
847:
848: str.data = strerror(err);
849: str.size = strlen(str.data);
850:
851: len += pdu_marshal(pdu, len, "s", &str);
852: id = P9_RERROR;
853: }
854:
855: len += pdu_marshal(pdu, len, "d", err);
856:
857: if (s->proto_version == V9FS_PROTO_2000L) {
858: id = P9_RLERROR;
859: }
1.1.1.2 ! root 860: trace_v9fs_rerror(pdu->tag, pdu->id, err); /* Trace ERROR */
1.1 root 861: }
862:
863: /* fill out the header */
864: pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag);
865:
866: /* keep these in sync */
867: pdu->size = len;
868: pdu->id = id;
869:
870: /* push onto queue and notify */
871: virtqueue_push(s->vq, &pdu->elem, len);
872:
873: /* FIXME: we should batch these completions */
874: virtio_notify(&s->vdev, s->vq);
875:
1.1.1.2 ! root 876: /* Now wakeup anybody waiting in flush for this request */
! 877: qemu_co_queue_next(&pdu->complete);
! 878:
1.1 root 879: free_pdu(s, pdu);
880: }
881:
882: static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
883: {
884: mode_t ret;
885:
886: ret = mode & 0777;
887: if (mode & P9_STAT_MODE_DIR) {
888: ret |= S_IFDIR;
889: }
890:
891: if (mode & P9_STAT_MODE_SYMLINK) {
892: ret |= S_IFLNK;
893: }
894: if (mode & P9_STAT_MODE_SOCKET) {
895: ret |= S_IFSOCK;
896: }
897: if (mode & P9_STAT_MODE_NAMED_PIPE) {
898: ret |= S_IFIFO;
899: }
900: if (mode & P9_STAT_MODE_DEVICE) {
901: if (extension && extension->data[0] == 'c') {
902: ret |= S_IFCHR;
903: } else {
904: ret |= S_IFBLK;
905: }
906: }
907:
908: if (!(ret&~0777)) {
909: ret |= S_IFREG;
910: }
911:
912: if (mode & P9_STAT_MODE_SETUID) {
913: ret |= S_ISUID;
914: }
915: if (mode & P9_STAT_MODE_SETGID) {
916: ret |= S_ISGID;
917: }
918: if (mode & P9_STAT_MODE_SETVTX) {
919: ret |= S_ISVTX;
920: }
921:
922: return ret;
923: }
924:
925: static int donttouch_stat(V9fsStat *stat)
926: {
927: if (stat->type == -1 &&
928: stat->dev == -1 &&
929: stat->qid.type == -1 &&
930: stat->qid.version == -1 &&
931: stat->qid.path == -1 &&
932: stat->mode == -1 &&
933: stat->atime == -1 &&
934: stat->mtime == -1 &&
935: stat->length == -1 &&
936: !stat->name.size &&
937: !stat->uid.size &&
938: !stat->gid.size &&
939: !stat->muid.size &&
940: stat->n_uid == -1 &&
941: stat->n_gid == -1 &&
942: stat->n_muid == -1) {
943: return 1;
944: }
945:
946: return 0;
947: }
948:
949: static void v9fs_stat_free(V9fsStat *stat)
950: {
951: v9fs_string_free(&stat->name);
952: v9fs_string_free(&stat->uid);
953: v9fs_string_free(&stat->gid);
954: v9fs_string_free(&stat->muid);
955: v9fs_string_free(&stat->extension);
956: }
957:
958: static uint32_t stat_to_v9mode(const struct stat *stbuf)
959: {
960: uint32_t mode;
961:
962: mode = stbuf->st_mode & 0777;
963: if (S_ISDIR(stbuf->st_mode)) {
964: mode |= P9_STAT_MODE_DIR;
965: }
966:
967: if (S_ISLNK(stbuf->st_mode)) {
968: mode |= P9_STAT_MODE_SYMLINK;
969: }
970:
971: if (S_ISSOCK(stbuf->st_mode)) {
972: mode |= P9_STAT_MODE_SOCKET;
973: }
974:
975: if (S_ISFIFO(stbuf->st_mode)) {
976: mode |= P9_STAT_MODE_NAMED_PIPE;
977: }
978:
979: if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) {
980: mode |= P9_STAT_MODE_DEVICE;
981: }
982:
983: if (stbuf->st_mode & S_ISUID) {
984: mode |= P9_STAT_MODE_SETUID;
985: }
986:
987: if (stbuf->st_mode & S_ISGID) {
988: mode |= P9_STAT_MODE_SETGID;
989: }
990:
991: if (stbuf->st_mode & S_ISVTX) {
992: mode |= P9_STAT_MODE_SETVTX;
993: }
994:
995: return mode;
996: }
997:
1.1.1.2 ! root 998: static int stat_to_v9stat(V9fsPDU *pdu, V9fsPath *name,
1.1 root 999: const struct stat *stbuf,
1000: V9fsStat *v9stat)
1001: {
1002: int err;
1003: const char *str;
1004:
1005: memset(v9stat, 0, sizeof(*v9stat));
1006:
1007: stat_to_qid(stbuf, &v9stat->qid);
1008: v9stat->mode = stat_to_v9mode(stbuf);
1009: v9stat->atime = stbuf->st_atime;
1010: v9stat->mtime = stbuf->st_mtime;
1011: v9stat->length = stbuf->st_size;
1012:
1013: v9fs_string_null(&v9stat->uid);
1014: v9fs_string_null(&v9stat->gid);
1015: v9fs_string_null(&v9stat->muid);
1016:
1017: v9stat->n_uid = stbuf->st_uid;
1018: v9stat->n_gid = stbuf->st_gid;
1019: v9stat->n_muid = 0;
1020:
1021: v9fs_string_null(&v9stat->extension);
1022:
1023: if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
1.1.1.2 ! root 1024: err = v9fs_co_readlink(pdu, name, &v9stat->extension);
! 1025: if (err < 0) {
1.1 root 1026: return err;
1027: }
1028: } else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
1029: v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
1030: S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
1031: major(stbuf->st_rdev), minor(stbuf->st_rdev));
1032: } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) {
1033: v9fs_string_sprintf(&v9stat->extension, "%s %lu",
1034: "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink);
1035: }
1036:
1037: str = strrchr(name->data, '/');
1038: if (str) {
1039: str += 1;
1040: } else {
1041: str = name->data;
1042: }
1043:
1044: v9fs_string_sprintf(&v9stat->name, "%s", str);
1045:
1046: v9stat->size = 61 +
1047: v9fs_string_size(&v9stat->name) +
1048: v9fs_string_size(&v9stat->uid) +
1049: v9fs_string_size(&v9stat->gid) +
1050: v9fs_string_size(&v9stat->muid) +
1051: v9fs_string_size(&v9stat->extension);
1052: return 0;
1053: }
1054:
1055: #define P9_STATS_MODE 0x00000001ULL
1056: #define P9_STATS_NLINK 0x00000002ULL
1057: #define P9_STATS_UID 0x00000004ULL
1058: #define P9_STATS_GID 0x00000008ULL
1059: #define P9_STATS_RDEV 0x00000010ULL
1060: #define P9_STATS_ATIME 0x00000020ULL
1061: #define P9_STATS_MTIME 0x00000040ULL
1062: #define P9_STATS_CTIME 0x00000080ULL
1063: #define P9_STATS_INO 0x00000100ULL
1064: #define P9_STATS_SIZE 0x00000200ULL
1065: #define P9_STATS_BLOCKS 0x00000400ULL
1066:
1067: #define P9_STATS_BTIME 0x00000800ULL
1068: #define P9_STATS_GEN 0x00001000ULL
1069: #define P9_STATS_DATA_VERSION 0x00002000ULL
1070:
1071: #define P9_STATS_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */
1072: #define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */
1073:
1074:
1075: static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf,
1.1.1.2 ! root 1076: V9fsStatDotl *v9lstat)
1.1 root 1077: {
1078: memset(v9lstat, 0, sizeof(*v9lstat));
1079:
1080: v9lstat->st_mode = stbuf->st_mode;
1081: v9lstat->st_nlink = stbuf->st_nlink;
1082: v9lstat->st_uid = stbuf->st_uid;
1083: v9lstat->st_gid = stbuf->st_gid;
1084: v9lstat->st_rdev = stbuf->st_rdev;
1085: v9lstat->st_size = stbuf->st_size;
1086: v9lstat->st_blksize = stbuf->st_blksize;
1087: v9lstat->st_blocks = stbuf->st_blocks;
1088: v9lstat->st_atime_sec = stbuf->st_atime;
1089: v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
1090: v9lstat->st_mtime_sec = stbuf->st_mtime;
1091: v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
1092: v9lstat->st_ctime_sec = stbuf->st_ctime;
1093: v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec;
1094: /* Currently we only support BASIC fields in stat */
1095: v9lstat->st_result_mask = P9_STATS_BASIC;
1096:
1097: stat_to_qid(stbuf, &v9lstat->qid);
1098: }
1099:
1100: static void print_sg(struct iovec *sg, int cnt)
1101: {
1102: int i;
1103:
1104: printf("sg[%d]: {", cnt);
1105: for (i = 0; i < cnt; i++) {
1106: if (i) {
1107: printf(", ");
1108: }
1109: printf("(%p, %zd)", sg[i].iov_base, sg[i].iov_len);
1110: }
1111: printf("}\n");
1112: }
1113:
1.1.1.2 ! root 1114: /* Will call this only for path name based fid */
! 1115: static void v9fs_fix_path(V9fsPath *dst, V9fsPath *src, int len)
! 1116: {
! 1117: V9fsPath str;
! 1118: v9fs_path_init(&str);
! 1119: v9fs_path_copy(&str, dst);
! 1120: v9fs_string_sprintf((V9fsString *)dst, "%s%s", src->data, str.data+len);
! 1121: v9fs_path_free(&str);
! 1122: /* +1 to include terminating NULL */
! 1123: dst->size++;
! 1124: }
! 1125:
! 1126: static inline bool is_ro_export(FsContext *ctx)
1.1 root 1127: {
1.1.1.2 ! root 1128: return ctx->export_flags & V9FS_RDONLY;
1.1 root 1129: }
1130:
1.1.1.2 ! root 1131: static void v9fs_version(void *opaque)
1.1 root 1132: {
1.1.1.2 ! root 1133: V9fsPDU *pdu = opaque;
! 1134: V9fsState *s = pdu->s;
1.1 root 1135: V9fsString version;
1136: size_t offset = 7;
1137:
1138: pdu_unmarshal(pdu, offset, "ds", &s->msize, &version);
1.1.1.2 ! root 1139: trace_v9fs_version(pdu->tag, pdu->id, s->msize, version.data);
! 1140:
! 1141: virtfs_reset(pdu);
1.1 root 1142:
1143: if (!strcmp(version.data, "9P2000.u")) {
1144: s->proto_version = V9FS_PROTO_2000U;
1145: } else if (!strcmp(version.data, "9P2000.L")) {
1146: s->proto_version = V9FS_PROTO_2000L;
1147: } else {
1148: v9fs_string_sprintf(&version, "unknown");
1149: }
1150:
1151: offset += pdu_marshal(pdu, offset, "ds", s->msize, &version);
1.1.1.2 ! root 1152: trace_v9fs_version_return(pdu->tag, pdu->id, s->msize, version.data);
! 1153:
1.1 root 1154: complete_pdu(s, pdu, offset);
1155:
1156: v9fs_string_free(&version);
1.1.1.2 ! root 1157: return;
1.1 root 1158: }
1159:
1.1.1.2 ! root 1160: static void v9fs_attach(void *opaque)
1.1 root 1161: {
1.1.1.2 ! root 1162: V9fsPDU *pdu = opaque;
! 1163: V9fsState *s = pdu->s;
1.1 root 1164: int32_t fid, afid, n_uname;
1165: V9fsString uname, aname;
1166: V9fsFidState *fidp;
1167: size_t offset = 7;
1.1.1.2 ! root 1168: V9fsQID qid;
1.1 root 1169: ssize_t err;
1170:
1171: pdu_unmarshal(pdu, offset, "ddssd", &fid, &afid, &uname, &aname, &n_uname);
1.1.1.2 ! root 1172: trace_v9fs_attach(pdu->tag, pdu->id, fid, afid, uname.data, aname.data);
1.1 root 1173:
1174: fidp = alloc_fid(s, fid);
1175: if (fidp == NULL) {
1176: err = -EINVAL;
1.1.1.2 ! root 1177: goto out_nofid;
1.1 root 1178: }
1179: fidp->uid = n_uname;
1.1.1.2 ! root 1180: err = v9fs_co_name_to_path(pdu, NULL, "/", &fidp->path);
! 1181: if (err < 0) {
1.1 root 1182: err = -EINVAL;
1.1.1.2 ! root 1183: clunk_fid(s, fid);
! 1184: goto out;
! 1185: }
! 1186: err = fid_to_qid(pdu, fidp, &qid);
! 1187: if (err < 0) {
! 1188: err = -EINVAL;
! 1189: clunk_fid(s, fid);
1.1 root 1190: goto out;
1191: }
1192: offset += pdu_marshal(pdu, offset, "Q", &qid);
1193: err = offset;
1.1.1.2 ! root 1194: trace_v9fs_attach_return(pdu->tag, pdu->id,
! 1195: qid.type, qid.version, qid.path);
! 1196: s->root_fid = fid;
! 1197: /* disable migration */
! 1198: error_set(&s->migration_blocker, QERR_VIRTFS_FEATURE_BLOCKS_MIGRATION,
! 1199: s->ctx.fs_root, s->tag);
! 1200: migrate_add_blocker(s->migration_blocker);
1.1 root 1201: out:
1.1.1.2 ! root 1202: put_fid(pdu, fidp);
! 1203: out_nofid:
1.1 root 1204: complete_pdu(s, pdu, err);
1205: v9fs_string_free(&uname);
1206: v9fs_string_free(&aname);
1207: }
1208:
1.1.1.2 ! root 1209: static void v9fs_stat(void *opaque)
1.1 root 1210: {
1211: int32_t fid;
1.1.1.2 ! root 1212: V9fsStat v9stat;
1.1 root 1213: ssize_t err = 0;
1.1.1.2 ! root 1214: size_t offset = 7;
! 1215: struct stat stbuf;
! 1216: V9fsFidState *fidp;
! 1217: V9fsPDU *pdu = opaque;
! 1218: V9fsState *s = pdu->s;
1.1 root 1219:
1.1.1.2 ! root 1220: pdu_unmarshal(pdu, offset, "d", &fid);
! 1221: trace_v9fs_stat(pdu->tag, pdu->id, fid);
1.1 root 1222:
1.1.1.2 ! root 1223: fidp = get_fid(pdu, fid);
! 1224: if (fidp == NULL) {
1.1 root 1225: err = -ENOENT;
1.1.1.2 ! root 1226: goto out_nofid;
! 1227: }
! 1228: err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
! 1229: if (err < 0) {
1.1 root 1230: goto out;
1231: }
1.1.1.2 ! root 1232: err = stat_to_v9stat(pdu, &fidp->path, &stbuf, &v9stat);
! 1233: if (err < 0) {
1.1 root 1234: goto out;
1235: }
1.1.1.2 ! root 1236: offset += pdu_marshal(pdu, offset, "wS", 0, &v9stat);
! 1237: err = offset;
! 1238: trace_v9fs_stat_return(pdu->tag, pdu->id, v9stat.mode,
! 1239: v9stat.atime, v9stat.mtime, v9stat.length);
! 1240: v9fs_stat_free(&v9stat);
1.1 root 1241: out:
1.1.1.2 ! root 1242: put_fid(pdu, fidp);
! 1243: out_nofid:
! 1244: complete_pdu(s, pdu, err);
1.1 root 1245: }
1246:
1.1.1.2 ! root 1247: static void v9fs_getattr(void *opaque)
1.1 root 1248: {
1249: int32_t fid;
1.1.1.2 ! root 1250: size_t offset = 7;
! 1251: ssize_t retval = 0;
! 1252: struct stat stbuf;
1.1 root 1253: V9fsFidState *fidp;
1254: uint64_t request_mask;
1.1.1.2 ! root 1255: V9fsStatDotl v9stat_dotl;
! 1256: V9fsPDU *pdu = opaque;
! 1257: V9fsState *s = pdu->s;
1.1 root 1258:
1.1.1.2 ! root 1259: pdu_unmarshal(pdu, offset, "dq", &fid, &request_mask);
! 1260: trace_v9fs_getattr(pdu->tag, pdu->id, fid, request_mask);
1.1 root 1261:
1.1.1.2 ! root 1262: fidp = get_fid(pdu, fid);
1.1 root 1263: if (fidp == NULL) {
1.1.1.2 ! root 1264: retval = -ENOENT;
! 1265: goto out_nofid;
1.1 root 1266: }
1.1.1.2 ! root 1267: /*
! 1268: * Currently we only support BASIC fields in stat, so there is no
1.1 root 1269: * need to look at request_mask.
1270: */
1.1.1.2 ! root 1271: retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
! 1272: if (retval < 0) {
! 1273: goto out;
! 1274: }
! 1275: stat_to_v9stat_dotl(s, &stbuf, &v9stat_dotl);
1.1 root 1276:
1.1.1.2 ! root 1277: /* fill st_gen if requested and supported by underlying fs */
! 1278: if (request_mask & P9_STATS_GEN) {
! 1279: retval = v9fs_co_st_gen(pdu, &fidp->path, stbuf.st_mode, &v9stat_dotl);
! 1280: if (retval < 0) {
! 1281: goto out;
! 1282: }
! 1283: v9stat_dotl.st_result_mask |= P9_STATS_GEN;
! 1284: }
! 1285: retval = offset;
! 1286: retval += pdu_marshal(pdu, offset, "A", &v9stat_dotl);
! 1287: trace_v9fs_getattr_return(pdu->tag, pdu->id, v9stat_dotl.st_result_mask,
! 1288: v9stat_dotl.st_mode, v9stat_dotl.st_uid,
! 1289: v9stat_dotl.st_gid);
! 1290: out:
! 1291: put_fid(pdu, fidp);
! 1292: out_nofid:
! 1293: complete_pdu(s, pdu, retval);
1.1 root 1294: }
1295:
1296: /* From Linux kernel code */
1297: #define ATTR_MODE (1 << 0)
1298: #define ATTR_UID (1 << 1)
1299: #define ATTR_GID (1 << 2)
1300: #define ATTR_SIZE (1 << 3)
1301: #define ATTR_ATIME (1 << 4)
1302: #define ATTR_MTIME (1 << 5)
1303: #define ATTR_CTIME (1 << 6)
1304: #define ATTR_MASK 127
1305: #define ATTR_ATIME_SET (1 << 7)
1306: #define ATTR_MTIME_SET (1 << 8)
1307:
1.1.1.2 ! root 1308: static void v9fs_setattr(void *opaque)
1.1 root 1309: {
1.1.1.2 ! root 1310: int err = 0;
! 1311: int32_t fid;
! 1312: V9fsFidState *fidp;
! 1313: size_t offset = 7;
! 1314: V9fsIattr v9iattr;
! 1315: V9fsPDU *pdu = opaque;
! 1316: V9fsState *s = pdu->s;
1.1 root 1317:
1.1.1.2 ! root 1318: pdu_unmarshal(pdu, offset, "dI", &fid, &v9iattr);
1.1 root 1319:
1.1.1.2 ! root 1320: fidp = get_fid(pdu, fid);
! 1321: if (fidp == NULL) {
! 1322: err = -EINVAL;
! 1323: goto out_nofid;
1.1 root 1324: }
1.1.1.2 ! root 1325: if (v9iattr.valid & ATTR_MODE) {
! 1326: err = v9fs_co_chmod(pdu, &fidp->path, v9iattr.mode);
! 1327: if (err < 0) {
! 1328: goto out;
1.1 root 1329: }
1330: }
1.1.1.2 ! root 1331: if (v9iattr.valid & (ATTR_ATIME | ATTR_MTIME)) {
1.1 root 1332: struct timespec times[2];
1.1.1.2 ! root 1333: if (v9iattr.valid & ATTR_ATIME) {
! 1334: if (v9iattr.valid & ATTR_ATIME_SET) {
! 1335: times[0].tv_sec = v9iattr.atime_sec;
! 1336: times[0].tv_nsec = v9iattr.atime_nsec;
1.1 root 1337: } else {
1338: times[0].tv_nsec = UTIME_NOW;
1339: }
1340: } else {
1341: times[0].tv_nsec = UTIME_OMIT;
1342: }
1.1.1.2 ! root 1343: if (v9iattr.valid & ATTR_MTIME) {
! 1344: if (v9iattr.valid & ATTR_MTIME_SET) {
! 1345: times[1].tv_sec = v9iattr.mtime_sec;
! 1346: times[1].tv_nsec = v9iattr.mtime_nsec;
1.1 root 1347: } else {
1348: times[1].tv_nsec = UTIME_NOW;
1349: }
1350: } else {
1351: times[1].tv_nsec = UTIME_OMIT;
1352: }
1.1.1.2 ! root 1353: err = v9fs_co_utimensat(pdu, &fidp->path, times);
! 1354: if (err < 0) {
! 1355: goto out;
! 1356: }
1.1 root 1357: }
1.1.1.2 ! root 1358: /*
! 1359: * If the only valid entry in iattr is ctime we can call
! 1360: * chown(-1,-1) to update the ctime of the file
! 1361: */
! 1362: if ((v9iattr.valid & (ATTR_UID | ATTR_GID)) ||
! 1363: ((v9iattr.valid & ATTR_CTIME)
! 1364: && !((v9iattr.valid & ATTR_MASK) & ~ATTR_CTIME))) {
! 1365: if (!(v9iattr.valid & ATTR_UID)) {
! 1366: v9iattr.uid = -1;
! 1367: }
! 1368: if (!(v9iattr.valid & ATTR_GID)) {
! 1369: v9iattr.gid = -1;
! 1370: }
! 1371: err = v9fs_co_chown(pdu, &fidp->path, v9iattr.uid,
! 1372: v9iattr.gid);
! 1373: if (err < 0) {
! 1374: goto out;
! 1375: }
! 1376: }
! 1377: if (v9iattr.valid & (ATTR_SIZE)) {
! 1378: err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size);
! 1379: if (err < 0) {
! 1380: goto out;
! 1381: }
! 1382: }
! 1383: err = offset;
1.1 root 1384: out:
1.1.1.2 ! root 1385: put_fid(pdu, fidp);
! 1386: out_nofid:
! 1387: complete_pdu(s, pdu, err);
1.1 root 1388: }
1389:
1.1.1.2 ! root 1390: static int v9fs_walk_marshal(V9fsPDU *pdu, uint16_t nwnames, V9fsQID *qids)
1.1 root 1391: {
1.1.1.2 ! root 1392: int i;
! 1393: size_t offset = 7;
! 1394: offset += pdu_marshal(pdu, offset, "w", nwnames);
! 1395: for (i = 0; i < nwnames; i++) {
! 1396: offset += pdu_marshal(pdu, offset, "Q", &qids[i]);
! 1397: }
! 1398: return offset;
! 1399: }
1.1 root 1400:
1.1.1.2 ! root 1401: static void v9fs_walk(void *opaque)
! 1402: {
! 1403: int name_idx;
! 1404: V9fsQID *qids = NULL;
! 1405: int i, err = 0;
! 1406: V9fsPath dpath, path;
! 1407: uint16_t nwnames;
! 1408: struct stat stbuf;
! 1409: size_t offset = 7;
! 1410: int32_t fid, newfid;
! 1411: V9fsString *wnames = NULL;
! 1412: V9fsFidState *fidp;
! 1413: V9fsFidState *newfidp = NULL;;
! 1414: V9fsPDU *pdu = opaque;
! 1415: V9fsState *s = pdu->s;
! 1416:
! 1417: offset += pdu_unmarshal(pdu, offset, "ddw", &fid,
! 1418: &newfid, &nwnames);
! 1419:
! 1420: trace_v9fs_walk(pdu->tag, pdu->id, fid, newfid, nwnames);
! 1421:
! 1422: if (nwnames && nwnames <= P9_MAXWELEM) {
! 1423: wnames = g_malloc0(sizeof(wnames[0]) * nwnames);
! 1424: qids = g_malloc0(sizeof(qids[0]) * nwnames);
! 1425: for (i = 0; i < nwnames; i++) {
! 1426: offset += pdu_unmarshal(pdu, offset, "s", &wnames[i]);
! 1427: }
! 1428: } else if (nwnames > P9_MAXWELEM) {
1.1 root 1429: err = -EINVAL;
1.1.1.2 ! root 1430: goto out_nofid;
1.1 root 1431: }
1.1.1.2 ! root 1432: fidp = get_fid(pdu, fid);
! 1433: if (fidp == NULL) {
! 1434: err = -ENOENT;
! 1435: goto out_nofid;
1.1 root 1436: }
1.1.1.2 ! root 1437: v9fs_path_init(&dpath);
! 1438: v9fs_path_init(&path);
! 1439: /*
! 1440: * Both dpath and path initially poin to fidp.
! 1441: * Needed to handle request with nwnames == 0
! 1442: */
! 1443: v9fs_path_copy(&dpath, &fidp->path);
! 1444: v9fs_path_copy(&path, &fidp->path);
! 1445: for (name_idx = 0; name_idx < nwnames; name_idx++) {
! 1446: err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data, &path);
! 1447: if (err < 0) {
! 1448: goto out;
! 1449: }
! 1450: err = v9fs_co_lstat(pdu, &path, &stbuf);
! 1451: if (err < 0) {
! 1452: goto out;
! 1453: }
! 1454: stat_to_qid(&stbuf, &qids[name_idx]);
! 1455: v9fs_path_copy(&dpath, &path);
! 1456: }
! 1457: if (fid == newfid) {
! 1458: BUG_ON(fidp->fid_type != P9_FID_NONE);
! 1459: v9fs_path_copy(&fidp->path, &path);
! 1460: } else {
! 1461: newfidp = alloc_fid(s, newfid);
! 1462: if (newfidp == NULL) {
! 1463: err = -EINVAL;
! 1464: goto out;
! 1465: }
! 1466: newfidp->uid = fidp->uid;
! 1467: v9fs_path_copy(&newfidp->path, &path);
! 1468: }
! 1469: err = v9fs_walk_marshal(pdu, nwnames, qids);
! 1470: trace_v9fs_walk_return(pdu->tag, pdu->id, nwnames, qids);
1.1 root 1471: out:
1.1.1.2 ! root 1472: put_fid(pdu, fidp);
! 1473: if (newfidp) {
! 1474: put_fid(pdu, newfidp);
! 1475: }
! 1476: v9fs_path_free(&dpath);
! 1477: v9fs_path_free(&path);
! 1478: out_nofid:
! 1479: complete_pdu(s, pdu, err);
! 1480: if (nwnames && nwnames <= P9_MAXWELEM) {
! 1481: for (name_idx = 0; name_idx < nwnames; name_idx++) {
! 1482: v9fs_string_free(&wnames[name_idx]);
! 1483: }
! 1484: g_free(wnames);
! 1485: g_free(qids);
! 1486: }
! 1487: return;
1.1 root 1488: }
1489:
1.1.1.2 ! root 1490: static int32_t get_iounit(V9fsPDU *pdu, V9fsPath *path)
1.1 root 1491: {
1.1.1.2 ! root 1492: struct statfs stbuf;
! 1493: int32_t iounit = 0;
! 1494: V9fsState *s = pdu->s;
1.1 root 1495:
1.1.1.2 ! root 1496: /*
! 1497: * iounit should be multiples of f_bsize (host filesystem block size
! 1498: * and as well as less than (client msize - P9_IOHDRSZ))
! 1499: */
! 1500: if (!v9fs_co_statfs(pdu, path, &stbuf)) {
! 1501: iounit = stbuf.f_bsize;
! 1502: iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize;
! 1503: }
! 1504: if (!iounit) {
! 1505: iounit = s->msize - P9_IOHDRSZ;
1.1 root 1506: }
1.1.1.2 ! root 1507: return iounit;
1.1 root 1508: }
1509:
1.1.1.2 ! root 1510: static void v9fs_open(void *opaque)
1.1 root 1511: {
1.1.1.2 ! root 1512: int flags;
! 1513: int32_t fid;
! 1514: int32_t mode;
! 1515: V9fsQID qid;
! 1516: int iounit = 0;
! 1517: ssize_t err = 0;
! 1518: size_t offset = 7;
! 1519: struct stat stbuf;
! 1520: V9fsFidState *fidp;
! 1521: V9fsPDU *pdu = opaque;
! 1522: V9fsState *s = pdu->s;
1.1 root 1523:
1.1.1.2 ! root 1524: if (s->proto_version == V9FS_PROTO_2000L) {
! 1525: pdu_unmarshal(pdu, offset, "dd", &fid, &mode);
! 1526: } else {
! 1527: pdu_unmarshal(pdu, offset, "db", &fid, &mode);
1.1 root 1528: }
1.1.1.2 ! root 1529: trace_v9fs_open(pdu->tag, pdu->id, fid, mode);
1.1 root 1530:
1.1.1.2 ! root 1531: fidp = get_fid(pdu, fid);
! 1532: if (fidp == NULL) {
1.1 root 1533: err = -ENOENT;
1.1.1.2 ! root 1534: goto out_nofid;
1.1 root 1535: }
1.1.1.2 ! root 1536: BUG_ON(fidp->fid_type != P9_FID_NONE);
1.1 root 1537:
1.1.1.2 ! root 1538: err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
! 1539: if (err < 0) {
1.1 root 1540: goto out;
1541: }
1.1.1.2 ! root 1542: stat_to_qid(&stbuf, &qid);
! 1543: if (S_ISDIR(stbuf.st_mode)) {
! 1544: err = v9fs_co_opendir(pdu, fidp);
! 1545: if (err < 0) {
1.1 root 1546: goto out;
1547: }
1.1.1.2 ! root 1548: fidp->fid_type = P9_FID_DIR;
! 1549: offset += pdu_marshal(pdu, offset, "Qd", &qid, 0);
! 1550: err = offset;
1.1 root 1551: } else {
1552: if (s->proto_version == V9FS_PROTO_2000L) {
1.1.1.2 ! root 1553: flags = get_dotl_openflags(s, mode);
1.1 root 1554: } else {
1.1.1.2 ! root 1555: flags = omode_to_uflags(mode);
1.1 root 1556: }
1.1.1.2 ! root 1557: if (is_ro_export(&s->ctx)) {
! 1558: if (mode & O_WRONLY || mode & O_RDWR ||
! 1559: mode & O_APPEND || mode & O_TRUNC) {
! 1560: err = -EROFS;
! 1561: goto out;
! 1562: }
! 1563: flags |= O_NOATIME;
1.1 root 1564: }
1.1.1.2 ! root 1565: err = v9fs_co_open(pdu, fidp, flags);
! 1566: if (err < 0) {
! 1567: goto out;
! 1568: }
! 1569: fidp->fid_type = P9_FID_FILE;
! 1570: fidp->open_flags = flags;
! 1571: if (flags & O_EXCL) {
! 1572: /*
! 1573: * We let the host file system do O_EXCL check
! 1574: * We should not reclaim such fd
! 1575: */
! 1576: fidp->flags |= FID_NON_RECLAIMABLE;
! 1577: }
! 1578: iounit = get_iounit(pdu, &fidp->path);
! 1579: offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit);
! 1580: err = offset;
1.1 root 1581: }
1.1.1.2 ! root 1582: trace_v9fs_open_return(pdu->tag, pdu->id,
! 1583: qid.type, qid.version, qid.path, iounit);
1.1 root 1584: out:
1.1.1.2 ! root 1585: put_fid(pdu, fidp);
! 1586: out_nofid:
! 1587: complete_pdu(s, pdu, err);
1.1 root 1588: }
1589:
1.1.1.2 ! root 1590: static void v9fs_lcreate(void *opaque)
1.1 root 1591: {
1592: int32_t dfid, flags, mode;
1593: gid_t gid;
1594: ssize_t err = 0;
1.1.1.2 ! root 1595: ssize_t offset = 7;
! 1596: V9fsString name;
! 1597: V9fsFidState *fidp;
! 1598: struct stat stbuf;
! 1599: V9fsQID qid;
! 1600: int32_t iounit;
! 1601: V9fsPDU *pdu = opaque;
1.1 root 1602:
1.1.1.2 ! root 1603: pdu_unmarshal(pdu, offset, "dsddd", &dfid, &name, &flags,
! 1604: &mode, &gid);
! 1605: trace_v9fs_lcreate(pdu->tag, pdu->id, dfid, flags, mode, gid);
1.1 root 1606:
1.1.1.2 ! root 1607: fidp = get_fid(pdu, dfid);
! 1608: if (fidp == NULL) {
1.1 root 1609: err = -ENOENT;
1.1.1.2 ! root 1610: goto out_nofid;
1.1 root 1611: }
1612:
1.1.1.2 ! root 1613: flags = get_dotl_openflags(pdu->s, flags);
! 1614: err = v9fs_co_open2(pdu, fidp, &name, gid,
! 1615: flags | O_CREAT, mode, &stbuf);
! 1616: if (err < 0) {
! 1617: goto out;
1.1 root 1618: }
1.1.1.2 ! root 1619: fidp->fid_type = P9_FID_FILE;
! 1620: fidp->open_flags = flags;
! 1621: if (flags & O_EXCL) {
! 1622: /*
! 1623: * We let the host file system do O_EXCL check
! 1624: * We should not reclaim such fd
! 1625: */
! 1626: fidp->flags |= FID_NON_RECLAIMABLE;
! 1627: }
! 1628: iounit = get_iounit(pdu, &fidp->path);
! 1629: stat_to_qid(&stbuf, &qid);
! 1630: offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit);
! 1631: err = offset;
! 1632: trace_v9fs_lcreate_return(pdu->tag, pdu->id,
! 1633: qid.type, qid.version, qid.path, iounit);
! 1634: out:
! 1635: put_fid(pdu, fidp);
! 1636: out_nofid:
! 1637: complete_pdu(pdu->s, pdu, err);
! 1638: v9fs_string_free(&name);
1.1 root 1639: }
1640:
1.1.1.2 ! root 1641: static void v9fs_fsync(void *opaque)
1.1 root 1642: {
1.1.1.2 ! root 1643: int err;
1.1 root 1644: int32_t fid;
1.1.1.2 ! root 1645: int datasync;
1.1 root 1646: size_t offset = 7;
1647: V9fsFidState *fidp;
1.1.1.2 ! root 1648: V9fsPDU *pdu = opaque;
! 1649: V9fsState *s = pdu->s;
1.1 root 1650:
1651: pdu_unmarshal(pdu, offset, "dd", &fid, &datasync);
1.1.1.2 ! root 1652: trace_v9fs_fsync(pdu->tag, pdu->id, fid, datasync);
! 1653:
! 1654: fidp = get_fid(pdu, fid);
1.1 root 1655: if (fidp == NULL) {
1656: err = -ENOENT;
1.1.1.2 ! root 1657: goto out_nofid;
! 1658: }
! 1659: err = v9fs_co_fsync(pdu, fidp, datasync);
! 1660: if (!err) {
! 1661: err = offset;
1.1 root 1662: }
1.1.1.2 ! root 1663: put_fid(pdu, fidp);
! 1664: out_nofid:
! 1665: complete_pdu(s, pdu, err);
1.1 root 1666: }
1667:
1.1.1.2 ! root 1668: static void v9fs_clunk(void *opaque)
1.1 root 1669: {
1.1.1.2 ! root 1670: int err;
1.1 root 1671: int32_t fid;
1672: size_t offset = 7;
1.1.1.2 ! root 1673: V9fsFidState *fidp;
! 1674: V9fsPDU *pdu = opaque;
! 1675: V9fsState *s = pdu->s;
1.1 root 1676:
1677: pdu_unmarshal(pdu, offset, "d", &fid);
1.1.1.2 ! root 1678: trace_v9fs_clunk(pdu->tag, pdu->id, fid);
1.1 root 1679:
1.1.1.2 ! root 1680: fidp = clunk_fid(s, fid);
! 1681: if (fidp == NULL) {
! 1682: err = -ENOENT;
! 1683: goto out_nofid;
1.1 root 1684: }
1.1.1.2 ! root 1685: /*
! 1686: * Bump the ref so that put_fid will
! 1687: * free the fid.
! 1688: */
! 1689: fidp->ref++;
1.1 root 1690: err = offset;
1.1.1.2 ! root 1691:
! 1692: put_fid(pdu, fidp);
! 1693: out_nofid:
1.1 root 1694: complete_pdu(s, pdu, err);
1695: }
1696:
1.1.1.2 ! root 1697: static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp,
! 1698: uint64_t off, uint32_t max_count)
1.1 root 1699: {
1.1.1.2 ! root 1700: size_t offset = 7;
! 1701: int read_count;
! 1702: int64_t xattr_len;
1.1 root 1703:
1.1.1.2 ! root 1704: xattr_len = fidp->fs.xattr.len;
! 1705: read_count = xattr_len - off;
! 1706: if (read_count > max_count) {
! 1707: read_count = max_count;
! 1708: } else if (read_count < 0) {
! 1709: /*
! 1710: * read beyond XATTR value
! 1711: */
! 1712: read_count = 0;
1.1 root 1713: }
1.1.1.2 ! root 1714: offset += pdu_marshal(pdu, offset, "d", read_count);
! 1715: offset += pdu_pack(pdu, offset,
! 1716: ((char *)fidp->fs.xattr.value) + off,
! 1717: read_count);
! 1718: return offset;
1.1 root 1719: }
1720:
1.1.1.2 ! root 1721: static int v9fs_do_readdir_with_stat(V9fsPDU *pdu,
! 1722: V9fsFidState *fidp, uint32_t max_count)
! 1723: {
! 1724: V9fsPath path;
! 1725: V9fsStat v9stat;
! 1726: int len, err = 0;
! 1727: int32_t count = 0;
! 1728: struct stat stbuf;
! 1729: off_t saved_dir_pos;
! 1730: struct dirent *dent, *result;
1.1 root 1731:
1.1.1.2 ! root 1732: /* save the directory position */
! 1733: saved_dir_pos = v9fs_co_telldir(pdu, fidp);
! 1734: if (saved_dir_pos < 0) {
! 1735: return saved_dir_pos;
! 1736: }
1.1 root 1737:
1.1.1.2 ! root 1738: dent = g_malloc(sizeof(struct dirent));
1.1 root 1739:
1.1.1.2 ! root 1740: while (1) {
! 1741: v9fs_path_init(&path);
! 1742: err = v9fs_co_readdir_r(pdu, fidp, dent, &result);
! 1743: if (err || !result) {
! 1744: break;
! 1745: }
! 1746: err = v9fs_co_name_to_path(pdu, &fidp->path, dent->d_name, &path);
! 1747: if (err < 0) {
! 1748: goto out;
! 1749: }
! 1750: err = v9fs_co_lstat(pdu, &path, &stbuf);
! 1751: if (err < 0) {
! 1752: goto out;
! 1753: }
! 1754: err = stat_to_v9stat(pdu, &path, &stbuf, &v9stat);
! 1755: if (err < 0) {
! 1756: goto out;
1.1 root 1757: }
1.1.1.2 ! root 1758: /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
! 1759: len = pdu_marshal(pdu, 11 + count, "S", &v9stat);
! 1760: if ((len != (v9stat.size + 2)) || ((count + len) > max_count)) {
! 1761: /* Ran out of buffer. Set dir back to old position and return */
! 1762: v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
! 1763: v9fs_stat_free(&v9stat);
! 1764: v9fs_path_free(&path);
! 1765: g_free(dent);
! 1766: return count;
! 1767: }
! 1768: count += len;
! 1769: v9fs_stat_free(&v9stat);
! 1770: v9fs_path_free(&path);
! 1771: saved_dir_pos = dent->d_off;
1.1 root 1772: }
1773: out:
1.1.1.2 ! root 1774: g_free(dent);
! 1775: v9fs_path_free(&path);
! 1776: if (err < 0) {
! 1777: return err;
! 1778: }
! 1779: return count;
1.1 root 1780: }
1781:
1.1.1.2 ! root 1782: /*
! 1783: * Create a QEMUIOVector for a sub-region of PDU iovecs
! 1784: *
! 1785: * @qiov: uninitialized QEMUIOVector
! 1786: * @skip: number of bytes to skip from beginning of PDU
! 1787: * @size: number of bytes to include
! 1788: * @is_write: true - write, false - read
! 1789: *
! 1790: * The resulting QEMUIOVector has heap-allocated iovecs and must be cleaned up
! 1791: * with qemu_iovec_destroy().
! 1792: */
! 1793: static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu,
! 1794: uint64_t skip, size_t size,
! 1795: bool is_write)
! 1796: {
! 1797: QEMUIOVector elem;
! 1798: struct iovec *iov;
! 1799: unsigned int niov;
! 1800:
! 1801: if (is_write) {
! 1802: iov = pdu->elem.out_sg;
! 1803: niov = pdu->elem.out_num;
! 1804: } else {
! 1805: iov = pdu->elem.in_sg;
! 1806: niov = pdu->elem.in_num;
1.1 root 1807: }
1.1.1.2 ! root 1808:
! 1809: qemu_iovec_init_external(&elem, iov, niov);
! 1810: qemu_iovec_init(qiov, niov);
! 1811: qemu_iovec_copy(qiov, &elem, skip, size);
1.1 root 1812: }
1813:
1.1.1.2 ! root 1814: static void v9fs_read(void *opaque)
1.1 root 1815: {
1816: int32_t fid;
1.1.1.2 ! root 1817: uint64_t off;
1.1 root 1818: ssize_t err = 0;
1.1.1.2 ! root 1819: int32_t count = 0;
! 1820: size_t offset = 7;
! 1821: uint32_t max_count;
! 1822: V9fsFidState *fidp;
! 1823: V9fsPDU *pdu = opaque;
! 1824: V9fsState *s = pdu->s;
1.1 root 1825:
1.1.1.2 ! root 1826: pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &max_count);
! 1827: trace_v9fs_read(pdu->tag, pdu->id, fid, off, max_count);
1.1 root 1828:
1.1.1.2 ! root 1829: fidp = get_fid(pdu, fid);
! 1830: if (fidp == NULL) {
1.1 root 1831: err = -EINVAL;
1.1.1.2 ! root 1832: goto out_nofid;
1.1 root 1833: }
1.1.1.2 ! root 1834: if (fidp->fid_type == P9_FID_DIR) {
1.1 root 1835:
1.1.1.2 ! root 1836: if (off == 0) {
! 1837: v9fs_co_rewinddir(pdu, fidp);
1.1 root 1838: }
1.1.1.2 ! root 1839: count = v9fs_do_readdir_with_stat(pdu, fidp, max_count);
! 1840: if (count < 0) {
! 1841: err = count;
! 1842: goto out;
! 1843: }
! 1844: err = offset;
! 1845: err += pdu_marshal(pdu, offset, "d", count);
! 1846: err += count;
! 1847: } else if (fidp->fid_type == P9_FID_FILE) {
! 1848: QEMUIOVector qiov_full;
! 1849: QEMUIOVector qiov;
! 1850: int32_t len;
! 1851:
! 1852: v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset + 4, max_count, false);
! 1853: qemu_iovec_init(&qiov, qiov_full.niov);
! 1854: do {
! 1855: qemu_iovec_reset(&qiov);
! 1856: qemu_iovec_copy(&qiov, &qiov_full, count, qiov_full.size - count);
! 1857: if (0) {
! 1858: print_sg(qiov.iov, qiov.niov);
! 1859: }
! 1860: /* Loop in case of EINTR */
! 1861: do {
! 1862: len = v9fs_co_preadv(pdu, fidp, qiov.iov, qiov.niov, off);
! 1863: if (len >= 0) {
! 1864: off += len;
! 1865: count += len;
! 1866: }
! 1867: } while (len == -EINTR && !pdu->cancelled);
! 1868: if (len < 0) {
! 1869: /* IO error return the error */
! 1870: err = len;
! 1871: goto out;
! 1872: }
! 1873: } while (count < max_count && len > 0);
! 1874: err = offset;
! 1875: err += pdu_marshal(pdu, offset, "d", count);
! 1876: err += count;
! 1877: qemu_iovec_destroy(&qiov);
! 1878: qemu_iovec_destroy(&qiov_full);
! 1879: } else if (fidp->fid_type == P9_FID_XATTR) {
! 1880: err = v9fs_xattr_read(s, pdu, fidp, off, max_count);
1.1 root 1881: } else {
1882: err = -EINVAL;
1883: }
1.1.1.2 ! root 1884: trace_v9fs_read_return(pdu->tag, pdu->id, count, err);
1.1 root 1885: out:
1.1.1.2 ! root 1886: put_fid(pdu, fidp);
! 1887: out_nofid:
1.1 root 1888: complete_pdu(s, pdu, err);
1889: }
1890:
1.1.1.2 ! root 1891: static size_t v9fs_readdir_data_size(V9fsString *name)
1.1 root 1892: {
1.1.1.2 ! root 1893: /*
! 1894: * Size of each dirent on the wire: size of qid (13) + size of offset (8)
! 1895: * size of type (1) + size of name.size (2) + strlen(name.data)
! 1896: */
! 1897: return 24 + v9fs_string_size(name);
1.1 root 1898: }
1899:
1.1.1.2 ! root 1900: static int v9fs_do_readdir(V9fsPDU *pdu,
! 1901: V9fsFidState *fidp, int32_t max_count)
1.1 root 1902: {
1903: size_t size;
1.1.1.2 ! root 1904: V9fsQID qid;
! 1905: V9fsString name;
! 1906: int len, err = 0;
! 1907: int32_t count = 0;
! 1908: off_t saved_dir_pos;
! 1909: struct dirent *dent, *result;
1.1 root 1910:
1.1.1.2 ! root 1911: /* save the directory position */
! 1912: saved_dir_pos = v9fs_co_telldir(pdu, fidp);
! 1913: if (saved_dir_pos < 0) {
! 1914: return saved_dir_pos;
! 1915: }
! 1916:
! 1917: dent = g_malloc(sizeof(struct dirent));
1.1 root 1918:
1.1.1.2 ! root 1919: while (1) {
! 1920: err = v9fs_co_readdir_r(pdu, fidp, dent, &result);
! 1921: if (err || !result) {
! 1922: break;
! 1923: }
! 1924: v9fs_string_init(&name);
! 1925: v9fs_string_sprintf(&name, "%s", dent->d_name);
! 1926: if ((count + v9fs_readdir_data_size(&name)) > max_count) {
1.1 root 1927: /* Ran out of buffer. Set dir back to old position and return */
1.1.1.2 ! root 1928: v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
! 1929: v9fs_string_free(&name);
! 1930: g_free(dent);
! 1931: return count;
1.1 root 1932: }
1.1.1.2 ! root 1933: /*
! 1934: * Fill up just the path field of qid because the client uses
1.1 root 1935: * only that. To fill the entire qid structure we will have
1936: * to stat each dirent found, which is expensive
1937: */
1.1.1.2 ! root 1938: size = MIN(sizeof(dent->d_ino), sizeof(qid.path));
! 1939: memcpy(&qid.path, &dent->d_ino, size);
1.1 root 1940: /* Fill the other fields with dummy values */
1.1.1.2 ! root 1941: qid.type = 0;
! 1942: qid.version = 0;
1.1 root 1943:
1.1.1.2 ! root 1944: /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
! 1945: len = pdu_marshal(pdu, 11 + count, "Qqbs",
! 1946: &qid, dent->d_off,
! 1947: dent->d_type, &name);
! 1948: count += len;
! 1949: v9fs_string_free(&name);
! 1950: saved_dir_pos = dent->d_off;
! 1951: }
! 1952: g_free(dent);
! 1953: if (err < 0) {
! 1954: return err;
! 1955: }
! 1956: return count;
1.1 root 1957: }
1958:
1.1.1.2 ! root 1959: static void v9fs_readdir(void *opaque)
1.1 root 1960: {
1961: int32_t fid;
1.1.1.2 ! root 1962: V9fsFidState *fidp;
! 1963: ssize_t retval = 0;
1.1 root 1964: size_t offset = 7;
1.1.1.2 ! root 1965: uint64_t initial_offset;
! 1966: int32_t count;
! 1967: uint32_t max_count;
! 1968: V9fsPDU *pdu = opaque;
! 1969: V9fsState *s = pdu->s;
1.1 root 1970:
1.1.1.2 ! root 1971: pdu_unmarshal(pdu, offset, "dqd", &fid, &initial_offset, &max_count);
1.1 root 1972:
1.1.1.2 ! root 1973: trace_v9fs_readdir(pdu->tag, pdu->id, fid, initial_offset, max_count);
1.1 root 1974:
1.1.1.2 ! root 1975: fidp = get_fid(pdu, fid);
! 1976: if (fidp == NULL) {
! 1977: retval = -EINVAL;
! 1978: goto out_nofid;
! 1979: }
! 1980: if (!fidp->fs.dir) {
! 1981: retval = -EINVAL;
1.1 root 1982: goto out;
1983: }
1.1.1.2 ! root 1984: if (initial_offset == 0) {
! 1985: v9fs_co_rewinddir(pdu, fidp);
1.1 root 1986: } else {
1.1.1.2 ! root 1987: v9fs_co_seekdir(pdu, fidp, initial_offset);
1.1 root 1988: }
1.1.1.2 ! root 1989: count = v9fs_do_readdir(pdu, fidp, max_count);
! 1990: if (count < 0) {
! 1991: retval = count;
1.1 root 1992: goto out;
1993: }
1.1.1.2 ! root 1994: retval = offset;
! 1995: retval += pdu_marshal(pdu, offset, "d", count);
! 1996: retval += count;
! 1997: trace_v9fs_readdir_return(pdu->tag, pdu->id, count, retval);
1.1 root 1998: out:
1.1.1.2 ! root 1999: put_fid(pdu, fidp);
! 2000: out_nofid:
! 2001: complete_pdu(s, pdu, retval);
1.1 root 2002: }
2003:
1.1.1.2 ! root 2004: static int v9fs_xattr_write(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp,
! 2005: uint64_t off, uint32_t count,
! 2006: struct iovec *sg, int cnt)
1.1 root 2007: {
2008: int i, to_copy;
2009: ssize_t err = 0;
2010: int write_count;
2011: int64_t xattr_len;
1.1.1.2 ! root 2012: size_t offset = 7;
! 2013:
1.1 root 2014:
1.1.1.2 ! root 2015: xattr_len = fidp->fs.xattr.len;
! 2016: write_count = xattr_len - off;
! 2017: if (write_count > count) {
! 2018: write_count = count;
1.1 root 2019: } else if (write_count < 0) {
2020: /*
2021: * write beyond XATTR value len specified in
2022: * xattrcreate
2023: */
2024: err = -ENOSPC;
2025: goto out;
2026: }
1.1.1.2 ! root 2027: offset += pdu_marshal(pdu, offset, "d", write_count);
! 2028: err = offset;
! 2029: fidp->fs.xattr.copied_len += write_count;
1.1 root 2030: /*
2031: * Now copy the content from sg list
2032: */
1.1.1.2 ! root 2033: for (i = 0; i < cnt; i++) {
! 2034: if (write_count > sg[i].iov_len) {
! 2035: to_copy = sg[i].iov_len;
1.1 root 2036: } else {
2037: to_copy = write_count;
2038: }
1.1.1.2 ! root 2039: memcpy((char *)fidp->fs.xattr.value + off, sg[i].iov_base, to_copy);
1.1 root 2040: /* updating vs->off since we are not using below */
1.1.1.2 ! root 2041: off += to_copy;
1.1 root 2042: write_count -= to_copy;
2043: }
2044: out:
1.1.1.2 ! root 2045: return err;
1.1 root 2046: }
2047:
1.1.1.2 ! root 2048: static void v9fs_write(void *opaque)
1.1 root 2049: {
2050: ssize_t err;
1.1.1.2 ! root 2051: int32_t fid;
! 2052: uint64_t off;
! 2053: uint32_t count;
! 2054: int32_t len = 0;
! 2055: int32_t total = 0;
! 2056: size_t offset = 7;
! 2057: V9fsFidState *fidp;
! 2058: V9fsPDU *pdu = opaque;
! 2059: V9fsState *s = pdu->s;
! 2060: QEMUIOVector qiov_full;
! 2061: QEMUIOVector qiov;
! 2062:
! 2063: offset += pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &count);
! 2064: v9fs_init_qiov_from_pdu(&qiov_full, pdu, offset, count, true);
! 2065: trace_v9fs_write(pdu->tag, pdu->id, fid, off, count, qiov_full.niov);
1.1 root 2066:
1.1.1.2 ! root 2067: fidp = get_fid(pdu, fid);
! 2068: if (fidp == NULL) {
1.1 root 2069: err = -EINVAL;
1.1.1.2 ! root 2070: goto out_nofid;
1.1 root 2071: }
1.1.1.2 ! root 2072: if (fidp->fid_type == P9_FID_FILE) {
! 2073: if (fidp->fs.fd == -1) {
1.1 root 2074: err = -EINVAL;
2075: goto out;
2076: }
1.1.1.2 ! root 2077: } else if (fidp->fid_type == P9_FID_XATTR) {
1.1 root 2078: /*
2079: * setxattr operation
2080: */
1.1.1.2 ! root 2081: err = v9fs_xattr_write(s, pdu, fidp, off, count,
! 2082: qiov_full.iov, qiov_full.niov);
! 2083: goto out;
1.1 root 2084: } else {
2085: err = -EINVAL;
2086: goto out;
2087: }
1.1.1.2 ! root 2088: qemu_iovec_init(&qiov, qiov_full.niov);
! 2089: do {
! 2090: qemu_iovec_reset(&qiov);
! 2091: qemu_iovec_copy(&qiov, &qiov_full, total, qiov_full.size - total);
! 2092: if (0) {
! 2093: print_sg(qiov.iov, qiov.niov);
1.1 root 2094: }
1.1.1.2 ! root 2095: /* Loop in case of EINTR */
! 2096: do {
! 2097: len = v9fs_co_pwritev(pdu, fidp, qiov.iov, qiov.niov, off);
! 2098: if (len >= 0) {
! 2099: off += len;
! 2100: total += len;
! 2101: }
! 2102: } while (len == -EINTR && !pdu->cancelled);
! 2103: if (len < 0) {
! 2104: /* IO error return the error */
! 2105: err = len;
! 2106: goto out_qiov;
! 2107: }
! 2108: } while (total < count && len > 0);
1.1 root 2109:
1.1.1.2 ! root 2110: offset = 7;
! 2111: offset += pdu_marshal(pdu, offset, "d", total);
! 2112: err = offset;
! 2113: trace_v9fs_write_return(pdu->tag, pdu->id, total, err);
! 2114: out_qiov:
! 2115: qemu_iovec_destroy(&qiov);
! 2116: out:
! 2117: put_fid(pdu, fidp);
! 2118: out_nofid:
! 2119: qemu_iovec_destroy(&qiov_full);
! 2120: complete_pdu(s, pdu, err);
1.1 root 2121: }
2122:
1.1.1.2 ! root 2123: static void v9fs_create(void *opaque)
1.1 root 2124: {
1.1.1.2 ! root 2125: int32_t fid;
! 2126: int err = 0;
! 2127: size_t offset = 7;
! 2128: V9fsFidState *fidp;
! 2129: V9fsQID qid;
! 2130: int32_t perm;
! 2131: int8_t mode;
! 2132: V9fsPath path;
! 2133: struct stat stbuf;
! 2134: V9fsString name;
! 2135: V9fsString extension;
! 2136: int iounit;
! 2137: V9fsPDU *pdu = opaque;
1.1 root 2138:
1.1.1.2 ! root 2139: v9fs_path_init(&path);
1.1 root 2140:
1.1.1.2 ! root 2141: pdu_unmarshal(pdu, offset, "dsdbs", &fid, &name,
! 2142: &perm, &mode, &extension);
1.1 root 2143:
1.1.1.2 ! root 2144: trace_v9fs_create(pdu->tag, pdu->id, fid, name.data, perm, mode);
1.1 root 2145:
1.1.1.2 ! root 2146: fidp = get_fid(pdu, fid);
! 2147: if (fidp == NULL) {
! 2148: err = -EINVAL;
! 2149: goto out_nofid;
1.1 root 2150: }
1.1.1.2 ! root 2151: if (perm & P9_STAT_MODE_DIR) {
! 2152: err = v9fs_co_mkdir(pdu, fidp, &name, perm & 0777,
! 2153: fidp->uid, -1, &stbuf);
! 2154: if (err < 0) {
! 2155: goto out;
! 2156: }
! 2157: err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
! 2158: if (err < 0) {
! 2159: goto out;
! 2160: }
! 2161: v9fs_path_copy(&fidp->path, &path);
! 2162: err = v9fs_co_opendir(pdu, fidp);
! 2163: if (err < 0) {
! 2164: goto out;
! 2165: }
! 2166: fidp->fid_type = P9_FID_DIR;
! 2167: } else if (perm & P9_STAT_MODE_SYMLINK) {
! 2168: err = v9fs_co_symlink(pdu, fidp, &name,
! 2169: extension.data, -1 , &stbuf);
! 2170: if (err < 0) {
! 2171: goto out;
! 2172: }
! 2173: err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
! 2174: if (err < 0) {
! 2175: goto out;
! 2176: }
! 2177: v9fs_path_copy(&fidp->path, &path);
! 2178: } else if (perm & P9_STAT_MODE_LINK) {
! 2179: int32_t ofid = atoi(extension.data);
! 2180: V9fsFidState *ofidp = get_fid(pdu, ofid);
! 2181: if (ofidp == NULL) {
! 2182: err = -EINVAL;
! 2183: goto out;
! 2184: }
! 2185: err = v9fs_co_link(pdu, ofidp, fidp, &name);
! 2186: put_fid(pdu, ofidp);
! 2187: if (err < 0) {
! 2188: goto out;
! 2189: }
! 2190: err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
! 2191: if (err < 0) {
! 2192: fidp->fid_type = P9_FID_NONE;
! 2193: goto out;
1.1 root 2194: }
1.1.1.2 ! root 2195: v9fs_path_copy(&fidp->path, &path);
! 2196: err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
! 2197: if (err < 0) {
! 2198: fidp->fid_type = P9_FID_NONE;
! 2199: goto out;
! 2200: }
! 2201: } else if (perm & P9_STAT_MODE_DEVICE) {
1.1 root 2202: char ctype;
2203: uint32_t major, minor;
2204: mode_t nmode = 0;
2205:
1.1.1.2 ! root 2206: if (sscanf(extension.data, "%c %u %u", &ctype, &major, &minor) != 3) {
1.1 root 2207: err = -errno;
1.1.1.2 ! root 2208: goto out;
1.1 root 2209: }
2210:
2211: switch (ctype) {
2212: case 'c':
2213: nmode = S_IFCHR;
2214: break;
2215: case 'b':
2216: nmode = S_IFBLK;
2217: break;
2218: default:
2219: err = -EIO;
1.1.1.2 ! root 2220: goto out;
1.1 root 2221: }
2222:
1.1.1.2 ! root 2223: nmode |= perm & 0777;
! 2224: err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1,
! 2225: makedev(major, minor), nmode, &stbuf);
! 2226: if (err < 0) {
! 2227: goto out;
! 2228: }
! 2229: err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
! 2230: if (err < 0) {
! 2231: goto out;
! 2232: }
! 2233: v9fs_path_copy(&fidp->path, &path);
! 2234: } else if (perm & P9_STAT_MODE_NAMED_PIPE) {
! 2235: err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1,
! 2236: 0, S_IFIFO | (perm & 0777), &stbuf);
! 2237: if (err < 0) {
! 2238: goto out;
! 2239: }
! 2240: err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
! 2241: if (err < 0) {
! 2242: goto out;
! 2243: }
! 2244: v9fs_path_copy(&fidp->path, &path);
! 2245: } else if (perm & P9_STAT_MODE_SOCKET) {
! 2246: err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1,
! 2247: 0, S_IFSOCK | (perm & 0777), &stbuf);
! 2248: if (err < 0) {
! 2249: goto out;
! 2250: }
! 2251: err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
! 2252: if (err < 0) {
! 2253: goto out;
! 2254: }
! 2255: v9fs_path_copy(&fidp->path, &path);
1.1 root 2256: } else {
1.1.1.2 ! root 2257: err = v9fs_co_open2(pdu, fidp, &name, -1,
! 2258: omode_to_uflags(mode)|O_CREAT, perm, &stbuf);
! 2259: if (err < 0) {
! 2260: goto out;
! 2261: }
! 2262: fidp->fid_type = P9_FID_FILE;
! 2263: fidp->open_flags = omode_to_uflags(mode);
! 2264: if (fidp->open_flags & O_EXCL) {
! 2265: /*
! 2266: * We let the host file system do O_EXCL check
! 2267: * We should not reclaim such fd
! 2268: */
! 2269: fidp->flags |= FID_NON_RECLAIMABLE;
! 2270: }
1.1 root 2271: }
1.1.1.2 ! root 2272: iounit = get_iounit(pdu, &fidp->path);
! 2273: stat_to_qid(&stbuf, &qid);
! 2274: offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit);
! 2275: err = offset;
! 2276: trace_v9fs_create_return(pdu->tag, pdu->id,
! 2277: qid.type, qid.version, qid.path, iounit);
1.1 root 2278: out:
1.1.1.2 ! root 2279: put_fid(pdu, fidp);
! 2280: out_nofid:
! 2281: complete_pdu(pdu->s, pdu, err);
! 2282: v9fs_string_free(&name);
! 2283: v9fs_string_free(&extension);
! 2284: v9fs_path_free(&path);
1.1 root 2285: }
2286:
1.1.1.2 ! root 2287: static void v9fs_symlink(void *opaque)
1.1 root 2288: {
1.1.1.2 ! root 2289: V9fsPDU *pdu = opaque;
! 2290: V9fsString name;
! 2291: V9fsString symname;
! 2292: V9fsFidState *dfidp;
! 2293: V9fsQID qid;
! 2294: struct stat stbuf;
! 2295: int32_t dfid;
1.1 root 2296: int err = 0;
1.1.1.2 ! root 2297: gid_t gid;
! 2298: size_t offset = 7;
1.1 root 2299:
1.1.1.2 ! root 2300: pdu_unmarshal(pdu, offset, "dssd", &dfid, &name, &symname, &gid);
! 2301: trace_v9fs_symlink(pdu->tag, pdu->id, dfid, name.data, symname.data, gid);
1.1 root 2302:
1.1.1.2 ! root 2303: dfidp = get_fid(pdu, dfid);
! 2304: if (dfidp == NULL) {
1.1 root 2305: err = -EINVAL;
1.1.1.2 ! root 2306: goto out_nofid;
1.1 root 2307: }
1.1.1.2 ! root 2308: err = v9fs_co_symlink(pdu, dfidp, &name, symname.data, gid, &stbuf);
! 2309: if (err < 0) {
1.1 root 2310: goto out;
2311: }
1.1.1.2 ! root 2312: stat_to_qid(&stbuf, &qid);
! 2313: offset += pdu_marshal(pdu, offset, "Q", &qid);
! 2314: err = offset;
! 2315: trace_v9fs_symlink_return(pdu->tag, pdu->id,
! 2316: qid.type, qid.version, qid.path);
1.1 root 2317: out:
1.1.1.2 ! root 2318: put_fid(pdu, dfidp);
! 2319: out_nofid:
! 2320: complete_pdu(pdu->s, pdu, err);
! 2321: v9fs_string_free(&name);
! 2322: v9fs_string_free(&symname);
1.1 root 2323: }
2324:
1.1.1.2 ! root 2325: static void v9fs_flush(void *opaque)
1.1 root 2326: {
1.1.1.2 ! root 2327: int16_t tag;
! 2328: size_t offset = 7;
! 2329: V9fsPDU *cancel_pdu;
! 2330: V9fsPDU *pdu = opaque;
! 2331: V9fsState *s = pdu->s;
1.1 root 2332:
1.1.1.2 ! root 2333: pdu_unmarshal(pdu, offset, "w", &tag);
! 2334: trace_v9fs_flush(pdu->tag, pdu->id, tag);
1.1 root 2335:
1.1.1.2 ! root 2336: QLIST_FOREACH(cancel_pdu, &s->active_list, next) {
! 2337: if (cancel_pdu->tag == tag) {
! 2338: break;
! 2339: }
! 2340: }
! 2341: if (cancel_pdu) {
! 2342: cancel_pdu->cancelled = 1;
! 2343: /*
! 2344: * Wait for pdu to complete.
! 2345: */
! 2346: qemu_co_queue_wait(&cancel_pdu->complete);
! 2347: cancel_pdu->cancelled = 0;
! 2348: free_pdu(pdu->s, cancel_pdu);
1.1 root 2349: }
2350: complete_pdu(s, pdu, 7);
1.1.1.2 ! root 2351: return;
1.1 root 2352: }
2353:
1.1.1.2 ! root 2354: static void v9fs_link(void *opaque)
1.1 root 2355: {
1.1.1.2 ! root 2356: V9fsPDU *pdu = opaque;
! 2357: V9fsState *s = pdu->s;
1.1 root 2358: int32_t dfid, oldfid;
2359: V9fsFidState *dfidp, *oldfidp;
1.1.1.2 ! root 2360: V9fsString name;;
1.1 root 2361: size_t offset = 7;
2362: int err = 0;
2363:
2364: pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name);
1.1.1.2 ! root 2365: trace_v9fs_link(pdu->tag, pdu->id, dfid, oldfid, name.data);
1.1 root 2366:
1.1.1.2 ! root 2367: dfidp = get_fid(pdu, dfid);
1.1 root 2368: if (dfidp == NULL) {
1.1.1.2 ! root 2369: err = -ENOENT;
! 2370: goto out_nofid;
1.1 root 2371: }
2372:
1.1.1.2 ! root 2373: oldfidp = get_fid(pdu, oldfid);
1.1 root 2374: if (oldfidp == NULL) {
1.1.1.2 ! root 2375: err = -ENOENT;
1.1 root 2376: goto out;
2377: }
1.1.1.2 ! root 2378: err = v9fs_co_link(pdu, oldfidp, dfidp, &name);
! 2379: if (!err) {
! 2380: err = offset;
1.1 root 2381: }
2382: out:
1.1.1.2 ! root 2383: put_fid(pdu, dfidp);
! 2384: out_nofid:
1.1 root 2385: v9fs_string_free(&name);
2386: complete_pdu(s, pdu, err);
2387: }
2388:
1.1.1.2 ! root 2389: /* Only works with path name based fid */
! 2390: static void v9fs_remove(void *opaque)
1.1 root 2391: {
1.1.1.2 ! root 2392: int32_t fid;
! 2393: int err = 0;
! 2394: size_t offset = 7;
! 2395: V9fsFidState *fidp;
! 2396: V9fsPDU *pdu = opaque;
! 2397:
! 2398: pdu_unmarshal(pdu, offset, "d", &fid);
! 2399: trace_v9fs_remove(pdu->tag, pdu->id, fid);
! 2400:
! 2401: fidp = get_fid(pdu, fid);
! 2402: if (fidp == NULL) {
! 2403: err = -EINVAL;
! 2404: goto out_nofid;
! 2405: }
! 2406: /* if fs driver is not path based, return EOPNOTSUPP */
! 2407: if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) {
! 2408: err = -EOPNOTSUPP;
! 2409: goto out_err;
! 2410: }
! 2411: /*
! 2412: * IF the file is unlinked, we cannot reopen
! 2413: * the file later. So don't reclaim fd
! 2414: */
! 2415: err = v9fs_mark_fids_unreclaim(pdu, &fidp->path);
1.1 root 2416: if (err < 0) {
1.1.1.2 ! root 2417: goto out_err;
1.1 root 2418: }
1.1.1.2 ! root 2419: err = v9fs_co_remove(pdu, &fidp->path);
! 2420: if (!err) {
! 2421: err = offset;
! 2422: }
! 2423: out_err:
1.1 root 2424: /* For TREMOVE we need to clunk the fid even on failed remove */
1.1.1.2 ! root 2425: clunk_fid(pdu->s, fidp->fid);
! 2426: put_fid(pdu, fidp);
! 2427: out_nofid:
! 2428: complete_pdu(pdu->s, pdu, err);
1.1 root 2429: }
2430:
1.1.1.2 ! root 2431: static void v9fs_unlinkat(void *opaque)
1.1 root 2432: {
2433: int err = 0;
1.1.1.2 ! root 2434: V9fsString name;
! 2435: int32_t dfid, flags;
! 2436: size_t offset = 7;
! 2437: V9fsPath path;
! 2438: V9fsFidState *dfidp;
! 2439: V9fsPDU *pdu = opaque;
1.1 root 2440:
1.1.1.2 ! root 2441: pdu_unmarshal(pdu, offset, "dsd", &dfid, &name, &flags);
1.1 root 2442:
1.1.1.2 ! root 2443: dfidp = get_fid(pdu, dfid);
! 2444: if (dfidp == NULL) {
1.1 root 2445: err = -EINVAL;
1.1.1.2 ! root 2446: goto out_nofid;
1.1 root 2447: }
1.1.1.2 ! root 2448: /*
! 2449: * IF the file is unlinked, we cannot reopen
! 2450: * the file later. So don't reclaim fd
! 2451: */
! 2452: v9fs_path_init(&path);
! 2453: err = v9fs_co_name_to_path(pdu, &dfidp->path, name.data, &path);
1.1 root 2454: if (err < 0) {
1.1.1.2 ! root 2455: goto out_err;
1.1 root 2456: }
1.1.1.2 ! root 2457: err = v9fs_mark_fids_unreclaim(pdu, &path);
1.1 root 2458: if (err < 0) {
1.1.1.2 ! root 2459: goto out_err;
1.1 root 2460: }
1.1.1.2 ! root 2461: err = v9fs_co_unlinkat(pdu, &dfidp->path, &name, flags);
! 2462: if (!err) {
! 2463: err = offset;
! 2464: }
! 2465: out_err:
! 2466: put_fid(pdu, dfidp);
! 2467: v9fs_path_free(&path);
! 2468: out_nofid:
! 2469: complete_pdu(pdu->s, pdu, err);
! 2470: v9fs_string_free(&name);
1.1 root 2471: }
2472:
1.1.1.2 ! root 2473:
! 2474: /* Only works with path name based fid */
! 2475: static int v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp,
! 2476: int32_t newdirfid, V9fsString *name)
1.1 root 2477: {
1.1.1.2 ! root 2478: char *end;
1.1 root 2479: int err = 0;
1.1.1.2 ! root 2480: V9fsPath new_path;
! 2481: V9fsFidState *tfidp;
! 2482: V9fsState *s = pdu->s;
! 2483: V9fsFidState *dirfidp = NULL;
1.1 root 2484: char *old_name, *new_name;
2485:
1.1.1.2 ! root 2486: v9fs_path_init(&new_path);
! 2487: if (newdirfid != -1) {
! 2488: dirfidp = get_fid(pdu, newdirfid);
1.1 root 2489: if (dirfidp == NULL) {
2490: err = -ENOENT;
1.1.1.2 ! root 2491: goto out_nofid;
1.1 root 2492: }
2493: BUG_ON(dirfidp->fid_type != P9_FID_NONE);
1.1.1.2 ! root 2494: v9fs_co_name_to_path(pdu, &dirfidp->path, name->data, &new_path);
1.1 root 2495: } else {
1.1.1.2 ! root 2496: old_name = fidp->path.data;
1.1 root 2497: end = strrchr(old_name, '/');
2498: if (end) {
2499: end++;
2500: } else {
2501: end = old_name;
2502: }
1.1.1.2 ! root 2503: new_name = g_malloc0(end - old_name + name->size + 1);
1.1 root 2504: strncat(new_name, old_name, end - old_name);
1.1.1.2 ! root 2505: strncat(new_name + (end - old_name), name->data, name->size);
! 2506: v9fs_co_name_to_path(pdu, NULL, new_name, &new_path);
! 2507: g_free(new_name);
1.1 root 2508: }
1.1.1.2 ! root 2509: err = v9fs_co_rename(pdu, &fidp->path, &new_path);
! 2510: if (err < 0) {
! 2511: goto out;
! 2512: }
! 2513: /*
! 2514: * Fixup fid's pointing to the old name to
! 2515: * start pointing to the new name
! 2516: */
! 2517: for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) {
! 2518: if (v9fs_path_is_ancestor(&fidp->path, &tfidp->path)) {
! 2519: /* replace the name */
! 2520: v9fs_fix_path(&tfidp->path, &new_path, strlen(fidp->path.data));
1.1 root 2521: }
2522: }
2523: out:
1.1.1.2 ! root 2524: if (dirfidp) {
! 2525: put_fid(pdu, dirfidp);
! 2526: }
! 2527: v9fs_path_free(&new_path);
! 2528: out_nofid:
1.1 root 2529: return err;
2530: }
2531:
1.1.1.2 ! root 2532: /* Only works with path name based fid */
! 2533: static void v9fs_rename(void *opaque)
1.1 root 2534: {
1.1.1.2 ! root 2535: int32_t fid;
! 2536: ssize_t err = 0;
! 2537: size_t offset = 7;
! 2538: V9fsString name;
! 2539: int32_t newdirfid;
! 2540: V9fsFidState *fidp;
! 2541: V9fsPDU *pdu = opaque;
! 2542: V9fsState *s = pdu->s;
1.1 root 2543:
1.1.1.2 ! root 2544: pdu_unmarshal(pdu, offset, "dds", &fid, &newdirfid, &name);
! 2545:
! 2546: fidp = get_fid(pdu, fid);
! 2547: if (fidp == NULL) {
! 2548: err = -ENOENT;
! 2549: goto out_nofid;
! 2550: }
! 2551: BUG_ON(fidp->fid_type != P9_FID_NONE);
! 2552: /* if fs driver is not path based, return EOPNOTSUPP */
! 2553: if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) {
! 2554: err = -EOPNOTSUPP;
1.1 root 2555: goto out;
2556: }
1.1.1.2 ! root 2557: v9fs_path_write_lock(s);
! 2558: err = v9fs_complete_rename(pdu, fidp, newdirfid, &name);
! 2559: v9fs_path_unlock(s);
! 2560: if (!err) {
! 2561: err = offset;
1.1 root 2562: }
2563: out:
1.1.1.2 ! root 2564: put_fid(pdu, fidp);
! 2565: out_nofid:
! 2566: complete_pdu(s, pdu, err);
! 2567: v9fs_string_free(&name);
1.1 root 2568: }
2569:
1.1.1.2 ! root 2570: static void v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir,
! 2571: V9fsString *old_name, V9fsPath *newdir,
! 2572: V9fsString *new_name)
! 2573: {
! 2574: V9fsFidState *tfidp;
! 2575: V9fsPath oldpath, newpath;
! 2576: V9fsState *s = pdu->s;
1.1 root 2577:
2578:
1.1.1.2 ! root 2579: v9fs_path_init(&oldpath);
! 2580: v9fs_path_init(&newpath);
! 2581: v9fs_co_name_to_path(pdu, olddir, old_name->data, &oldpath);
! 2582: v9fs_co_name_to_path(pdu, newdir, new_name->data, &newpath);
1.1 root 2583:
1.1.1.2 ! root 2584: /*
! 2585: * Fixup fid's pointing to the old name to
! 2586: * start pointing to the new name
! 2587: */
! 2588: for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) {
! 2589: if (v9fs_path_is_ancestor(&oldpath, &tfidp->path)) {
! 2590: /* replace the name */
! 2591: v9fs_fix_path(&tfidp->path, &newpath, strlen(oldpath.data));
! 2592: }
1.1 root 2593: }
1.1.1.2 ! root 2594: v9fs_path_free(&oldpath);
! 2595: v9fs_path_free(&newpath);
1.1 root 2596: }
2597:
1.1.1.2 ! root 2598: static int v9fs_complete_renameat(V9fsPDU *pdu, int32_t olddirfid,
! 2599: V9fsString *old_name, int32_t newdirfid,
! 2600: V9fsString *new_name)
1.1 root 2601: {
1.1.1.2 ! root 2602: int err = 0;
! 2603: V9fsState *s = pdu->s;
! 2604: V9fsFidState *newdirfidp = NULL, *olddirfidp = NULL;
! 2605:
! 2606: olddirfidp = get_fid(pdu, olddirfid);
! 2607: if (olddirfidp == NULL) {
! 2608: err = -ENOENT;
1.1 root 2609: goto out;
2610: }
1.1.1.2 ! root 2611: if (newdirfid != -1) {
! 2612: newdirfidp = get_fid(pdu, newdirfid);
! 2613: if (newdirfidp == NULL) {
! 2614: err = -ENOENT;
! 2615: goto out;
1.1 root 2616: }
1.1.1.2 ! root 2617: } else {
! 2618: newdirfidp = get_fid(pdu, olddirfid);
1.1 root 2619: }
2620:
1.1.1.2 ! root 2621: err = v9fs_co_renameat(pdu, &olddirfidp->path, old_name,
! 2622: &newdirfidp->path, new_name);
1.1 root 2623: if (err < 0) {
2624: goto out;
2625: }
1.1.1.2 ! root 2626: if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
! 2627: /* Only for path based fid we need to do the below fixup */
! 2628: v9fs_fix_fid_paths(pdu, &olddirfidp->path, old_name,
! 2629: &newdirfidp->path, new_name);
1.1 root 2630: }
2631: out:
1.1.1.2 ! root 2632: if (olddirfidp) {
! 2633: put_fid(pdu, olddirfidp);
! 2634: }
! 2635: if (newdirfidp) {
! 2636: put_fid(pdu, newdirfidp);
1.1 root 2637: }
1.1.1.2 ! root 2638: return err;
1.1 root 2639: }
2640:
1.1.1.2 ! root 2641: static void v9fs_renameat(void *opaque)
1.1 root 2642: {
1.1.1.2 ! root 2643: ssize_t err = 0;
! 2644: size_t offset = 7;
! 2645: V9fsPDU *pdu = opaque;
! 2646: V9fsState *s = pdu->s;
! 2647: int32_t olddirfid, newdirfid;
! 2648: V9fsString old_name, new_name;
! 2649:
! 2650: pdu_unmarshal(pdu, offset, "dsds", &olddirfid,
! 2651: &old_name, &newdirfid, &new_name);
! 2652:
! 2653: v9fs_path_write_lock(s);
! 2654: err = v9fs_complete_renameat(pdu, olddirfid,
! 2655: &old_name, newdirfid, &new_name);
! 2656: v9fs_path_unlock(s);
! 2657: if (!err) {
! 2658: err = offset;
1.1 root 2659: }
1.1.1.2 ! root 2660: complete_pdu(s, pdu, err);
! 2661: v9fs_string_free(&old_name);
! 2662: v9fs_string_free(&new_name);
1.1 root 2663: }
2664:
1.1.1.2 ! root 2665: static void v9fs_wstat(void *opaque)
1.1 root 2666: {
2667: int32_t fid;
2668: int err = 0;
1.1.1.2 ! root 2669: int16_t unused;
! 2670: V9fsStat v9stat;
! 2671: size_t offset = 7;
! 2672: struct stat stbuf;
! 2673: V9fsFidState *fidp;
! 2674: V9fsPDU *pdu = opaque;
! 2675: V9fsState *s = pdu->s;
1.1 root 2676:
1.1.1.2 ! root 2677: pdu_unmarshal(pdu, offset, "dwS", &fid, &unused, &v9stat);
! 2678: trace_v9fs_wstat(pdu->tag, pdu->id, fid,
! 2679: v9stat.mode, v9stat.atime, v9stat.mtime);
1.1 root 2680:
1.1.1.2 ! root 2681: fidp = get_fid(pdu, fid);
! 2682: if (fidp == NULL) {
1.1 root 2683: err = -EINVAL;
1.1.1.2 ! root 2684: goto out_nofid;
1.1 root 2685: }
2686: /* do we need to sync the file? */
1.1.1.2 ! root 2687: if (donttouch_stat(&v9stat)) {
! 2688: err = v9fs_co_fsync(pdu, fidp, 0);
! 2689: goto out;
1.1 root 2690: }
1.1.1.2 ! root 2691: if (v9stat.mode != -1) {
! 2692: uint32_t v9_mode;
! 2693: err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
! 2694: if (err < 0) {
! 2695: goto out;
! 2696: }
! 2697: v9_mode = stat_to_v9mode(&stbuf);
! 2698: if ((v9stat.mode & P9_STAT_MODE_TYPE_BITS) !=
! 2699: (v9_mode & P9_STAT_MODE_TYPE_BITS)) {
! 2700: /* Attempting to change the type */
! 2701: err = -EIO;
! 2702: goto out;
! 2703: }
! 2704: err = v9fs_co_chmod(pdu, &fidp->path,
! 2705: v9mode_to_mode(v9stat.mode,
! 2706: &v9stat.extension));
! 2707: if (err < 0) {
! 2708: goto out;
! 2709: }
1.1 root 2710: }
1.1.1.2 ! root 2711: if (v9stat.mtime != -1 || v9stat.atime != -1) {
! 2712: struct timespec times[2];
! 2713: if (v9stat.atime != -1) {
! 2714: times[0].tv_sec = v9stat.atime;
! 2715: times[0].tv_nsec = 0;
! 2716: } else {
! 2717: times[0].tv_nsec = UTIME_OMIT;
! 2718: }
! 2719: if (v9stat.mtime != -1) {
! 2720: times[1].tv_sec = v9stat.mtime;
! 2721: times[1].tv_nsec = 0;
! 2722: } else {
! 2723: times[1].tv_nsec = UTIME_OMIT;
! 2724: }
! 2725: err = v9fs_co_utimensat(pdu, &fidp->path, times);
! 2726: if (err < 0) {
! 2727: goto out;
! 2728: }
! 2729: }
! 2730: if (v9stat.n_gid != -1 || v9stat.n_uid != -1) {
! 2731: err = v9fs_co_chown(pdu, &fidp->path, v9stat.n_uid, v9stat.n_gid);
! 2732: if (err < 0) {
! 2733: goto out;
! 2734: }
! 2735: }
! 2736: if (v9stat.name.size != 0) {
! 2737: err = v9fs_complete_rename(pdu, fidp, -1, &v9stat.name);
! 2738: if (err < 0) {
! 2739: goto out;
! 2740: }
! 2741: }
! 2742: if (v9stat.length != -1) {
! 2743: err = v9fs_co_truncate(pdu, &fidp->path, v9stat.length);
! 2744: if (err < 0) {
! 2745: goto out;
! 2746: }
! 2747: }
! 2748: err = offset;
1.1 root 2749: out:
1.1.1.2 ! root 2750: put_fid(pdu, fidp);
! 2751: out_nofid:
! 2752: v9fs_stat_free(&v9stat);
! 2753: complete_pdu(s, pdu, err);
1.1 root 2754: }
2755:
1.1.1.2 ! root 2756: static int v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf)
1.1 root 2757: {
1.1.1.2 ! root 2758: uint32_t f_type;
! 2759: uint32_t f_bsize;
! 2760: uint64_t f_blocks;
! 2761: uint64_t f_bfree;
! 2762: uint64_t f_bavail;
! 2763: uint64_t f_files;
! 2764: uint64_t f_ffree;
! 2765: uint64_t fsid_val;
! 2766: uint32_t f_namelen;
! 2767: size_t offset = 7;
1.1 root 2768: int32_t bsize_factor;
2769:
2770: /*
2771: * compute bsize factor based on host file system block size
2772: * and client msize
2773: */
1.1.1.2 ! root 2774: bsize_factor = (s->msize - P9_IOHDRSZ)/stbuf->f_bsize;
1.1 root 2775: if (!bsize_factor) {
2776: bsize_factor = 1;
2777: }
1.1.1.2 ! root 2778: f_type = stbuf->f_type;
! 2779: f_bsize = stbuf->f_bsize;
! 2780: f_bsize *= bsize_factor;
1.1 root 2781: /*
2782: * f_bsize is adjusted(multiplied) by bsize factor, so we need to
2783: * adjust(divide) the number of blocks, free blocks and available
2784: * blocks by bsize factor
2785: */
1.1.1.2 ! root 2786: f_blocks = stbuf->f_blocks/bsize_factor;
! 2787: f_bfree = stbuf->f_bfree/bsize_factor;
! 2788: f_bavail = stbuf->f_bavail/bsize_factor;
! 2789: f_files = stbuf->f_files;
! 2790: f_ffree = stbuf->f_ffree;
! 2791: fsid_val = (unsigned int) stbuf->f_fsid.__val[0] |
! 2792: (unsigned long long)stbuf->f_fsid.__val[1] << 32;
! 2793: f_namelen = stbuf->f_namelen;
! 2794:
! 2795: return pdu_marshal(pdu, offset, "ddqqqqqqd",
! 2796: f_type, f_bsize, f_blocks, f_bfree,
! 2797: f_bavail, f_files, f_ffree,
! 2798: fsid_val, f_namelen);
1.1 root 2799: }
2800:
1.1.1.2 ! root 2801: static void v9fs_statfs(void *opaque)
1.1 root 2802: {
1.1.1.2 ! root 2803: int32_t fid;
! 2804: ssize_t retval = 0;
! 2805: size_t offset = 7;
! 2806: V9fsFidState *fidp;
! 2807: struct statfs stbuf;
! 2808: V9fsPDU *pdu = opaque;
! 2809: V9fsState *s = pdu->s;
1.1 root 2810:
1.1.1.2 ! root 2811: pdu_unmarshal(pdu, offset, "d", &fid);
! 2812: fidp = get_fid(pdu, fid);
! 2813: if (fidp == NULL) {
! 2814: retval = -ENOENT;
! 2815: goto out_nofid;
1.1 root 2816: }
1.1.1.2 ! root 2817: retval = v9fs_co_statfs(pdu, &fidp->path, &stbuf);
! 2818: if (retval < 0) {
1.1 root 2819: goto out;
2820: }
1.1.1.2 ! root 2821: retval = offset;
! 2822: retval += v9fs_fill_statfs(s, pdu, &stbuf);
1.1 root 2823: out:
1.1.1.2 ! root 2824: put_fid(pdu, fidp);
! 2825: out_nofid:
! 2826: complete_pdu(s, pdu, retval);
1.1 root 2827: return;
2828: }
2829:
1.1.1.2 ! root 2830: static void v9fs_mknod(void *opaque)
1.1 root 2831: {
1.1.1.2 ! root 2832:
! 2833: int mode;
! 2834: gid_t gid;
1.1 root 2835: int32_t fid;
1.1.1.2 ! root 2836: V9fsQID qid;
1.1 root 2837: int err = 0;
2838: int major, minor;
1.1.1.2 ! root 2839: size_t offset = 7;
! 2840: V9fsString name;
! 2841: struct stat stbuf;
! 2842: V9fsFidState *fidp;
! 2843: V9fsPDU *pdu = opaque;
! 2844: V9fsState *s = pdu->s;
1.1 root 2845:
1.1.1.2 ! root 2846: pdu_unmarshal(pdu, offset, "dsdddd", &fid, &name, &mode,
! 2847: &major, &minor, &gid);
! 2848: trace_v9fs_mknod(pdu->tag, pdu->id, fid, mode, major, minor);
1.1 root 2849:
1.1.1.2 ! root 2850: fidp = get_fid(pdu, fid);
1.1 root 2851: if (fidp == NULL) {
2852: err = -ENOENT;
1.1.1.2 ! root 2853: goto out_nofid;
! 2854: }
! 2855: err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, gid,
! 2856: makedev(major, minor), mode, &stbuf);
! 2857: if (err < 0) {
1.1 root 2858: goto out;
2859: }
1.1.1.2 ! root 2860: stat_to_qid(&stbuf, &qid);
! 2861: err = offset;
! 2862: err += pdu_marshal(pdu, offset, "Q", &qid);
! 2863: trace_v9fs_mknod_return(pdu->tag, pdu->id,
! 2864: qid.type, qid.version, qid.path);
1.1 root 2865: out:
1.1.1.2 ! root 2866: put_fid(pdu, fidp);
! 2867: out_nofid:
! 2868: complete_pdu(s, pdu, err);
! 2869: v9fs_string_free(&name);
1.1 root 2870: }
2871:
2872: /*
2873: * Implement posix byte range locking code
2874: * Server side handling of locking code is very simple, because 9p server in
2875: * QEMU can handle only one client. And most of the lock handling
2876: * (like conflict, merging) etc is done by the VFS layer itself, so no need to
2877: * do any thing in * qemu 9p server side lock code path.
2878: * So when a TLOCK request comes, always return success
2879: */
1.1.1.2 ! root 2880: static void v9fs_lock(void *opaque)
1.1 root 2881: {
1.1.1.2 ! root 2882: int8_t status;
! 2883: V9fsFlock *flock;
! 2884: size_t offset = 7;
! 2885: struct stat stbuf;
! 2886: V9fsFidState *fidp;
1.1 root 2887: int32_t fid, err = 0;
1.1.1.2 ! root 2888: V9fsPDU *pdu = opaque;
! 2889: V9fsState *s = pdu->s;
! 2890:
! 2891: flock = g_malloc(sizeof(*flock));
! 2892: pdu_unmarshal(pdu, offset, "dbdqqds", &fid, &flock->type,
! 2893: &flock->flags, &flock->start, &flock->length,
! 2894: &flock->proc_id, &flock->client_id);
1.1 root 2895:
1.1.1.2 ! root 2896: trace_v9fs_lock(pdu->tag, pdu->id, fid,
! 2897: flock->type, flock->start, flock->length);
1.1 root 2898:
1.1.1.2 ! root 2899: status = P9_LOCK_ERROR;
1.1 root 2900:
2901: /* We support only block flag now (that too ignored currently) */
1.1.1.2 ! root 2902: if (flock->flags & ~P9_LOCK_FLAGS_BLOCK) {
1.1 root 2903: err = -EINVAL;
1.1.1.2 ! root 2904: goto out_nofid;
1.1 root 2905: }
1.1.1.2 ! root 2906: fidp = get_fid(pdu, fid);
! 2907: if (fidp == NULL) {
1.1 root 2908: err = -ENOENT;
1.1.1.2 ! root 2909: goto out_nofid;
1.1 root 2910: }
1.1.1.2 ! root 2911: err = v9fs_co_fstat(pdu, fidp, &stbuf);
1.1 root 2912: if (err < 0) {
2913: goto out;
2914: }
1.1.1.2 ! root 2915: status = P9_LOCK_SUCCESS;
1.1 root 2916: out:
1.1.1.2 ! root 2917: put_fid(pdu, fidp);
! 2918: out_nofid:
! 2919: err = offset;
! 2920: err += pdu_marshal(pdu, offset, "b", status);
! 2921: trace_v9fs_lock_return(pdu->tag, pdu->id, status);
! 2922: complete_pdu(s, pdu, err);
! 2923: v9fs_string_free(&flock->client_id);
! 2924: g_free(flock);
1.1 root 2925: }
2926:
2927: /*
2928: * When a TGETLOCK request comes, always return success because all lock
2929: * handling is done by client's VFS layer.
2930: */
1.1.1.2 ! root 2931: static void v9fs_getlock(void *opaque)
1.1 root 2932: {
1.1.1.2 ! root 2933: size_t offset = 7;
! 2934: struct stat stbuf;
! 2935: V9fsFidState *fidp;
! 2936: V9fsGetlock *glock;
1.1 root 2937: int32_t fid, err = 0;
1.1.1.2 ! root 2938: V9fsPDU *pdu = opaque;
! 2939: V9fsState *s = pdu->s;
! 2940:
! 2941: glock = g_malloc(sizeof(*glock));
! 2942: pdu_unmarshal(pdu, offset, "dbqqds", &fid, &glock->type,
! 2943: &glock->start, &glock->length, &glock->proc_id,
! 2944: &glock->client_id);
1.1 root 2945:
1.1.1.2 ! root 2946: trace_v9fs_getlock(pdu->tag, pdu->id, fid,
! 2947: glock->type, glock->start, glock->length);
1.1 root 2948:
1.1.1.2 ! root 2949: fidp = get_fid(pdu, fid);
! 2950: if (fidp == NULL) {
1.1 root 2951: err = -ENOENT;
1.1.1.2 ! root 2952: goto out_nofid;
1.1 root 2953: }
1.1.1.2 ! root 2954: err = v9fs_co_fstat(pdu, fidp, &stbuf);
1.1 root 2955: if (err < 0) {
2956: goto out;
2957: }
1.1.1.2 ! root 2958: glock->type = P9_LOCK_TYPE_UNLCK;
! 2959: offset += pdu_marshal(pdu, offset, "bqqds", glock->type,
! 2960: glock->start, glock->length, glock->proc_id,
! 2961: &glock->client_id);
! 2962: err = offset;
! 2963: trace_v9fs_getlock_return(pdu->tag, pdu->id, glock->type, glock->start,
! 2964: glock->length, glock->proc_id);
1.1 root 2965: out:
1.1.1.2 ! root 2966: put_fid(pdu, fidp);
! 2967: out_nofid:
! 2968: complete_pdu(s, pdu, err);
! 2969: v9fs_string_free(&glock->client_id);
! 2970: g_free(glock);
1.1 root 2971: }
2972:
1.1.1.2 ! root 2973: static void v9fs_mkdir(void *opaque)
1.1 root 2974: {
1.1.1.2 ! root 2975: V9fsPDU *pdu = opaque;
! 2976: size_t offset = 7;
1.1 root 2977: int32_t fid;
1.1.1.2 ! root 2978: struct stat stbuf;
! 2979: V9fsQID qid;
! 2980: V9fsString name;
1.1 root 2981: V9fsFidState *fidp;
2982: gid_t gid;
2983: int mode;
1.1.1.2 ! root 2984: int err = 0;
! 2985:
! 2986: pdu_unmarshal(pdu, offset, "dsdd", &fid, &name, &mode, &gid);
1.1 root 2987:
1.1.1.2 ! root 2988: trace_v9fs_mkdir(pdu->tag, pdu->id, fid, name.data, mode, gid);
1.1 root 2989:
1.1.1.2 ! root 2990: fidp = get_fid(pdu, fid);
1.1 root 2991: if (fidp == NULL) {
2992: err = -ENOENT;
1.1.1.2 ! root 2993: goto out_nofid;
1.1 root 2994: }
1.1.1.2 ! root 2995: err = v9fs_co_mkdir(pdu, fidp, &name, mode, fidp->uid, gid, &stbuf);
1.1 root 2996: if (err < 0) {
2997: goto out;
2998: }
1.1.1.2 ! root 2999: stat_to_qid(&stbuf, &qid);
! 3000: offset += pdu_marshal(pdu, offset, "Q", &qid);
! 3001: err = offset;
! 3002: trace_v9fs_mkdir_return(pdu->tag, pdu->id,
! 3003: qid.type, qid.version, qid.path, err);
1.1 root 3004: out:
1.1.1.2 ! root 3005: put_fid(pdu, fidp);
! 3006: out_nofid:
! 3007: complete_pdu(pdu->s, pdu, err);
! 3008: v9fs_string_free(&name);
1.1 root 3009: }
3010:
1.1.1.2 ! root 3011: static void v9fs_xattrwalk(void *opaque)
1.1 root 3012: {
1.1.1.2 ! root 3013: int64_t size;
! 3014: V9fsString name;
1.1 root 3015: ssize_t err = 0;
1.1.1.2 ! root 3016: size_t offset = 7;
1.1 root 3017: int32_t fid, newfid;
1.1.1.2 ! root 3018: V9fsFidState *file_fidp;
! 3019: V9fsFidState *xattr_fidp = NULL;
! 3020: V9fsPDU *pdu = opaque;
! 3021: V9fsState *s = pdu->s;
! 3022:
! 3023: pdu_unmarshal(pdu, offset, "dds", &fid, &newfid, &name);
! 3024: trace_v9fs_xattrwalk(pdu->tag, pdu->id, fid, newfid, name.data);
1.1 root 3025:
1.1.1.2 ! root 3026: file_fidp = get_fid(pdu, fid);
! 3027: if (file_fidp == NULL) {
1.1 root 3028: err = -ENOENT;
1.1.1.2 ! root 3029: goto out_nofid;
1.1 root 3030: }
1.1.1.2 ! root 3031: xattr_fidp = alloc_fid(s, newfid);
! 3032: if (xattr_fidp == NULL) {
1.1 root 3033: err = -EINVAL;
3034: goto out;
3035: }
1.1.1.2 ! root 3036: v9fs_path_copy(&xattr_fidp->path, &file_fidp->path);
! 3037: if (name.data[0] == 0) {
1.1 root 3038: /*
3039: * listxattr request. Get the size first
3040: */
1.1.1.2 ! root 3041: size = v9fs_co_llistxattr(pdu, &xattr_fidp->path, NULL, 0);
! 3042: if (size < 0) {
! 3043: err = size;
! 3044: clunk_fid(s, xattr_fidp->fid);
! 3045: goto out;
! 3046: }
! 3047: /*
! 3048: * Read the xattr value
! 3049: */
! 3050: xattr_fidp->fs.xattr.len = size;
! 3051: xattr_fidp->fid_type = P9_FID_XATTR;
! 3052: xattr_fidp->fs.xattr.copied_len = -1;
! 3053: if (size) {
! 3054: xattr_fidp->fs.xattr.value = g_malloc(size);
! 3055: err = v9fs_co_llistxattr(pdu, &xattr_fidp->path,
! 3056: xattr_fidp->fs.xattr.value,
! 3057: xattr_fidp->fs.xattr.len);
! 3058: if (err < 0) {
! 3059: clunk_fid(s, xattr_fidp->fid);
! 3060: goto out;
! 3061: }
1.1 root 3062: }
1.1.1.2 ! root 3063: offset += pdu_marshal(pdu, offset, "q", size);
! 3064: err = offset;
1.1 root 3065: } else {
3066: /*
3067: * specific xattr fid. We check for xattr
3068: * presence also collect the xattr size
3069: */
1.1.1.2 ! root 3070: size = v9fs_co_lgetxattr(pdu, &xattr_fidp->path,
! 3071: &name, NULL, 0);
! 3072: if (size < 0) {
! 3073: err = size;
! 3074: clunk_fid(s, xattr_fidp->fid);
! 3075: goto out;
! 3076: }
! 3077: /*
! 3078: * Read the xattr value
! 3079: */
! 3080: xattr_fidp->fs.xattr.len = size;
! 3081: xattr_fidp->fid_type = P9_FID_XATTR;
! 3082: xattr_fidp->fs.xattr.copied_len = -1;
! 3083: if (size) {
! 3084: xattr_fidp->fs.xattr.value = g_malloc(size);
! 3085: err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path,
! 3086: &name, xattr_fidp->fs.xattr.value,
! 3087: xattr_fidp->fs.xattr.len);
! 3088: if (err < 0) {
! 3089: clunk_fid(s, xattr_fidp->fid);
! 3090: goto out;
! 3091: }
1.1 root 3092: }
1.1.1.2 ! root 3093: offset += pdu_marshal(pdu, offset, "q", size);
! 3094: err = offset;
1.1 root 3095: }
1.1.1.2 ! root 3096: trace_v9fs_xattrwalk_return(pdu->tag, pdu->id, size);
1.1 root 3097: out:
1.1.1.2 ! root 3098: put_fid(pdu, file_fidp);
! 3099: if (xattr_fidp) {
! 3100: put_fid(pdu, xattr_fidp);
! 3101: }
! 3102: out_nofid:
! 3103: complete_pdu(s, pdu, err);
! 3104: v9fs_string_free(&name);
1.1 root 3105: }
3106:
1.1.1.2 ! root 3107: static void v9fs_xattrcreate(void *opaque)
1.1 root 3108: {
3109: int flags;
3110: int32_t fid;
1.1.1.2 ! root 3111: int64_t size;
1.1 root 3112: ssize_t err = 0;
1.1.1.2 ! root 3113: V9fsString name;
! 3114: size_t offset = 7;
! 3115: V9fsFidState *file_fidp;
! 3116: V9fsFidState *xattr_fidp;
! 3117: V9fsPDU *pdu = opaque;
! 3118: V9fsState *s = pdu->s;
! 3119:
! 3120: pdu_unmarshal(pdu, offset, "dsqd",
! 3121: &fid, &name, &size, &flags);
! 3122: trace_v9fs_xattrcreate(pdu->tag, pdu->id, fid, name.data, size, flags);
1.1 root 3123:
1.1.1.2 ! root 3124: file_fidp = get_fid(pdu, fid);
! 3125: if (file_fidp == NULL) {
1.1 root 3126: err = -EINVAL;
1.1.1.2 ! root 3127: goto out_nofid;
1.1 root 3128: }
3129: /* Make the file fid point to xattr */
1.1.1.2 ! root 3130: xattr_fidp = file_fidp;
! 3131: xattr_fidp->fid_type = P9_FID_XATTR;
! 3132: xattr_fidp->fs.xattr.copied_len = 0;
! 3133: xattr_fidp->fs.xattr.len = size;
! 3134: xattr_fidp->fs.xattr.flags = flags;
! 3135: v9fs_string_init(&xattr_fidp->fs.xattr.name);
! 3136: v9fs_string_copy(&xattr_fidp->fs.xattr.name, &name);
! 3137: if (size) {
! 3138: xattr_fidp->fs.xattr.value = g_malloc(size);
! 3139: } else {
! 3140: xattr_fidp->fs.xattr.value = NULL;
1.1 root 3141: }
1.1.1.2 ! root 3142: err = offset;
! 3143: put_fid(pdu, file_fidp);
! 3144: out_nofid:
! 3145: complete_pdu(s, pdu, err);
! 3146: v9fs_string_free(&name);
1.1 root 3147: }
3148:
1.1.1.2 ! root 3149: static void v9fs_readlink(void *opaque)
1.1 root 3150: {
1.1.1.2 ! root 3151: V9fsPDU *pdu = opaque;
! 3152: size_t offset = 7;
! 3153: V9fsString target;
1.1 root 3154: int32_t fid;
3155: int err = 0;
3156: V9fsFidState *fidp;
3157:
1.1.1.2 ! root 3158: pdu_unmarshal(pdu, offset, "d", &fid);
! 3159: trace_v9fs_readlink(pdu->tag, pdu->id, fid);
! 3160: fidp = get_fid(pdu, fid);
1.1 root 3161: if (fidp == NULL) {
3162: err = -ENOENT;
1.1.1.2 ! root 3163: goto out_nofid;
1.1 root 3164: }
3165:
1.1.1.2 ! root 3166: v9fs_string_init(&target);
! 3167: err = v9fs_co_readlink(pdu, &fidp->path, &target);
! 3168: if (err < 0) {
! 3169: goto out;
! 3170: }
! 3171: offset += pdu_marshal(pdu, offset, "s", &target);
! 3172: err = offset;
! 3173: trace_v9fs_readlink_return(pdu->tag, pdu->id, target.data);
! 3174: v9fs_string_free(&target);
1.1 root 3175: out:
1.1.1.2 ! root 3176: put_fid(pdu, fidp);
! 3177: out_nofid:
! 3178: complete_pdu(pdu->s, pdu, err);
1.1 root 3179: }
3180:
1.1.1.2 ! root 3181: static CoroutineEntry *pdu_co_handlers[] = {
1.1 root 3182: [P9_TREADDIR] = v9fs_readdir,
3183: [P9_TSTATFS] = v9fs_statfs,
3184: [P9_TGETATTR] = v9fs_getattr,
3185: [P9_TSETATTR] = v9fs_setattr,
3186: [P9_TXATTRWALK] = v9fs_xattrwalk,
3187: [P9_TXATTRCREATE] = v9fs_xattrcreate,
3188: [P9_TMKNOD] = v9fs_mknod,
3189: [P9_TRENAME] = v9fs_rename,
3190: [P9_TLOCK] = v9fs_lock,
3191: [P9_TGETLOCK] = v9fs_getlock,
1.1.1.2 ! root 3192: [P9_TRENAMEAT] = v9fs_renameat,
1.1 root 3193: [P9_TREADLINK] = v9fs_readlink,
1.1.1.2 ! root 3194: [P9_TUNLINKAT] = v9fs_unlinkat,
1.1 root 3195: [P9_TMKDIR] = v9fs_mkdir,
3196: [P9_TVERSION] = v9fs_version,
3197: [P9_TLOPEN] = v9fs_open,
3198: [P9_TATTACH] = v9fs_attach,
3199: [P9_TSTAT] = v9fs_stat,
3200: [P9_TWALK] = v9fs_walk,
3201: [P9_TCLUNK] = v9fs_clunk,
3202: [P9_TFSYNC] = v9fs_fsync,
3203: [P9_TOPEN] = v9fs_open,
3204: [P9_TREAD] = v9fs_read,
3205: #if 0
3206: [P9_TAUTH] = v9fs_auth,
3207: #endif
3208: [P9_TFLUSH] = v9fs_flush,
3209: [P9_TLINK] = v9fs_link,
3210: [P9_TSYMLINK] = v9fs_symlink,
3211: [P9_TCREATE] = v9fs_create,
3212: [P9_TLCREATE] = v9fs_lcreate,
3213: [P9_TWRITE] = v9fs_write,
3214: [P9_TWSTAT] = v9fs_wstat,
3215: [P9_TREMOVE] = v9fs_remove,
3216: };
3217:
1.1.1.2 ! root 3218: static void v9fs_op_not_supp(void *opaque)
1.1 root 3219: {
1.1.1.2 ! root 3220: V9fsPDU *pdu = opaque;
! 3221: complete_pdu(pdu->s, pdu, -EOPNOTSUPP);
! 3222: }
! 3223:
! 3224: static void v9fs_fs_ro(void *opaque)
! 3225: {
! 3226: V9fsPDU *pdu = opaque;
! 3227: complete_pdu(pdu->s, pdu, -EROFS);
! 3228: }
! 3229:
! 3230: static inline bool is_read_only_op(V9fsPDU *pdu)
! 3231: {
! 3232: switch (pdu->id) {
! 3233: case P9_TREADDIR:
! 3234: case P9_TSTATFS:
! 3235: case P9_TGETATTR:
! 3236: case P9_TXATTRWALK:
! 3237: case P9_TLOCK:
! 3238: case P9_TGETLOCK:
! 3239: case P9_TREADLINK:
! 3240: case P9_TVERSION:
! 3241: case P9_TLOPEN:
! 3242: case P9_TATTACH:
! 3243: case P9_TSTAT:
! 3244: case P9_TWALK:
! 3245: case P9_TCLUNK:
! 3246: case P9_TFSYNC:
! 3247: case P9_TOPEN:
! 3248: case P9_TREAD:
! 3249: case P9_TAUTH:
! 3250: case P9_TFLUSH:
! 3251: return 1;
! 3252: default:
! 3253: return 0;
! 3254: }
1.1 root 3255: }
3256:
3257: static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
3258: {
1.1.1.2 ! root 3259: Coroutine *co;
! 3260: CoroutineEntry *handler;
1.1 root 3261:
1.1.1.2 ! root 3262: if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) ||
! 3263: (pdu_co_handlers[pdu->id] == NULL)) {
1.1 root 3264: handler = v9fs_op_not_supp;
3265: } else {
1.1.1.2 ! root 3266: handler = pdu_co_handlers[pdu->id];
! 3267: }
! 3268:
! 3269: if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) {
! 3270: handler = v9fs_fs_ro;
1.1 root 3271: }
1.1.1.2 ! root 3272: co = qemu_coroutine_create(handler);
! 3273: qemu_coroutine_enter(co, pdu);
1.1 root 3274: }
3275:
3276: void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
3277: {
3278: V9fsState *s = (V9fsState *)vdev;
3279: V9fsPDU *pdu;
3280: ssize_t len;
3281:
3282: while ((pdu = alloc_pdu(s)) &&
3283: (len = virtqueue_pop(vq, &pdu->elem)) != 0) {
3284: uint8_t *ptr;
1.1.1.2 ! root 3285: pdu->s = s;
1.1 root 3286: BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0);
3287: BUG_ON(pdu->elem.out_sg[0].iov_len < 7);
3288:
3289: ptr = pdu->elem.out_sg[0].iov_base;
3290:
3291: memcpy(&pdu->size, ptr, 4);
3292: pdu->id = ptr[4];
3293: memcpy(&pdu->tag, ptr + 5, 2);
1.1.1.2 ! root 3294: qemu_co_queue_init(&pdu->complete);
1.1 root 3295: submit_pdu(s, pdu);
3296: }
3297: free_pdu(s, pdu);
3298: }
1.1.1.2 ! root 3299:
! 3300: void virtio_9p_set_fd_limit(void)
! 3301: {
! 3302: struct rlimit rlim;
! 3303: if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
! 3304: fprintf(stderr, "Failed to get the resource limit\n");
! 3305: exit(1);
! 3306: }
! 3307: open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur/3);
! 3308: open_fd_rc = rlim.rlim_cur/2;
! 3309: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.