Statistics
| Branch: | Revision:

root / hw / 9pfs / virtio-9p-local.c @ 2c30dd74

History | View | Annotate | Download (32.5 kB)

1
/*
2
 * Virtio 9p Posix callback
3
 *
4
 * Copyright IBM, Corp. 2010
5
 *
6
 * Authors:
7
 *  Anthony Liguori   <aliguori@us.ibm.com>
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>
22
#include "qemu-xattr.h"
23
#include <libgen.h>
24
#include <linux/fs.h>
25
#ifdef CONFIG_LINUX_MAGIC_H
26
#include <linux/magic.h>
27
#endif
28
#include <sys/ioctl.h>
29

    
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
42

    
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

    
91
static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
92
{
93
    int err;
94
    char buffer[PATH_MAX];
95
    char *path = fs_path->data;
96

    
97
    err =  lstat(rpath(fs_ctx, path, buffer), stbuf);
98
    if (err) {
99
        return err;
100
    }
101
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
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
        }
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;
141
    }
142
    free(tmp_path);
143
    return err;
144
}
145

    
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

    
220
static int local_set_xattr(const char *path, FsCred *credp)
221
{
222
    int err;
223

    
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,
256
                                         FsCred *credp)
257
{
258
    char buffer[PATH_MAX];
259

    
260
    if (chmod(rpath(fs_ctx, path, buffer), credp->fc_mode & 07777) < 0) {
261
        return -1;
262
    }
263
    if (lchown(rpath(fs_ctx, path, buffer), credp->fc_uid,
264
                credp->fc_gid) < 0) {
265
        /*
266
         * If we fail to change ownership and if we are
267
         * using security model none. Ignore the error
268
         */
269
        if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
270
            return -1;
271
        }
272
    }
273
    return 0;
274
}
275

    
276
static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
277
                              char *buf, size_t bufsz)
278
{
279
    ssize_t tsize = -1;
280
    char buffer[PATH_MAX];
281
    char *path = fs_path->data;
282

    
283
    if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
284
        (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
285
        int fd;
286
        fd = open(rpath(fs_ctx, path, buffer), O_RDONLY);
287
        if (fd == -1) {
288
            return -1;
289
        }
290
        do {
291
            tsize = read(fd, (void *)buf, bufsz);
292
        } while (tsize == -1 && errno == EINTR);
293
        close(fd);
294
        return tsize;
295
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
296
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
297
        tsize = readlink(rpath(fs_ctx, path, buffer), buf, bufsz);
298
    }
299
    return tsize;
300
}
301

    
302
static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
303
{
304
    return close(fs->fd);
305
}
306

    
307
static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
308
{
309
    return closedir(fs->dir);
310
}
311

    
312
static int local_open(FsContext *ctx, V9fsPath *fs_path,
313
                      int flags, V9fsFidOpenState *fs)
314
{
315
    char buffer[PATH_MAX];
316
    char *path = fs_path->data;
317

    
318
    fs->fd = open(rpath(ctx, path, buffer), flags);
319
    return fs->fd;
320
}
321

    
322
static int local_opendir(FsContext *ctx,
323
                         V9fsPath *fs_path, V9fsFidOpenState *fs)
324
{
325
    char buffer[PATH_MAX];
326
    char *path = fs_path->data;
327

    
328
    fs->dir = opendir(rpath(ctx, path, buffer));
329
    if (!fs->dir) {
330
        return -1;
331
    }
332
    return 0;
333
}
334

    
335
static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
336
{
337
    return rewinddir(fs->dir);
338
}
339

    
340
static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
341
{
342
    return telldir(fs->dir);
343
}
344

    
345
static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
346
                           struct dirent *entry,
347
                           struct dirent **result)
348
{
349
    int ret;
350

    
351
again:
352
    ret = readdir_r(fs->dir, entry, result);
353
    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
354
        if (!ret && *result != NULL &&
355
            !strcmp(entry->d_name, VIRTFS_META_DIR)) {
356
            /* skp the meta data directory */
357
            goto again;
358
        }
359
    }
360
    return ret;
361
}
362

    
363
static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
364
{
365
    return seekdir(fs->dir, off);
366
}
367

    
368
static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
369
                            const struct iovec *iov,
370
                            int iovcnt, off_t offset)
