|
|
1.1 root 1: /*
2: * Virtio 9p Posix callback
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:
14: #include "hw/virtio.h"
15: #include "virtio-9p.h"
16: #include "virtio-9p-xattr.h"
17: #include <arpa/inet.h>
18: #include <pwd.h>
19: #include <grp.h>
20: #include <sys/socket.h>
21: #include <sys/un.h>
1.1.1.2 root 22: #include "qemu-xattr.h"
1.1.1.3 ! root 23: #include <libgen.h>
1.1.1.2 root 24: #include <linux/fs.h>
25: #ifdef CONFIG_LINUX_MAGIC_H
26: #include <linux/magic.h>
27: #endif
28: #include <sys/ioctl.h>
1.1 root 29:
1.1.1.2 root 30: #ifndef XFS_SUPER_MAGIC
31: #define XFS_SUPER_MAGIC 0x58465342
32: #endif
33: #ifndef EXT2_SUPER_MAGIC
34: #define EXT2_SUPER_MAGIC 0xEF53
35: #endif
36: #ifndef REISERFS_SUPER_MAGIC
37: #define REISERFS_SUPER_MAGIC 0x52654973
38: #endif
39: #ifndef BTRFS_SUPER_MAGIC
40: #define BTRFS_SUPER_MAGIC 0x9123683E
41: #endif
1.1 root 42:
1.1.1.3 ! root 43: #define VIRTFS_META_DIR ".virtfs_metadata"
! 44:
! 45: static const char *local_mapped_attr_path(FsContext *ctx,
! 46: const char *path, char *buffer)
! 47: {
! 48: char *dir_name;
! 49: char *tmp_path = strdup(path);
! 50: char *base_name = basename(tmp_path);
! 51:
! 52: /* NULL terminate the directory */
! 53: dir_name = tmp_path;
! 54: *(base_name - 1) = '\0';
! 55:
! 56: snprintf(buffer, PATH_MAX, "%s/%s/%s/%s",
! 57: ctx->fs_root, dir_name, VIRTFS_META_DIR, base_name);
! 58: free(tmp_path);
! 59: return buffer;
! 60: }
! 61:
! 62: #define ATTR_MAX 100
! 63: static void local_mapped_file_attr(FsContext *ctx, const char *path,
! 64: struct stat *stbuf)
! 65: {
! 66: FILE *fp;
! 67: char buf[ATTR_MAX];
! 68: char attr_path[PATH_MAX];
! 69:
! 70: local_mapped_attr_path(ctx, path, attr_path);
! 71: fp = fopen(attr_path, "r");
! 72: if (!fp) {
! 73: return;
! 74: }
! 75: memset(buf, 0, ATTR_MAX);
! 76: while (fgets(buf, ATTR_MAX, fp)) {
! 77: if (!strncmp(buf, "virtfs.uid", 10)) {
! 78: stbuf->st_uid = atoi(buf+11);
! 79: } else if (!strncmp(buf, "virtfs.gid", 10)) {
! 80: stbuf->st_gid = atoi(buf+11);
! 81: } else if (!strncmp(buf, "virtfs.mode", 11)) {
! 82: stbuf->st_mode = atoi(buf+12);
! 83: } else if (!strncmp(buf, "virtfs.rdev", 11)) {
! 84: stbuf->st_rdev = atoi(buf+12);
! 85: }
! 86: memset(buf, 0, ATTR_MAX);
! 87: }
! 88: fclose(fp);
! 89: }
! 90:
1.1.1.2 root 91: static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
1.1 root 92: {
93: int err;
94: char buffer[PATH_MAX];
1.1.1.2 root 95: char *path = fs_path->data;
96:
1.1 root 97: err = lstat(rpath(fs_ctx, path, buffer), stbuf);
98: if (err) {
99: return err;
100: }
1.1.1.2 root 101: if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
1.1 root 102: /* Actual credentials are part of extended attrs */
103: uid_t tmp_uid;
104: gid_t tmp_gid;
105: mode_t tmp_mode;
106: dev_t tmp_dev;
107: if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.uid", &tmp_uid,
108: sizeof(uid_t)) > 0) {
109: stbuf->st_uid = tmp_uid;
110: }
111: if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.gid", &tmp_gid,
112: sizeof(gid_t)) > 0) {
113: stbuf->st_gid = tmp_gid;
114: }
115: if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.mode",
116: &tmp_mode, sizeof(mode_t)) > 0) {
117: stbuf->st_mode = tmp_mode;
118: }
119: if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.rdev", &tmp_dev,
120: sizeof(dev_t)) > 0) {
121: stbuf->st_rdev = tmp_dev;
122: }
1.1.1.3 ! root 123: } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
! 124: local_mapped_file_attr(fs_ctx, path, stbuf);
! 125: }
! 126: return err;
! 127: }
! 128:
! 129: static int local_create_mapped_attr_dir(FsContext *ctx, const char *path)
! 130: {
! 131: int err;
! 132: char attr_dir[PATH_MAX];
! 133: char *tmp_path = strdup(path);
! 134:
! 135: snprintf(attr_dir, PATH_MAX, "%s/%s/%s",
! 136: ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR);
! 137:
! 138: err = mkdir(attr_dir, 0700);
! 139: if (err < 0 && errno == EEXIST) {
! 140: err = 0;
1.1 root 141: }
1.1.1.3 ! root 142: free(tmp_path);
1.1 root 143: return err;
144: }
145:
1.1.1.3 ! root 146: static int local_set_mapped_file_attr(FsContext *ctx,
! 147: const char *path, FsCred *credp)
! 148: {
! 149: FILE *fp;
! 150: int ret = 0;
! 151: char buf[ATTR_MAX];
! 152: char attr_path[PATH_MAX];
! 153: int uid = -1, gid = -1, mode = -1, rdev = -1;
! 154:
! 155: fp = fopen(local_mapped_attr_path(ctx, path, attr_path), "r");
! 156: if (!fp) {
! 157: goto create_map_file;
! 158: }
! 159: memset(buf, 0, ATTR_MAX);
! 160: while (fgets(buf, ATTR_MAX, fp)) {
! 161: if (!strncmp(buf, "virtfs.uid", 10)) {
! 162: uid = atoi(buf+11);
! 163: } else if (!strncmp(buf, "virtfs.gid", 10)) {
! 164: gid = atoi(buf+11);
! 165: } else if (!strncmp(buf, "virtfs.mode", 11)) {
! 166: mode = atoi(buf+12);
! 167: } else if (!strncmp(buf, "virtfs.rdev", 11)) {
! 168: rdev = atoi(buf+12);
! 169: }
! 170: memset(buf, 0, ATTR_MAX);
! 171: }
! 172: fclose(fp);
! 173: goto update_map_file;
! 174:
! 175: create_map_file:
! 176: ret = local_create_mapped_attr_dir(ctx, path);
! 177: if (ret < 0) {
! 178: goto err_out;
! 179: }
! 180:
! 181: update_map_file:
! 182: fp = fopen(attr_path, "w");
! 183: if (!fp) {
! 184: ret = -1;
! 185: goto err_out;
! 186: }
! 187:
! 188: if (credp->fc_uid != -1) {
! 189: uid = credp->fc_uid;
! 190: }
! 191: if (credp->fc_gid != -1) {
! 192: gid = credp->fc_gid;
! 193: }
! 194: if (credp->fc_mode != -1) {
! 195: mode = credp->fc_mode;
! 196: }
! 197: if (credp->fc_rdev != -1) {
! 198: rdev = credp->fc_rdev;
! 199: }
! 200:
! 201:
! 202: if (uid != -1) {
! 203: fprintf(fp, "virtfs.uid=%d\n", uid);
! 204: }
! 205: if (gid != -1) {
! 206: fprintf(fp, "virtfs.gid=%d\n", gid);
! 207: }
! 208: if (mode != -1) {
! 209: fprintf(fp, "virtfs.mode=%d\n", mode);
! 210: }
! 211: if (rdev != -1) {
! 212: fprintf(fp, "virtfs.rdev=%d\n", rdev);
! 213: }
! 214: fclose(fp);
! 215:
! 216: err_out:
! 217: return ret;
! 218: }
! 219:
1.1 root 220: static int local_set_xattr(const char *path, FsCred *credp)
221: {
222: int err;
1.1.1.2 root 223:
1.1 root 224: if (credp->fc_uid != -1) {
225: err = setxattr(path, "user.virtfs.uid", &credp->fc_uid, sizeof(uid_t),
226: 0);
227: if (err) {
228: return err;
229: }
230: }
231: if (credp->fc_gid != -1) {
232: err = setxattr(path, "user.virtfs.gid", &credp->fc_gid, sizeof(gid_t),
233: 0);
234: if (err) {
235: return err;
236: }
237: }
238: if (credp->fc_mode != -1) {
239: err = setxattr(path, "user.virtfs.mode", &credp->fc_mode,
240: sizeof(mode_t), 0);
241: if (err) {
242: return err;
243: }
244: }
245: if (credp->fc_rdev != -1) {
246: err = setxattr(path, "user.virtfs.rdev", &credp->fc_rdev,
247: sizeof(dev_t), 0);
248: if (err) {
249: return err;
250: }
251: }
252: return 0;
253: }
254:
255: static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
1.1.1.2 root 256: FsCred *credp)
1.1 root 257: {
258: char buffer[PATH_MAX];
1.1.1.2 root 259:
1.1 root 260: if (lchown(rpath(fs_ctx, path, buffer), credp->fc_uid,
261: credp->fc_gid) < 0) {
262: /*
263: * If we fail to change ownership and if we are
264: * using security model none. Ignore the error
265: */
1.1.1.2 root 266: if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
1.1 root 267: return -1;
268: }
269: }
1.1.1.3 ! root 270:
! 271: if (chmod(rpath(fs_ctx, path, buffer), credp->fc_mode & 07777) < 0) {
! 272: return -1;
! 273: }
1.1 root 274: return 0;
275: }
276:
1.1.1.2 root 277: static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
278: char *buf, size_t bufsz)
1.1 root 279: {
280: ssize_t tsize = -1;
281: char buffer[PATH_MAX];
1.1.1.2 root 282: char *path = fs_path->data;
283:
1.1.1.3 ! root 284: if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
! 285: (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
1.1 root 286: int fd;
287: fd = open(rpath(fs_ctx, path, buffer), O_RDONLY);
288: if (fd == -1) {
289: return -1;
290: }
291: do {
292: tsize = read(fd, (void *)buf, bufsz);
293: } while (tsize == -1 && errno == EINTR);
294: close(fd);
295: return tsize;
1.1.1.2 root 296: } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
297: (fs_ctx->export_flags & V9FS_SM_NONE)) {
1.1 root 298: tsize = readlink(rpath(fs_ctx, path, buffer), buf, bufsz);
299: }
300: return tsize;
301: }
302:
1.1.1.2 root 303: static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
1.1 root 304: {
1.1.1.2 root 305: return close(fs->fd);
1.1 root 306: }
307:
1.1.1.2 root 308: static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
1.1 root 309: {
1.1.1.2 root 310: return closedir(fs->dir);
1.1 root 311: }
312:
1.1.1.2 root 313: static int local_open(FsContext *ctx, V9fsPath *fs_path,
314: int flags, V9fsFidOpenState *fs)
1.1 root 315: {
316: char buffer[PATH_MAX];
1.1.1.2 root 317: char *path = fs_path->data;
318:
319: fs->fd = open(rpath(ctx, path, buffer), flags);
320: return fs->fd;
1.1 root 321: }
322:
1.1.1.2 root 323: static int local_opendir(FsContext *ctx,
324: V9fsPath *fs_path, V9fsFidOpenState *fs)
1.1 root 325: {
326: char buffer[PATH_MAX];
1.1.1.2 root 327: char *path = fs_path->data;
328:
329: fs->dir = opendir(rpath(ctx, path, buffer));
330: if (!fs->dir) {
331: return -1;
332: }
333: return 0;
1.1 root 334: }
335:
1.1.1.2 root 336: static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
1.1 root 337: {
1.1.1.2 root 338: return rewinddir(fs->dir);
1.1 root 339: }
340:
1.1.1.2 root 341: static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
1.1 root 342: {
1.1.1.2 root 343: return telldir(fs->dir);
1.1 root 344: }
345:
1.1.1.2 root 346: static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
347: struct dirent *entry,
348: struct dirent **result)
1.1 root 349: {
1.1.1.3 ! root 350: int ret;
! 351:
! 352: again:
! 353: ret = readdir_r(fs->dir, entry, result);
! 354: if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
! 355: if (!ret && *result != NULL &&
! 356: !strcmp(entry->d_name, VIRTFS_META_DIR)) {
! 357: /* skp the meta data directory */
! 358: goto again;
! 359: }
! 360: }
! 361: return ret;
1.1 root 362: }
363:
1.1.1.2 root 364: static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
1.1 root 365: {
1.1.1.2 root 366: return seekdir(fs->dir, off);
1.1 root 367: }
368:
1.1.1.2 root 369: static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
370: const struct iovec *iov,
1.1 root 371: int iovcnt, off_t offset)
372: {
373: #ifdef CONFIG_PREADV
1.1.1.2 root 374: return preadv(fs->fd, iov, iovcnt, offset);
1.1 root 375: #else
1.1.1.2 root 376: int err = lseek(fs->fd, offset, SEEK_SET);
1.1 root 377: if (err == -1) {
378: return err;
379: } else {
1.1.1.2 root 380: return readv(fs->fd, iov, iovcnt);
1.1 root 381: }
382: #endif
383: }
384:
1.1.1.2 root 385: static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
386: const struct iovec *iov,
387: int iovcnt, off_t offset)
1.1 root 388: {
1.1.1.2 root 389: ssize_t ret
390: ;
1.1 root 391: #ifdef CONFIG_PREADV
1.1.1.2 root 392: ret = pwritev(fs->fd, iov, iovcnt, offset);
1.1 root 393: #else
1.1.1.2 root 394: int err = lseek(fs->fd, offset, SEEK_SET);
1.1 root 395: if (err == -1) {
396: return err;
397: } else {
1.1.1.2 root 398: ret = writev(fs->fd, iov, iovcnt);
1.1 root 399: }
400: #endif
1.1.1.2 root 401: #ifdef CONFIG_SYNC_FILE_RANGE
402: if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
403: /*
404: * Initiate a writeback. This is not a data integrity sync.
405: * We want to ensure that we don't leave dirty pages in the cache
406: * after write when writeout=immediate is sepcified.
407: */
408: sync_file_range(fs->fd, offset, ret,
409: SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
410: }
411: #endif
412: return ret;
1.1 root 413: }
414:
1.1.1.2 root 415: static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
1.1 root 416: {
417: char buffer[PATH_MAX];
1.1.1.2 root 418: char *path = fs_path->data;
419:
420: if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
1.1 root 421: return local_set_xattr(rpath(fs_ctx, path, buffer), credp);
1.1.1.3 ! root 422: } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
! 423: return local_set_mapped_file_attr(fs_ctx, path, credp);
1.1.1.2 root 424: } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
425: (fs_ctx->export_flags & V9FS_SM_NONE)) {
1.1 root 426: return chmod(rpath(fs_ctx, path, buffer), credp->fc_mode);
427: }
428: return -1;
429: }
430:
1.1.1.2 root 431: static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
432: const char *name, FsCred *credp)
1.1 root 433: {
1.1.1.2 root 434: char *path;
1.1 root 435: int err = -1;
436: int serrno = 0;
1.1.1.2 root 437: V9fsString fullname;
1.1 root 438: char buffer[PATH_MAX];
439:
1.1.1.2 root 440: v9fs_string_init(&fullname);
441: v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
442: path = fullname.data;
443:
1.1 root 444: /* Determine the security model */
1.1.1.2 root 445: if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
1.1 root 446: err = mknod(rpath(fs_ctx, path, buffer),
447: SM_LOCAL_MODE_BITS|S_IFREG, 0);
448: if (err == -1) {
1.1.1.2 root 449: goto out;
1.1 root 450: }
1.1.1.2 root 451: err = local_set_xattr(rpath(fs_ctx, path, buffer), credp);
1.1 root 452: if (err == -1) {
453: serrno = errno;
454: goto err_end;
455: }
1.1.1.3 ! root 456: } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
! 457:
! 458: err = mknod(rpath(fs_ctx, path, buffer),
! 459: SM_LOCAL_MODE_BITS|S_IFREG, 0);
! 460: if (err == -1) {
! 461: goto out;
! 462: }
! 463: err = local_set_mapped_file_attr(fs_ctx, path, credp);
! 464: if (err == -1) {
! 465: serrno = errno;
! 466: goto err_end;
! 467: }
1.1.1.2 root 468: } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
469: (fs_ctx->export_flags & V9FS_SM_NONE)) {
1.1 root 470: err = mknod(rpath(fs_ctx, path, buffer), credp->fc_mode,
471: credp->fc_rdev);
472: if (err == -1) {
1.1.1.2 root 473: goto out;
1.1 root 474: }
475: err = local_post_create_passthrough(fs_ctx, path, credp);
476: if (err == -1) {
477: serrno = errno;
478: goto err_end;
479: }
480: }
1.1.1.2 root 481: goto out;
1.1 root 482:
483: err_end:
484: remove(rpath(fs_ctx, path, buffer));
485: errno = serrno;
1.1.1.2 root 486: out:
487: v9fs_string_free(&fullname);
1.1 root 488: return err;
489: }
490:
1.1.1.2 root 491: static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
492: const char *name, FsCred *credp)
1.1 root 493: {
1.1.1.2 root 494: char *path;
1.1 root 495: int err = -1;
496: int serrno = 0;
1.1.1.2 root 497: V9fsString fullname;
1.1 root 498: char buffer[PATH_MAX];
499:
1.1.1.2 root 500: v9fs_string_init(&fullname);
501: v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
502: path = fullname.data;
503:
1.1 root 504: /* Determine the security model */
1.1.1.2 root 505: if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
1.1 root 506: err = mkdir(rpath(fs_ctx, path, buffer), SM_LOCAL_DIR_MODE_BITS);
507: if (err == -1) {
1.1.1.2 root 508: goto out;
1.1 root 509: }
510: credp->fc_mode = credp->fc_mode|S_IFDIR;
511: err = local_set_xattr(rpath(fs_ctx, path, buffer), credp);
512: if (err == -1) {
513: serrno = errno;
514: goto err_end;
515: }
1.1.1.3 ! root 516: } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
! 517: err = mkdir(rpath(fs_ctx, path, buffer), SM_LOCAL_DIR_MODE_BITS);
! 518: if (err == -1) {
! 519: goto out;
! 520: }
! 521: credp->fc_mode = credp->fc_mode|S_IFDIR;
! 522: err = local_set_mapped_file_attr(fs_ctx, path, credp);
! 523: if (err == -1) {
! 524: serrno = errno;
! 525: goto err_end;
! 526: }
1.1.1.2 root 527: } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
528: (fs_ctx->export_flags & V9FS_SM_NONE)) {
1.1 root 529: err = mkdir(rpath(fs_ctx, path, buffer), credp->fc_mode);
530: if (err == -1) {
1.1.1.2 root 531: goto out;
1.1 root 532: }
533: err = local_post_create_passthrough(fs_ctx, path, credp);
534: if (err == -1) {
535: serrno = errno;
536: goto err_end;
537: }
538: }
1.1.1.2 root 539: goto out;
1.1 root 540:
541: err_end:
542: remove(rpath(fs_ctx, path, buffer));
543: errno = serrno;
1.1.1.2 root 544: out:
545: v9fs_string_free(&fullname);
1.1 root 546: return err;
547: }
548:
1.1.1.2 root 549: static int local_fstat(FsContext *fs_ctx, int fid_type,
550: V9fsFidOpenState *fs, struct stat *stbuf)
1.1 root 551: {
1.1.1.2 root 552: int err, fd;
553:
554: if (fid_type == P9_FID_DIR) {
555: fd = dirfd(fs->dir);
556: } else {
557: fd = fs->fd;
558: }
559:
1.1 root 560: err = fstat(fd, stbuf);
561: if (err) {
562: return err;
563: }
1.1.1.2 root 564: if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
1.1 root 565: /* Actual credentials are part of extended attrs */
566: uid_t tmp_uid;
567: gid_t tmp_gid;
568: mode_t tmp_mode;
569: dev_t tmp_dev;
570:
1.1.1.2 root 571: if (fgetxattr(fd, "user.virtfs.uid",
572: &tmp_uid, sizeof(uid_t)) > 0) {
1.1 root 573: stbuf->st_uid = tmp_uid;
574: }
1.1.1.2 root 575: if (fgetxattr(fd, "user.virtfs.gid",
576: &tmp_gid, sizeof(gid_t)) > 0) {
1.1 root 577: stbuf->st_gid = tmp_gid;
578: }
1.1.1.2 root 579: if (fgetxattr(fd, "user.virtfs.mode",
580: &tmp_mode, sizeof(mode_t)) > 0) {
1.1 root 581: stbuf->st_mode = tmp_mode;
582: }
1.1.1.2 root 583: if (fgetxattr(fd, "user.virtfs.rdev",
584: &tmp_dev, sizeof(dev_t)) > 0) {
1.1 root 585: stbuf->st_rdev = tmp_dev;
586: }
1.1.1.3 ! root 587: } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
! 588: errno = EOPNOTSUPP;
! 589: return -1;
1.1 root 590: }
591: return err;
592: }
593:
1.1.1.2 root 594: static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
595: int flags, FsCred *credp, V9fsFidOpenState *fs)
1.1 root 596: {
1.1.1.2 root 597: char *path;
1.1 root 598: int fd = -1;
599: int err = -1;
600: int serrno = 0;
1.1.1.2 root 601: V9fsString fullname;
1.1 root 602: char buffer[PATH_MAX];
603:
1.1.1.2 root 604: v9fs_string_init(&fullname);
605: v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
606: path = fullname.data;
607:
1.1 root 608: /* Determine the security model */
1.1.1.2 root 609: if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
1.1 root 610: fd = open(rpath(fs_ctx, path, buffer), flags, SM_LOCAL_MODE_BITS);
611: if (fd == -1) {
1.1.1.2 root 612: err = fd;
613: goto out;
1.1 root 614: }
615: credp->fc_mode = credp->fc_mode|S_IFREG;
616: /* Set cleint credentials in xattr */
617: err = local_set_xattr(rpath(fs_ctx, path, buffer), credp);
618: if (err == -1) {
619: serrno = errno;
620: goto err_end;
621: }
1.1.1.3 ! root 622: } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
! 623: fd = open(rpath(fs_ctx, path, buffer), flags, SM_LOCAL_MODE_BITS);
! 624: if (fd == -1) {
! 625: err = fd;
! 626: goto out;
! 627: }
! 628: credp->fc_mode = credp->fc_mode|S_IFREG;
! 629: /* Set client credentials in .virtfs_metadata directory files */
! 630: err = local_set_mapped_file_attr(fs_ctx, path, credp);
! 631: if (err == -1) {
! 632: serrno = errno;
! 633: goto err_end;
! 634: }
1.1.1.2 root 635: } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
636: (fs_ctx->export_flags & V9FS_SM_NONE)) {
1.1 root 637: fd = open(rpath(fs_ctx, path, buffer), flags, credp->fc_mode);
638: if (fd == -1) {
1.1.1.2 root 639: err = fd;
640: goto out;
1.1 root 641: }
642: err = local_post_create_passthrough(fs_ctx, path, credp);
643: if (err == -1) {
644: serrno = errno;
645: goto err_end;
646: }
647: }
1.1.1.2 root 648: err = fd;
649: fs->fd = fd;
650: goto out;
1.1 root 651:
652: err_end:
653: close(fd);
654: remove(rpath(fs_ctx, path, buffer));
655: errno = serrno;
1.1.1.2 root 656: out:
657: v9fs_string_free(&fullname);
1.1 root 658: return err;
659: }
660:
661:
662: static int local_symlink(FsContext *fs_ctx, const char *oldpath,
1.1.1.2 root 663: V9fsPath *dir_path, const char *name, FsCred *credp)
1.1 root 664: {
665: int err = -1;
666: int serrno = 0;
1.1.1.2 root 667: char *newpath;
668: V9fsString fullname;
1.1 root 669: char buffer[PATH_MAX];
670:
1.1.1.2 root 671: v9fs_string_init(&fullname);
672: v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
673: newpath = fullname.data;
674:
1.1 root 675: /* Determine the security model */
1.1.1.2 root 676: if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
1.1 root 677: int fd;
678: ssize_t oldpath_size, write_size;
679: fd = open(rpath(fs_ctx, newpath, buffer), O_CREAT|O_EXCL|O_RDWR,
680: SM_LOCAL_MODE_BITS);
681: if (fd == -1) {
1.1.1.2 root 682: err = fd;
683: goto out;
1.1 root 684: }
685: /* Write the oldpath (target) to the file. */
686: oldpath_size = strlen(oldpath);
687: do {
688: write_size = write(fd, (void *)oldpath, oldpath_size);
689: } while (write_size == -1 && errno == EINTR);
690:
691: if (write_size != oldpath_size) {
692: serrno = errno;
693: close(fd);
694: err = -1;
695: goto err_end;
696: }
697: close(fd);
698: /* Set cleint credentials in symlink's xattr */
699: credp->fc_mode = credp->fc_mode|S_IFLNK;
700: err = local_set_xattr(rpath(fs_ctx, newpath, buffer), credp);
701: if (err == -1) {
702: serrno = errno;
703: goto err_end;
704: }
1.1.1.3 ! root 705: } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
! 706: int fd;
! 707: ssize_t oldpath_size, write_size;
! 708: fd = open(rpath(fs_ctx, newpath, buffer), O_CREAT|O_EXCL|O_RDWR,
! 709: SM_LOCAL_MODE_BITS);
! 710: if (fd == -1) {
! 711: err = fd;
! 712: goto out;
! 713: }
! 714: /* Write the oldpath (target) to the file. */
! 715: oldpath_size = strlen(oldpath);
! 716: do {
! 717: write_size = write(fd, (void *)oldpath, oldpath_size);
! 718: } while (write_size == -1 && errno == EINTR);
! 719:
! 720: if (write_size != oldpath_size) {
! 721: serrno = errno;
! 722: close(fd);
! 723: err = -1;
! 724: goto err_end;
! 725: }
! 726: close(fd);
! 727: /* Set cleint credentials in symlink's xattr */
! 728: credp->fc_mode = credp->fc_mode|S_IFLNK;
! 729: err = local_set_mapped_file_attr(fs_ctx, newpath, credp);
! 730: if (err == -1) {
! 731: serrno = errno;
! 732: goto err_end;
! 733: }
1.1.1.2 root 734: } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
735: (fs_ctx->export_flags & V9FS_SM_NONE)) {
1.1 root 736: err = symlink(oldpath, rpath(fs_ctx, newpath, buffer));
737: if (err) {
1.1.1.2 root 738: goto out;
1.1 root 739: }
740: err = lchown(rpath(fs_ctx, newpath, buffer), credp->fc_uid,
1.1.1.2 root 741: credp->fc_gid);
1.1 root 742: if (err == -1) {
743: /*
744: * If we fail to change ownership and if we are
745: * using security model none. Ignore the error
746: */
1.1.1.2 root 747: if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
1.1 root 748: serrno = errno;
749: goto err_end;
750: } else
751: err = 0;
752: }
753: }
1.1.1.2 root 754: goto out;
1.1 root 755:
756: err_end:
757: remove(rpath(fs_ctx, newpath, buffer));
758: errno = serrno;
1.1.1.2 root 759: out:
760: v9fs_string_free(&fullname);
1.1 root 761: return err;
762: }
763:
1.1.1.2 root 764: static int local_link(FsContext *ctx, V9fsPath *oldpath,
765: V9fsPath *dirpath, const char *name)
1.1 root 766: {
1.1.1.2 root 767: int ret;
768: V9fsString newpath;
1.1 root 769: char buffer[PATH_MAX], buffer1[PATH_MAX];
770:
1.1.1.2 root 771: v9fs_string_init(&newpath);
772: v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
773:
774: ret = link(rpath(ctx, oldpath->data, buffer),
775: rpath(ctx, newpath.data, buffer1));
1.1.1.3 ! root 776:
! 777: /* now link the virtfs_metadata files */
! 778: if (!ret && (ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
! 779: /* Link the .virtfs_metadata files. Create the metada directory */
! 780: ret = local_create_mapped_attr_dir(ctx, newpath.data);
! 781: if (ret < 0) {
! 782: goto err_out;
! 783: }
! 784: ret = link(local_mapped_attr_path(ctx, oldpath->data, buffer),
! 785: local_mapped_attr_path(ctx, newpath.data, buffer1));
! 786: if (ret < 0 && errno != ENOENT) {
! 787: goto err_out;
! 788: }
! 789: }
! 790: err_out:
1.1.1.2 root 791: v9fs_string_free(&newpath);
792: return ret;
1.1 root 793: }
794:
1.1.1.2 root 795: static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
1.1 root 796: {
797: char buffer[PATH_MAX];
1.1.1.2 root 798: char *path = fs_path->data;
799:
1.1 root 800: return truncate(rpath(ctx, path, buffer), size);
801: }
802:
803: static int local_rename(FsContext *ctx, const char *oldpath,
804: const char *newpath)
805: {
1.1.1.3 ! root 806: int err;
1.1 root 807: char buffer[PATH_MAX], buffer1[PATH_MAX];
808:
1.1.1.3 ! root 809: if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
! 810: err = local_create_mapped_attr_dir(ctx, newpath);
! 811: if (err < 0) {
! 812: return err;
! 813: }
! 814: /* rename the .virtfs_metadata files */
! 815: err = rename(local_mapped_attr_path(ctx, oldpath, buffer),
! 816: local_mapped_attr_path(ctx, newpath, buffer1));
! 817: if (err < 0 && errno != ENOENT) {
! 818: return err;
! 819: }
! 820: }
1.1 root 821: return rename(rpath(ctx, oldpath, buffer), rpath(ctx, newpath, buffer1));
822: }
823:
1.1.1.2 root 824: static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
1.1 root 825: {
826: char buffer[PATH_MAX];
1.1.1.2 root 827: char *path = fs_path->data;
828:
1.1 root 829: if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
1.1.1.2 root 830: (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
831: (fs_ctx->export_flags & V9FS_SM_NONE)) {
832: return lchown(rpath(fs_ctx, path, buffer),
833: credp->fc_uid, credp->fc_gid);
834: } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
1.1 root 835: return local_set_xattr(rpath(fs_ctx, path, buffer), credp);
1.1.1.3 ! root 836: } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
! 837: return local_set_mapped_file_attr(fs_ctx, path, credp);
1.1 root 838: }
839: return -1;
840: }
841:
1.1.1.2 root 842: static int local_utimensat(FsContext *s, V9fsPath *fs_path,
1.1 root 843: const struct timespec *buf)
844: {
845: char buffer[PATH_MAX];
1.1.1.2 root 846: char *path = fs_path->data;
847:
848: return qemu_utimens(rpath(s, path, buffer), buf);
1.1 root 849: }
850:
851: static int local_remove(FsContext *ctx, const char *path)
852: {
1.1.1.3 ! root 853: int err;
! 854: struct stat stbuf;
1.1 root 855: char buffer[PATH_MAX];
1.1.1.3 ! root 856:
! 857: if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
! 858: err = lstat(rpath(ctx, path, buffer), &stbuf);
! 859: if (err) {
! 860: goto err_out;
! 861: }
! 862: /*
! 863: * If directory remove .virtfs_metadata contained in the
! 864: * directory
! 865: */
! 866: if (S_ISDIR(stbuf.st_mode)) {
! 867: sprintf(buffer, "%s/%s/%s", ctx->fs_root, path, VIRTFS_META_DIR);
! 868: err = remove(buffer);
! 869: if (err < 0 && errno != ENOENT) {
! 870: /*
! 871: * We didn't had the .virtfs_metadata file. May be file created
! 872: * in non-mapped mode ?. Ignore ENOENT.
! 873: */
! 874: goto err_out;
! 875: }
! 876: }
! 877: /*
! 878: * Now remove the name from parent directory
! 879: * .virtfs_metadata directory
! 880: */
! 881: err = remove(local_mapped_attr_path(ctx, path, buffer));;
! 882: if (err < 0 && errno != ENOENT) {
! 883: /*
! 884: * We didn't had the .virtfs_metadata file. May be file created
! 885: * in non-mapped mode ?. Ignore ENOENT.
! 886: */
! 887: goto err_out;
! 888: }
! 889: }
1.1 root 890: return remove(rpath(ctx, path, buffer));
1.1.1.3 ! root 891: err_out:
! 892: return err;
1.1 root 893: }
894:
1.1.1.2 root 895: static int local_fsync(FsContext *ctx, int fid_type,
896: V9fsFidOpenState *fs, int datasync)
1.1 root 897: {
1.1.1.2 root 898: int fd;
899:
900: if (fid_type == P9_FID_DIR) {
901: fd = dirfd(fs->dir);
902: } else {
903: fd = fs->fd;
904: }
905:
1.1 root 906: if (datasync) {
907: return qemu_fdatasync(fd);
908: } else {
909: return fsync(fd);
910: }
911: }
912:
1.1.1.2 root 913: static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
1.1 root 914: {
915: char buffer[PATH_MAX];
1.1.1.2 root 916: char *path = fs_path->data;
917:
918: return statfs(rpath(s, path, buffer), stbuf);
1.1 root 919: }
920:
1.1.1.2 root 921: static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
1.1 root 922: const char *name, void *value, size_t size)
923: {
1.1.1.2 root 924: char *path = fs_path->data;
925:
1.1 root 926: return v9fs_get_xattr(ctx, path, name, value, size);
927: }
928:
1.1.1.2 root 929: static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
1.1 root 930: void *value, size_t size)
931: {
1.1.1.2 root 932: char *path = fs_path->data;
933:
1.1 root 934: return v9fs_list_xattr(ctx, path, value, size);
935: }
936:
1.1.1.2 root 937: static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
1.1 root 938: void *value, size_t size, int flags)
939: {
1.1.1.2 root 940: char *path = fs_path->data;
941:
1.1 root 942: return v9fs_set_xattr(ctx, path, name, value, size, flags);
943: }
944:
1.1.1.2 root 945: static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
946: const char *name)
1.1 root 947: {
1.1.1.2 root 948: char *path = fs_path->data;
949:
1.1 root 950: return v9fs_remove_xattr(ctx, path, name);
951: }
952:
1.1.1.2 root 953: static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
954: const char *name, V9fsPath *target)
955: {
956: if (dir_path) {
957: v9fs_string_sprintf((V9fsString *)target, "%s/%s",
958: dir_path->data, name);
959: } else {
960: v9fs_string_sprintf((V9fsString *)target, "%s", name);
961: }
962: /* Bump the size for including terminating NULL */
963: target->size++;
964: return 0;
965: }
966:
967: static int local_renameat(FsContext *ctx, V9fsPath *olddir,
968: const char *old_name, V9fsPath *newdir,
969: const char *new_name)
970: {
971: int ret;
972: V9fsString old_full_name, new_full_name;
973:
974: v9fs_string_init(&old_full_name);
975: v9fs_string_init(&new_full_name);
976:
977: v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
978: v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
979:
980: ret = local_rename(ctx, old_full_name.data, new_full_name.data);
981: v9fs_string_free(&old_full_name);
982: v9fs_string_free(&new_full_name);
983: return ret;
984: }
985:
986: static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
987: const char *name, int flags)
988: {
989: int ret;
990: V9fsString fullname;
991: char buffer[PATH_MAX];
1.1.1.3 ! root 992:
1.1.1.2 root 993: v9fs_string_init(&fullname);
994:
995: v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
1.1.1.3 ! root 996: if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
! 997: if (flags == AT_REMOVEDIR) {
! 998: /*
! 999: * If directory remove .virtfs_metadata contained in the
! 1000: * directory
! 1001: */
! 1002: sprintf(buffer, "%s/%s/%s", ctx->fs_root,
! 1003: fullname.data, VIRTFS_META_DIR);
! 1004: ret = remove(buffer);
! 1005: if (ret < 0 && errno != ENOENT) {
! 1006: /*
! 1007: * We didn't had the .virtfs_metadata file. May be file created
! 1008: * in non-mapped mode ?. Ignore ENOENT.
! 1009: */
! 1010: goto err_out;
! 1011: }
! 1012: }
! 1013: /*
! 1014: * Now remove the name from parent directory
! 1015: * .virtfs_metadata directory.
! 1016: */
! 1017: ret = remove(local_mapped_attr_path(ctx, fullname.data, buffer));
! 1018: if (ret < 0 && errno != ENOENT) {
! 1019: /*
! 1020: * We didn't had the .virtfs_metadata file. May be file created
! 1021: * in non-mapped mode ?. Ignore ENOENT.
! 1022: */
! 1023: goto err_out;
! 1024: }
! 1025: }
! 1026: /* Remove the name finally */
1.1.1.2 root 1027: ret = remove(rpath(ctx, fullname.data, buffer));
1028: v9fs_string_free(&fullname);
1029:
1.1.1.3 ! root 1030: err_out:
1.1.1.2 root 1031: return ret;
1032: }
1033:
1034: static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
1035: mode_t st_mode, uint64_t *st_gen)
1036: {
1037: int err;
1038: #ifdef FS_IOC_GETVERSION
1039: V9fsFidOpenState fid_open;
1040:
1041: /*
1042: * Do not try to open special files like device nodes, fifos etc
1043: * We can get fd for regular files and directories only
1044: */
1045: if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
1046: return 0;
1047: }
1048: err = local_open(ctx, path, O_RDONLY, &fid_open);
1049: if (err < 0) {
1050: return err;
1051: }
1052: err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
1053: local_close(ctx, &fid_open);
1054: #else
1055: err = -ENOTTY;
1056: #endif
1057: return err;
1058: }
1059:
1060: static int local_init(FsContext *ctx)
1061: {
1062: int err = 0;
1063: struct statfs stbuf;
1064:
1.1.1.3 ! root 1065: if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
! 1066: ctx->xops = passthrough_xattr_ops;
! 1067: } else if (ctx->export_flags & V9FS_SM_MAPPED) {
! 1068: ctx->xops = mapped_xattr_ops;
! 1069: } else if (ctx->export_flags & V9FS_SM_NONE) {
! 1070: ctx->xops = none_xattr_ops;
! 1071: } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
! 1072: /*
! 1073: * xattr operation for mapped-file and passthrough
! 1074: * remain same.
! 1075: */
! 1076: ctx->xops = passthrough_xattr_ops;
! 1077: }
1.1.1.2 root 1078: ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1079: #ifdef FS_IOC_GETVERSION
1080: /*
1081: * use ioc_getversion only if the iocl is definied
1082: */
1083: err = statfs(ctx->fs_root, &stbuf);
1084: if (!err) {
1085: switch (stbuf.f_type) {
1086: case EXT2_SUPER_MAGIC:
1087: case BTRFS_SUPER_MAGIC:
1088: case REISERFS_SUPER_MAGIC:
1089: case XFS_SUPER_MAGIC:
1090: ctx->exops.get_st_gen = local_ioc_getversion;
1091: break;
1092: }
1093: }
1094: #endif
1095: return err;
1096: }
1.1 root 1097:
1.1.1.3 ! root 1098: static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
! 1099: {
! 1100: const char *sec_model = qemu_opt_get(opts, "security_model");
! 1101: const char *path = qemu_opt_get(opts, "path");
! 1102:
! 1103: if (!sec_model) {
! 1104: fprintf(stderr, "security model not specified, "
! 1105: "local fs needs security model\nvalid options are:"
! 1106: "\tsecurity_model=[passthrough|mapped|none]\n");
! 1107: return -1;
! 1108: }
! 1109:
! 1110: if (!strcmp(sec_model, "passthrough")) {
! 1111: fse->export_flags |= V9FS_SM_PASSTHROUGH;
! 1112: } else if (!strcmp(sec_model, "mapped") ||
! 1113: !strcmp(sec_model, "mapped-xattr")) {
! 1114: fse->export_flags |= V9FS_SM_MAPPED;
! 1115: } else if (!strcmp(sec_model, "none")) {
! 1116: fse->export_flags |= V9FS_SM_NONE;
! 1117: } else if (!strcmp(sec_model, "mapped-file")) {
! 1118: fse->export_flags |= V9FS_SM_MAPPED_FILE;
! 1119: } else {
! 1120: fprintf(stderr, "Invalid security model %s specified, valid options are"
! 1121: "\n\t [passthrough|mapped-xattr|mapped-file|none]\n",
! 1122: sec_model);
! 1123: return -1;
! 1124: }
! 1125:
! 1126: if (!path) {
! 1127: fprintf(stderr, "fsdev: No path specified.\n");
! 1128: return -1;
! 1129: }
! 1130: fse->path = g_strdup(path);
! 1131:
! 1132: return 0;
! 1133: }
! 1134:
1.1 root 1135: FileOperations local_ops = {
1.1.1.3 ! root 1136: .parse_opts = local_parse_opts,
1.1.1.2 root 1137: .init = local_init,
1.1 root 1138: .lstat = local_lstat,
1139: .readlink = local_readlink,
1140: .close = local_close,
1141: .closedir = local_closedir,
1142: .open = local_open,
1143: .opendir = local_opendir,
1144: .rewinddir = local_rewinddir,
1145: .telldir = local_telldir,
1.1.1.2 root 1146: .readdir_r = local_readdir_r,
1.1 root 1147: .seekdir = local_seekdir,
1148: .preadv = local_preadv,
1149: .pwritev = local_pwritev,
1150: .chmod = local_chmod,
1151: .mknod = local_mknod,
1152: .mkdir = local_mkdir,
1153: .fstat = local_fstat,
1154: .open2 = local_open2,
1155: .symlink = local_symlink,
1156: .link = local_link,
1157: .truncate = local_truncate,
1158: .rename = local_rename,
1159: .chown = local_chown,
1160: .utimensat = local_utimensat,
1161: .remove = local_remove,
1162: .fsync = local_fsync,
1163: .statfs = local_statfs,
1164: .lgetxattr = local_lgetxattr,
1165: .llistxattr = local_llistxattr,
1166: .lsetxattr = local_lsetxattr,
1167: .lremovexattr = local_lremovexattr,
1.1.1.2 root 1168: .name_to_path = local_name_to_path,
1169: .renameat = local_renameat,
1170: .unlinkat = local_unlinkat,
1.1 root 1171: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.