371
{
372
#ifdef CONFIG_PREADV
373
    return preadv(fs->fd, iov, iovcnt, offset);
374
#else
375
    int err = lseek(fs->fd, offset, SEEK_SET);
376
    if (err == -1) {
377
        return err;
378
    } else {
379
        return readv(fs->fd, iov, iovcnt);
380
    }
381
#endif
382
}
383

    
384
static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
385
                             const struct iovec *iov,
386
                             int iovcnt, off_t offset)
387
{
388
    ssize_t ret
389
;
390
#ifdef CONFIG_PREADV
391
    ret = pwritev(fs->fd, iov, iovcnt, offset);
392
#else
393
    int err = lseek(fs->fd, offset, SEEK_SET);
394
    if (err == -1) {
395
        return err;
396
    } else {
397
        ret = writev(fs->fd, iov, iovcnt);
398
    }
399
#endif
400
#ifdef CONFIG_SYNC_FILE_RANGE
401
    if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
402
        /*
403
         * Initiate a writeback. This is not a data integrity sync.
404
         * We want to ensure that we don't leave dirty pages in the cache
405
         * after write when writeout=immediate is sepcified.
406
         */
407
        sync_file_range(fs->fd, offset, ret,
408
                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
409
    }
410
#endif
411
    return ret;
412
}
413

    
414
static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
415
{
416
    char buffer[PATH_MAX];
417
    char *path = fs_path->data;
418

    
419
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
420
        return local_set_xattr(rpath(fs_ctx, path, buffer), credp);
421
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
422
        return local_set_mapped_file_attr(fs_ctx, path, credp);
423
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
424
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
425
        return chmod(rpath(fs_ctx, path, buffer), credp->fc_mode);
426
    }
427
    return -1;
428
}
429

    
430
static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
431
                       const char *name, FsCred *credp)
432
{
433
    char *path;
434
    int err = -1;
435
    int serrno = 0;
436
    V9fsString fullname;
437
    char buffer[PATH_MAX];
438

    
439
    v9fs_string_init(&fullname);
440
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
441
    path = fullname.data;
442

    
443
    /* Determine the security model */
444
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
445
        err = mknod(rpath(fs_ctx, path, buffer),
446
                SM_LOCAL_MODE_BITS|S_IFREG, 0);
447
        if (err == -1) {
448
            goto out;
449
        }
450
        err = local_set_xattr(rpath(fs_ctx, path, buffer), credp);
451
        if (err == -1) {
452
            serrno = errno;
453
            goto err_end;
454
        }
455
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
456

    
457
        err = mknod(rpath(fs_ctx, path, buffer),
458
                    SM_LOCAL_MODE_BITS|S_IFREG, 0);
459
        if (err == -1) {
460
            goto out;
461
        }
462
        err = local_set_mapped_file_attr(fs_ctx, path, credp);
463
        if (err == -1) {
464
            serrno = errno;
465
            goto err_end;
466
        }
467
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
468
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
469
        err = mknod(rpath(fs_ctx, path, buffer), credp->fc_mode,
470
                credp->fc_rdev);
471
        if (err == -1) {
472
            goto out;
473
        }
474
        err = local_post_create_passthrough(fs_ctx, path, credp);
475
        if (err == -1) {
476
            serrno = errno;
477
            goto err_end;
478
        }
479
    }
480
    goto out;
481

    
482
err_end:
483
    remove(rpath(fs_ctx, path, buffer));
484
    errno = serrno;
485
out:
486
    v9fs_string_free(&fullname);
487
    return err;
488
}
489

    
490
static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
491
                       const char *name, FsCred *credp)
492
{
493
    char *path;
494
    int err = -1;
495
    int serrno = 0;
496
    V9fsString fullname;
497
    char buffer[PATH_MAX];
498

    
499
    v9fs_string_init(&fullname);
500
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
501
    path = fullname.data;
502

    
503
    /* Determine the security model */
504
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
505
        err = mkdir(rpath(fs_ctx, path, buffer), SM_LOCAL_DIR_MODE_BITS);
506
        if (err == -1) {
507
            goto out;
508
        }
509
        credp->fc_mode = credp->fc_mode|S_IFDIR;
510
        err = local_set_xattr(rpath(fs_ctx, path, buffer), credp);
511
        if (err == -1) {
512
            serrno = errno;
513
            goto err_end;
514
        }
515
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
516
        err = mkdir(rpath(fs_ctx, path, buffer), SM_LOCAL_DIR_MODE_BITS);
517
        if (err == -1) {
518
            goto out;
519
        }
520
        credp->fc_mode = credp->fc_mode|S_IFDIR;
521
        err = local_set_mapped_file_attr(fs_ctx, path, credp);
522
        if (err == -1) {
523
            serrno = errno;
524
            goto err_end;
525
        }
526
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
527
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
528
        err = mkdir(rpath(fs_ctx, path, buffer), credp->fc_mode);
529
        if (err == -1) {
530
            goto out;
531
        }
532
        err = local_post_create_passthrough(fs_ctx, path, credp);
533
        if (err == -1) {
534
            serrno = errno;
535
            goto err_end;
536
        }
537
    }
538
    goto out;
539

    
540
err_end:
541
    remove(rpath(fs_ctx, path, buffer));
542
    errno = serrno;
543
out:
544
    v9fs_string_free(&fullname);
545
    return err;
546
}
547

    
548
static int local_fstat(FsContext *fs_ctx, int fid_type,
549
                       V9fsFidOpenState *fs, struct stat *stbuf)
550
{
551
    int err, fd;
552

    
553
    if (fid_type == P9_FID_DIR) {
554
        fd = dirfd(fs->dir);
555
    } else {
556
        fd = fs->fd;
557
    }
558

    
559
    err = fstat(fd, stbuf);
560
    if (err) {
561
        return err;
562
    }
563
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
564
        /* Actual credentials are part of extended attrs */
565
        uid_t tmp_uid;
566
        gid_t tmp_gid;
567
        mode_t tmp_mode;
568
        dev_t tmp_dev;
569

    
570
        if (fgetxattr(fd, "user.virtfs.uid",
571
                      &tmp_uid, sizeof(uid_t)) > 0) {
572
            stbuf->st_uid = tmp_uid;
573
        }
574
        if (fgetxattr(fd, "user.virtfs.gid",
575
                      &tmp_gid, sizeof(gid_t)) > 0) {
576
            stbuf->st_gid = tmp_gid;
577
        }
578
        if (fgetxattr(fd, "user.virtfs.mode",
579
                      &tmp_mode, sizeof(mode_t)) > 0) {
580
            stbuf->st_mode = tmp_mode;
581
        }
582
        if (fgetxattr(fd, "user.virtfs.rdev",
583
                      &tmp_dev, sizeof(dev_t)) > 0) {
584
                stbuf->st_rdev = tmp_dev;
585
        }
586
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
587
        errno = EOPNOTSUPP;
588
        return -1;
589
    }
590
    return err;
591
}
592

    
593
static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
594
                       int flags, FsCred *credp, V9fsFidOpenState *fs)
595
{
596
    char *path;
597
    int fd = -1;
598
    int err = -1;
599
    int serrno = 0;
600
    V9fsString fullname;
601
    char buffer[PATH_MAX];
602

    
603
    v9fs_string_init(&fullname);
604
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
605
    path = fullname.data;
606

    
607
    /* Determine the security model */
608
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
609
        fd = open(rpath(fs_ctx, path, buffer), flags, SM_LOCAL_MODE_BITS);
610
        if (fd == -1) {
611
            err = fd;
612
            goto out;
613
        }
614
        credp->fc_mode = credp->fc_mode|S_IFREG;
615
        /* Set cleint credentials in xattr */
616
        err = local_set_xattr(rpath(fs_ctx, path, buffer), credp);
617
        if (err == -1) {
618
            serrno = errno;
619
            goto err_end;
620
        }
621
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
622
        fd = open(rpath(fs_ctx, path, buffer), flags, SM_LOCAL_MODE_BITS);
623
        if (fd == -1) {
624
            err = fd;
625
            goto out;
626
        }
627
        credp->fc_mode = credp->fc_mode|S_IFREG;
628
        /* Set client credentials in .virtfs_metadata directory files */
629
        err = local_set_mapped_file_attr(fs_ctx, path, credp);
630
        if (err == -1) {
631
            serrno = errno;
632
            goto err_end;
633
        }
634
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
635
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
636
        fd = open(rpath(fs_ctx, path, buffer), flags, credp->fc_mode);
637
        if (fd == -1) {
638
            err = fd;
639
            goto out;
640
        }
641
        err = local_post_create_passthrough(fs_ctx, path, credp);
642
        if (err == -1) {
643
            serrno = errno;
644
            goto err_end;
645
        }
646
    }
647
    err = fd;
648
    fs->fd = fd;
649
    goto out;
650

    
651
err_end:
652
    close(fd);
653
    remove(rpath(fs_ctx, path, buffer));
654
    errno = serrno;
655
out:
656
    v9fs_string_free(&fullname);
657
    return err;
658
}
659

    
660

    
661
static int local_symlink(FsContext *fs_ctx, const char *oldpath,
662
                         V9fsPath *dir_path, const char *name, FsCred *credp)
663
{
664
    int err = -1;
665
    int serrno = 0;
666
    char *newpath;
667
    V9fsString fullname;
668
    char buffer[PATH_MAX];
669

    
670
    v9fs_string_init(&fullname);
671
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
672
    newpath = fullname.data;
673

    
674
    /* Determine the security model */
675
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
676
        int fd;
677
        ssize_t oldpath_size, write_size;
678
        fd = open(rpath(fs_ctx, newpath, buffer), O_CREAT|O_EXCL|O_RDWR,
679
                SM_LOCAL_MODE_BITS);
680
        if (fd == -1) {
681
            err = fd;
682
            goto out;
683
        }
684
        /* Write the oldpath (target) to the file. */
685
        oldpath_size = strlen(oldpath);
686
        do {
687
            write_size = write(fd, (void *)oldpath, oldpath_size);
688
        } while (write_size == -1 && errno == EINTR);
689

    
690
        if (write_size != oldpath_size) {
691
            serrno = errno;
692
            close(fd);
693
            err = -1;
694
            goto err_end;
695
        }
696
        close(fd);
697
        /* Set cleint credentials in symlink's xattr */
698
        credp->fc_mode = credp->fc_mode|S_IFLNK;
699
        err = local_set_xattr(rpath(fs_ctx, newpath, buffer), credp);
700
        if (err == -1) {
701
            serrno = errno;
702
            goto err_end;
703
        }
704
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
705
        int fd;
706
        ssize_t oldpath_size, write_size;
707
        fd = open(rpath(fs_ctx, newpath, buffer), O_CREAT|O_EXCL|O_RDWR,
708
                  SM_LOCAL_MODE_BITS);
709
        if (fd == -1) {
710
            err = fd;
711
            goto out;
712
        }
713
        /* Write the oldpath (target) to the file. */
714
        oldpath_size = strlen(oldpath);
715
        do {
716
            write_size = write(fd, (void *)oldpath, oldpath_size);
717
        } while (write_size == -1 && errno == EINTR);
718

    
719
        if (write_size != oldpath_size) {
720
            serrno = errno;
721
            close(fd);
722
            err = -1;
723
            goto err_end;
724
        }
725
        close(fd);
726
        /* Set cleint credentials in symlink's xattr */
727
        credp->fc_mode = credp->fc_mode|S_IFLNK;
728
        err = local_set_mapped_file_attr(fs_ctx, newpath, credp);
729
        if (err == -1) {
730
            serrno = errno;
731
            goto err_end;
732
        }
733
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
734
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
735
        err = symlink(oldpath, rpath(fs_ctx, newpath, buffer));
736
        if (err) {
737
            goto out;
738
        }
739
        err = lchown(rpath(fs_ctx, newpath, buffer), credp->fc_uid,
740
                     credp->fc_gid);
741
        if (err == -1) {
742
            /*
743
             * If we fail to change ownership and if we are
744
             * using security model none. Ignore the error
745
             */
746
            if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
747
                serrno = errno;
748
                goto err_end;
749
            } else
750
                err = 0;
751
        }
752
    }
753
    goto out;
754

    
755
err_end:
756
    remove(rpath(fs_ctx, newpath, buffer));
757
    errno = serrno;
758
out:
759
    v9fs_string_free(&fullname);
760
    return err;
761
}
762

    
763
static int local_link(FsContext *ctx, V9fsPath *oldpath,
764
                      V9fsPath *dirpath, const char *name)
765
{
766
    int ret;
767
    V9fsString newpath;
768
    char buffer[PATH_MAX], buffer1[PATH_MAX];
769

    
770
    v9fs_string_init(&newpath);
771
    v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
772

    
773
    ret = link(rpath(ctx, oldpath->data, buffer),
774
               rpath(ctx, newpath.data, buffer1));
775

    
776
    /* now link the virtfs_metadata files */
777
    if (!ret && (ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
778
        /* Link the .virtfs_metadata files. Create the metada directory */
779
        ret = local_create_mapped_attr_dir(ctx, newpath.data);
780
        if (ret < 0) {
781
            goto err_out;
782
        }
783
        ret = link(local_mapped_attr_path(ctx, oldpath->data, buffer),
784
                   local_mapped_attr_path(ctx, newpath.data, buffer1));
785
        if (ret < 0 && errno != ENOENT) {
786
            goto err_out;
787
        }
788
    }
789
err_out:
790
    v9fs_string_free(&newpath);
791
    return ret;
792
}
793

    
794
static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
795
{
796
    char buffer[PATH_MAX];
797
    char *path = fs_path->data;
798

    
799
    return truncate(rpath(ctx, path, buffer), size);
800
}
801

    
802
static int local_rename(FsContext *ctx, const char *oldpath,
803
                        const char *newpath)
804
{
805
    int err;
806
    char buffer[PATH_MAX], buffer1[PATH_MAX];
807

    
808
    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
809
        err = local_create_mapped_attr_dir(ctx, newpath);
810
        if (err < 0) {
811
            return err;
812
        }
813
        /* rename the .virtfs_metadata files */
814
        err = rename(local_mapped_attr_path(ctx, oldpath, buffer),
815
                     local_mapped_attr_path(ctx, newpath, buffer1));
816
        if (err < 0 && errno != ENOENT) {
817
            return err;
818
        }
819
    }
820
    return rename(rpath(ctx, oldpath, buffer), rpath(ctx, newpath, buffer1));
821
}
822

    
823
static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
824
{
825
    char buffer[PATH_MAX];
826
    char *path = fs_path->data;
827

    
828
    if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
829
        (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
830
        (fs_ctx->export_flags & V9FS_SM_NONE)) {
831
        return lchown(rpath(fs_ctx, path, buffer),
832
                      credp->fc_uid, credp->fc_gid);
833
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
834
        return local_set_xattr(rpath(fs_ctx, path, buffer), credp);
835
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
836
        return local_set_mapped_file_attr(fs_ctx, path, credp);
837
    }
838
    return -1;
839
}
840

    
841
static int local_utimensat(FsContext *s, V9fsPath *fs_path,
842
                           const struct timespec *buf)
843
{
844
    char buffer[PATH_MAX];
845
    char *path = fs_path->data;
846

    
847
    return qemu_utimens(rpath(s, path, buffer), buf);
848
}
849

    
850
static int local_remove(FsContext *ctx, const char *path)
851
{
852
    int err;
853
    struct stat stbuf;
854
    char buffer[PATH_MAX];
855

    
856
    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
857
        err =  lstat(rpath(ctx, path, buffer), &stbuf);
858
        if (err) {
859
            goto err_out;
860
        }
861
        /*
862
         * If directory remove .virtfs_metadata contained in the
863
         * directory
864
         */
865
        if (S_ISDIR(stbuf.st_mode)) {
866
            sprintf(buffer, "%s/%s/%s", ctx->fs_root, path, VIRTFS_META_DIR);
867
            err = remove(buffer);
868
            if (err < 0 && errno != ENOENT) {
869
                /*
870
                 * We didn't had the .virtfs_metadata file. May be file created
871
                 * in non-mapped mode ?. Ignore ENOENT.
872
                 */
873
                goto err_out;
874
            }
875
        }
876
        /*
877
         * Now remove the name from parent directory
878
         * .virtfs_metadata directory
879
         */
880
        err = remove(local_mapped_attr_path(ctx, path, buffer));;
881
        if (err < 0 && errno != ENOENT) {
882
            /*
883
             * We didn't had the .virtfs_metadata file. May be file created
884
             * in non-mapped mode ?. Ignore ENOENT.
885
             */
886
            goto err_out;
887
        }
888
    }
889
    return remove(rpath(ctx, path, buffer));
890
err_out:
891
    return err;
892
}
893

    
894
static int local_fsync(FsContext *ctx, int fid_type,
895
                       V9fsFidOpenState *fs, int datasync)
896
{
897
    int fd;
898

    
899
    if (fid_type == P9_FID_DIR) {
900
        fd = dirfd(fs->dir);
901
    } else {
902
        fd = fs->fd;
903
    }
904

    
905
    if (datasync) {
906
        return qemu_fdatasync(fd);
907
    } else {
908
        return fsync(fd);
909
    }
910
}
911

    
912
static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
913
{
914
    char buffer[PATH_MAX];
915
    char *path = fs_path->data;
916

    
917
    return statfs(rpath(s, path, buffer), stbuf);
918
}
919

    
920
static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
921
                               const char *name, void *value, size_t size)
922
{
923
    char *path = fs_path->data;
924

    
925
    return v9fs_get_xattr(ctx, path, name, value, size);
926
}
927

    
928
static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
929
                                void *value, size_t size)
930
{
931
    char *path = fs_path->data;
932

    
933
    return v9fs_list_xattr(ctx, path, value, size);
934
}
935

    
936
static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
937
                           void *value, size_t size, int flags)
938
{
939
    char *path = fs_path->data;
940

    
941
    return v9fs_set_xattr(ctx, path, name, value, size, flags);
942
}
943

    
944
static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
945
                              const char *name)
946
{
947
    char *path = fs_path->data;
948

    
949
    return v9fs_remove_xattr(ctx, path, name);
950
}
951

    
952
static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
953
                              const char *name, V9fsPath *target)
954
{
955
    if (dir_path) {
956
        v9fs_string_sprintf((V9fsString *)target, "%s/%s",
957
                            dir_path->data, name);
958
    } else {
959
        v9fs_string_sprintf((V9fsString *)target, "%s", name);
960
    }
961
    /* Bump the size for including terminating NULL */
962
    target->size++;
963
    return 0;
964
}
965

    
966
static int local_renameat(FsContext *ctx, V9fsPath *olddir,
967
                          const char *old_name, V9fsPath *newdir,
968
                          const char *new_name)
969
{
970
    int ret;
971
    V9fsString old_full_name, new_full_name;
972

    
973
    v9fs_string_init(&old_full_name);
974
    v9fs_string_init(&new_full_name);
975

    
976
    v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
977
    v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
978

    
979
    ret = local_rename(ctx, old_full_name.data, new_full_name.data);
980
    v9fs_string_free(&old_full_name);
981
    v9fs_string_free(&new_full_name);
982
    return ret;
983
}
984

    
985
static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
986
                          const char *name, int flags)
987
{
988
    int ret;
989
    V9fsString fullname;
990
    char buffer[PATH_MAX];
991

    
992
    v9fs_string_init(&fullname);
993

    
994
    v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
995
    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
996
        if (flags == AT_REMOVEDIR) {
997
            /*
998
             * If directory remove .virtfs_metadata contained in the
999
             * directory
1000
             */
1001
            sprintf(buffer, "%s/%s/%s", ctx->fs_root,
1002
                    fullname.data, VIRTFS_META_DIR);
1003
            ret = remove(buffer);
1004
            if (ret < 0 && errno != ENOENT) {
1005
                /*
1006
                 * We didn't had the .virtfs_metadata file. May be file created
1007
                 * in non-mapped mode ?. Ignore ENOENT.
1008
                 */
1009
                goto err_out;
1010
            }
1011
        }
1012
        /*
1013
         * Now remove the name from parent directory
1014
         * .virtfs_metadata directory.
1015
         */
1016
        ret = remove(local_mapped_attr_path(ctx, fullname.data, buffer));
1017
        if (ret < 0 && errno != ENOENT) {
1018
            /*
1019
             * We didn't had the .virtfs_metadata file. May be file created
1020
             * in non-mapped mode ?. Ignore ENOENT.
1021
             */
1022
            goto err_out;
1023
        }
1024
    }
1025
    /* Remove the name finally */
1026
    ret = remove(rpath(ctx, fullname.data, buffer));
1027
    v9fs_string_free(&fullname);
1028

    
1029
err_out:
1030
    return ret;
1031
}
1032

    
1033
static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
1034
                                mode_t st_mode, uint64_t *st_gen)
1035
{
1036
    int err;
1037
#ifdef FS_IOC_GETVERSION
1038
    V9fsFidOpenState fid_open;
1039

    
1040
    /*
1041
     * Do not try to open special files like device nodes, fifos etc
1042
     * We can get fd for regular files and directories only
1043
     */
1044
    if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
1045
            return 0;
1046
    }
1047
    err = local_open(ctx, path, O_RDONLY, &fid_open);
1048
    if (err < 0) {
1049
        return err;
1050
    }
1051
    err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
1052
    local_close(ctx, &fid_open);
1053
#else
1054
    err = -ENOTTY;
1055
#endif
1056
    return err;
1057
}
1058

    
1059
static int local_init(FsContext *ctx)
1060
{
1061
    int err = 0;
1062
    struct statfs stbuf;
1063

    
1064
    if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
1065
        ctx->xops = passthrough_xattr_ops;
1066
    } else if (ctx->export_flags & V9FS_SM_MAPPED) {
1067
        ctx->xops = mapped_xattr_ops;
1068
    } else if (ctx->export_flags & V9FS_SM_NONE) {
1069
        ctx->xops = none_xattr_ops;
1070
    } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1071
        /*
1072
         * xattr operation for mapped-file and passthrough
1073
         * remain same.
1074
         */
1075
        ctx->xops = passthrough_xattr_ops;
1076
    }
1077
    ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1078
#ifdef FS_IOC_GETVERSION
1079
    /*
1080
     * use ioc_getversion only if the iocl is definied
1081
     */
1082
    err = statfs(ctx->fs_root, &stbuf);
1083
    if (!err) {
1084
        switch (stbuf.f_type) {
1085
        case EXT2_SUPER_MAGIC:
1086
        case BTRFS_SUPER_MAGIC:
1087
        case REISERFS_SUPER_MAGIC:
1088
        case XFS_SUPER_MAGIC:
1089
            ctx->exops.get_st_gen = local_ioc_getversion;
1090
            break;
1091
        }
1092
    }
1093
#endif
1094
    return err;
1095
}
1096

    
1097
static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
1098
{
1099
    const char *sec_model = qemu_opt_get(opts, "security_model");
1100
    const char *path = qemu_opt_get(opts, "path");
1101

    
1102
    if (!sec_model) {
1103
        fprintf(stderr, "security model not specified, "
1104
                "local fs needs security model\nvalid options are:"
1105
                "\tsecurity_model=[passthrough|mapped|none]\n");
1106
        return -1;
1107
    }
1108

    
1109
    if (!strcmp(sec_model, "passthrough")) {
1110
        fse->export_flags |= V9FS_SM_PASSTHROUGH;
1111
    } else if (!strcmp(sec_model, "mapped") ||
1112
               !strcmp(sec_model, "mapped-xattr")) {
1113
        fse->export_flags |= V9FS_SM_MAPPED;
1114
    } else if (!strcmp(sec_model, "none")) {
1115
        fse->export_flags |= V9FS_SM_NONE;
1116
    } else if (!strcmp(sec_model, "mapped-file")) {
1117
        fse->export_flags |= V9FS_SM_MAPPED_FILE;
1118
    } else {
1119
        fprintf(stderr, "Invalid security model %s specified, valid options are"
1120
                "\n\t [passthrough|mapped-xattr|mapped-file|none]\n",
1121
                sec_model);
1122
        return -1;
1123
    }
1124

    
1125
    if (!path) {
1126
        fprintf(stderr, "fsdev: No path specified.\n");
1127
        return -1;
1128
    }
1129
    fse->path = g_strdup(path);
1130

    
1131
    return 0;
1132
}
1133

    
1134
FileOperations local_ops = {
1135
    .parse_opts = local_parse_opts,
1136
    .init  = local_init,
1137
    .lstat = local_lstat,
1138
    .readlink = local_readlink,
1139
    .close = local_close,
1140
    .closedir = local_closedir,
1141
    .open = local_open,
1142
    .opendir = local_opendir,
1143
    .rewinddir = local_rewinddir,
1144
    .telldir = local_telldir,
1145
    .readdir_r = local_readdir_r,
1146
    .seekdir = local_seekdir,
1147
    .preadv = local_preadv,
1148
    .pwritev = local_pwritev,
1149
    .chmod = local_chmod,
1150
    .mknod = local_mknod,
1151
    .mkdir = local_mkdir,
1152
    .fstat = local_fstat,
1153
    .open2 = local_open2,
1154
    .symlink = local_symlink,
1155
    .link = local_link,
1156
    .truncate = local_truncate,
1157
    .rename = local_rename,
1158
    .chown = local_chown,
1159
    .utimensat = local_utimensat,
1160
    .remove = local_remove,
1161
    .fsync = local_fsync,
1162
    .statfs = local_statfs,
1163
    .lgetxattr = local_lgetxattr,
1164
    .llistxattr = local_llistxattr,
1165
    .lsetxattr = local_lsetxattr,
1166
    .lremovexattr = local_lremovexattr,
1167
    .name_to_path = local_name_to_path,
1168
    .renameat  = local_renameat,
1169
    .unlinkat = local_unlinkat,
1170
};